Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set $container_uuid and mount basic /run/host #13217

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require (
github.com/fsnotify/fsnotify v1.5.1
github.com/ghodss/yaml v1.0.0
github.com/godbus/dbus/v5 v5.0.6
github.com/google/go-cmp v0.5.7
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
Expand Down
77 changes: 77 additions & 0 deletions libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,14 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, err
}

if err := c.makeRunHostMount(g); err != nil {
return nil, err
}

if err := c.injectContainerUUID(g); err != nil {
return nil, err
}
Comment on lines +376 to +382
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should only be added for systemd containers

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably agree, does machinectl and other tools do anything in the case where systemd is not running within the container.

Copy link
Author

@jfroy jfroy Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The container interface is something coming out of systemd, for sure, but if I set that aside, it is not bad to have basic container information available in the filesystem, in addition to the environment. I'm happy to make this conditional.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have some container information in /run/.containerenv, but we only leak information their in --privileged mode.

podman run --privileged fedora cat /run/.containerenv
engine="podman-4.0.0-rc4"
name="musing_swartz"
id="fde1c78d6d4cc8a461dc2eb53d6f235946b79e43f812a048b7c79ea5a71df950"
image="registry.fedoraproject.org/fedora:latest"
imageid="e417cd49a84e1749071c516c4f0013ea62113cb5adc98a8504a63a04bfd43479"
rootless=1


// Get host UID and GID based on the container process UID and GID.
hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), uint32(execUser.Uid), uint32(execUser.Gid))
if err != nil {
Expand Down Expand Up @@ -513,6 +521,9 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if dstPath == "/dev/shm" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir {
newMount.Options = append(newMount.Options, "nosuid", "noexec", "nodev")
}
if dstPath == "/run/host" {
newMount.Options = append(newMount.Options, "ro", "nosuid", "noexec", "nodev")
}
if !MountExists(g.Mounts(), dstPath) {
g.AddMount(newMount)
} else {
Expand Down Expand Up @@ -909,6 +920,72 @@ func (c *Container) mountNotifySocket(g generate.Generator) error {
return nil
}

func (c *Container) runHostDir() string {
return filepath.Join(c.state.RunDir, "run-host")
}

// makeRunHostMount creates the run-host bind mount for the container
// https://systemd.io/CONTAINER_INTERFACE/#the-runhost-hierarchy
func (c *Container) makeRunHostMount(g generate.Generator) error {
src := c.runHostDir()
if _, err := os.Stat(src); err == nil || !os.IsNotExist(err) {
return err
}
oldUmask := umask.Set(0)
defer umask.Set(oldUmask)

if err := os.MkdirAll(src, 0755); err != nil {
return err
}
if err := label.Relabel(src, c.config.MountLabel, false); err != nil {
return err
}
if err := os.Chown(src, c.RootUID(), c.RootGID()); err != nil {
return err
}
c.state.BindMounts["/run/host"] = src

src = filepath.Join(src, "container-manager")
if err := os.Remove(src); err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "error removing %s for container %s", src, c.ID())
}
if err := writeStringToPath(src, "podman\n", c.config.MountLabel, c.RootUID(), c.RootGID()); err != nil {
return errors.Wrapf(err, "error writing %s for container %s", src, c.ID())
}
if err := os.Chmod(src, 0444); err != nil {
return err
}

return nil
}

func (c *Container) injectContainerUUID(g generate.Generator) error {
uuid := c.ID()[:32]
addEnv := true
for _, checkEnv := range g.Config.Process.Env {
if strings.SplitN(checkEnv, "=", 2)[0] == "container_uuid" {
addEnv = false
break
}
}
if addEnv {
g.AddProcessEnv("container_uuid", uuid)
}

src := filepath.Join(c.runHostDir(), "container-uuid")
if err := os.Remove(src); err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "error removing %s for container %s", src, c.ID())
}
if err := writeStringToPath(src, uuid+"\n", c.config.MountLabel, c.RootUID(), c.RootGID()); err != nil {
return errors.Wrapf(err, "error writing %s for container %s", src, c.ID())
}
if err := os.Chmod(src, 0444); err != nil {
return err
}

return nil
}

// systemd expects to have /run, /run/lock and /tmp on tmpfs
// It also expects to be able to write to /sys/fs/cgroup/systemd and /var/log/journal
func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) error {
Expand Down
100 changes: 100 additions & 0 deletions libpod/container_internal_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@
package libpod

import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/stringid"
"github.com/google/go-cmp/cmp"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -97,3 +104,96 @@ func TestAppendLocalhost(t *testing.T) {
assert.Equal(t, "127.0.0.1\tlocalhost", c.appendLocalhost("127.0.0.1\tlocalhost"))
}
}

func TestContainerUUIDEnv(t *testing.T) {
ctx := context.Background()
c := makeTestContainer(t)

s, err := c.generateSpec(ctx)
if err != nil {
t.Fatal(err)
}
assert.True(t, hasProcessEnv(t, s, "container_uuid="+c.config.ID[:32]))

c.config.Spec.Process.Env = append(c.config.Spec.Process.Env, "container_uuid=test")
s, err = c.generateSpec(ctx)
if err != nil {
t.Fatal(err)
}
assert.True(t, hasProcessEnv(t, s, "container_uuid=test"))
}

func TestContainerRunHost(t *testing.T) {
ctx := context.Background()
c := makeTestContainer(t)

s, err := c.generateSpec(ctx)
if err != nil {
t.Fatal(err)
}
assert.True(t, hasMount(t, s, spec.Mount{
Destination: "/run/host",
Source: c.runHostDir(),
Type: "bind",
Options: []string{"bind", "rprivate", "ro", "nosuid", "noexec", "nodev"}},
), "run-host mount not found: %+v", s.Mounts)
b, err := os.ReadFile(filepath.Join(c.runHostDir(), "container-manager"))
if err != nil {
t.Fatalf("ReadFile failed: %v", err)
}
assert.Equal(t, string(b), "podman\n")
b, err = os.ReadFile(filepath.Join(c.runHostDir(), "container-uuid"))
if err != nil {
t.Fatalf("ReadFile failed: %v", err)
}
assert.Equal(t, string(b), c.config.ID[:32]+"\n")
}

func makeTestContainer(t *testing.T) Container {
t.Helper()
return Container{
config: &ContainerConfig{
Spec: &spec.Spec{
Process: &spec.Process{},
Root: &spec.Root{},
Linux: &spec.Linux{
Namespaces: []spec.LinuxNamespace{{Type: spec.NetworkNamespace, Path: ""}},
},
},
ID: stringid.GenerateNonCryptoID(),
IDMappings: storage.IDMappingOptions{
UIDMap: []idtools.IDMap{{ContainerID: 0, HostID: os.Geteuid(), Size: 1}},
GIDMap: []idtools.IDMap{{ContainerID: 0, HostID: os.Getegid(), Size: 1}},
},
ContainerNetworkConfig: ContainerNetworkConfig{UseImageHosts: true},
ContainerMiscConfig: ContainerMiscConfig{CgroupManager: config.SystemdCgroupsManager},
},
state: &ContainerState{
BindMounts: map[string]string{"/run/.containerenv": ""},
RunDir: t.TempDir(),
},
runtime: &Runtime{
config: &config.Config{Containers: config.ContainersConfig{}},
},
}
}

func hasProcessEnv(t *testing.T, s *spec.Spec, want string) bool {
t.Helper()
for _, checkEnv := range s.Process.Env {
if checkEnv == want {
return true
}
}
return false
}

func hasMount(t *testing.T, s *spec.Spec, want spec.Mount) bool {
t.Helper()
for _, m := range s.Mounts {
if cmp.Equal(want, m) {
return true
}
}
return false
}
1 change: 1 addition & 0 deletions libpod/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var initInodes = map[string]bool{
"/etc/resolv.conf": true,
"/proc": true,
"/run": true,
"/run/host": true,
"/run/notify": true,
"/run/.containerenv": true,
"/run/secrets": true,
Expand Down
1 change: 1 addition & 0 deletions vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ github.com/golang/protobuf/ptypes/any
github.com/golang/protobuf/ptypes/duration
github.com/golang/protobuf/ptypes/timestamp
# github.com/google/go-cmp v0.5.7
## explicit
github.com/google/go-cmp/cmp
github.com/google/go-cmp/cmp/internal/diff
github.com/google/go-cmp/cmp/internal/flags
Expand Down