Skip to content

Commit

Permalink
Set $container_uuid and mount basic /run/host
Browse files Browse the repository at this point in the history
This patch sets the `container_uuid` environment variable to the
container's fist 32 characters.

This patch also mounts a basic /run/host that contains container-manager
and container-uuid.

See https://systemd.io/CONTAINER_INTERFACE for the details.

Fixed containers#13187

Signed-off-by: Jean-Francois Roy <[email protected]>
  • Loading branch information
Jean-Francois Roy committed Feb 17, 2022
1 parent 71474f6 commit 6519a09
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 0 deletions.
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
}

// 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

0 comments on commit 6519a09

Please sign in to comment.