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

Add support for ipc namespace modes "none, private, sharable" #13583

Merged
merged 1 commit into from
Apr 16, 2022
Merged
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
2 changes: 1 addition & 1 deletion docs/source/markdown/podman-container-inspect.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ $ podman container inspect foobar
"DnsSearch": [],
"ExtraHosts": [],
"GroupAdd": [],
"IpcMode": "private",
"IpcMode": "shareable",
"Cgroup": "",
"Cgroups": "default",
"Links": null,
Expand Down
14 changes: 10 additions & 4 deletions docs/source/markdown/podman-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,10 +504,16 @@ To specify multiple static IPv6 addresses per container, set multiple networks u

#### **--ipc**=*ipc*

Default is to create a private IPC namespace (POSIX SysV IPC) for the container
`container:<name|id>`: reuses another container shared memory, semaphores and message queues
`host`: use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.
`ns:<path>` path to an IPC namespace to join.
Set the IPC namespace mode for a container. The default is to create
a private IPC namespace.

- "": Use Podman's default, defined in containers.conf.
- **container:**_id_: reuses another container's shared memory, semaphores, and message queues
Copy link
Member

Choose a reason for hiding this comment

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

nit

Suggested change
- **container:**_id_: reuses another container's shared memory, semaphores, and message queues
- **container:**_id_: reuses another container's shared memory, semaphores, and message queues.

- **host**: use the host's shared memory, semaphores, and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.
- **none**: private IPC namespace, with /dev/shm not mounted.
- **ns:**_path_: path to an IPC namespace to join.
- **private**: private IPC namespace.
= **shareable**: private IPC namespace with a possibility to share it with other containers.

#### **--label**, **-l**=*label*

Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -528,9 +528,13 @@ To specify multiple static IPv6 addresses per container, set multiple networks u
Set the IPC namespace mode for a container. The default is to create
a private IPC namespace.

- "": Use Podman's default, defined in containers.conf.
- **container:**_id_: reuses another container shared memory, semaphores and message queues
Copy link
Member

Choose a reason for hiding this comment

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

same period nit

- **host**: use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.
- **none**: private IPC namespace, with /dev/shm not mounted.
- **ns:**_path_: path to an IPC namespace to join.
- **private**: private IPC namespace.
= **shareable**: private IPC namespace with a possibility to share it with other containers.

#### **--label**, **-l**=*key*=*value*

Expand Down
7 changes: 7 additions & 0 deletions libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,13 @@ func (c *Container) Config() *ContainerConfig {
return returnConfig
}

// ConfigNoCopy returns the configuration used by the container.
// Note that the returned value is not a copy and must hence
// only be used in a reading fashion.
func (c *Container) ConfigNoCopy() *ContainerConfig {
return c.config
}

// DeviceHostSrc returns the user supplied device to be passed down in the pod
func (c *Container) DeviceHostSrc() []spec.LinuxDevice {
return c.config.DeviceHostSrc
Expand Down
4 changes: 4 additions & 0 deletions libpod/container_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ type ContainerRootFSConfig struct {
// with the size specified in ShmSize and populate this with the path of
// said tmpfs.
ShmDir string `json:"ShmDir,omitempty"`
// NoShmShare indicates whether /dev/shm can be shared with other containers
NoShmShare bool `json:"NOShmShare,omitempty"`
// NoShm indicates whether a tmpfs should be created and mounted on /dev/shm
NoShm bool `json:"NoShm,omitempty"`
// ShmSize is the size of the container's SHM. Only used if ShmDir was
// not set manually at time of creation.
ShmSize int64 `json:"shmSize"`
Expand Down
27 changes: 13 additions & 14 deletions libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -703,32 +703,31 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
}
hostConfig.CapAdd = capAdd
hostConfig.CapDrop = capDrop

// IPC Namespace mode
ipcMode := ""
if c.config.IPCNsCtr != "" {
ipcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
} else if ctrSpec.Linux != nil {
switch {
case c.config.IPCNsCtr != "":
hostConfig.IpcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
case ctrSpec.Linux != nil:
// Locate the spec's IPC namespace.
// If there is none, it's ipc=host.
// If there is one and it has a path, it's "ns:".
// If no path, it's default - the empty string.

for _, ns := range ctrSpec.Linux.Namespaces {
if ns.Type == spec.IPCNamespace {
if ns.Path != "" {
ipcMode = fmt.Sprintf("ns:%s", ns.Path)
hostConfig.IpcMode = fmt.Sprintf("ns:%s", ns.Path)
} else {
ipcMode = "private"
break
}
break
}
}
if ipcMode == "" {
ipcMode = "host"
}
case c.config.NoShm:
hostConfig.IpcMode = "none"
case c.config.NoShmShare:
hostConfig.IpcMode = "private"
}
if hostConfig.IpcMode == "" {
hostConfig.IpcMode = "shareable"
}
hostConfig.IpcMode = ipcMode

// Cgroup namespace mode
cgroupMode := ""
Expand Down
38 changes: 20 additions & 18 deletions libpod/container_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -1507,26 +1507,28 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
return c.state.Mountpoint, nil
}

mounted, err := mount.Mounted(c.config.ShmDir)
if err != nil {
return "", errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir)
}

if !mounted && !MountExists(c.config.Spec.Mounts, "/dev/shm") {
shmOptions := fmt.Sprintf("mode=1777,size=%d", c.config.ShmSize)
if err := c.mountSHM(shmOptions); err != nil {
return "", err
}
if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil {
return "", errors.Wrapf(err, "failed to chown %s", c.config.ShmDir)
if !c.config.NoShm {
mounted, err := mount.Mounted(c.config.ShmDir)
if err != nil {
return "", errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir)
}
defer func() {
if deferredErr != nil {
if err := c.unmountSHM(c.config.ShmDir); err != nil {
logrus.Errorf("Unmounting SHM for container %s after mount error: %v", c.ID(), err)
}

if !mounted && !MountExists(c.config.Spec.Mounts, "/dev/shm") {
shmOptions := fmt.Sprintf("mode=1777,size=%d", c.config.ShmSize)
if err := c.mountSHM(shmOptions); err != nil {
return "", err
}
}()
if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil {
return "", errors.Wrapf(err, "failed to chown %s", c.config.ShmDir)
}
defer func() {
if deferredErr != nil {
if err := c.unmountSHM(c.config.ShmDir); err != nil {
logrus.Errorf("Unmounting SHM for container %s after mount error: %v", c.ID(), err)
}
}
}()
}
}

// We need to mount the container before volumes - to ensure the copyup
Expand Down
6 changes: 4 additions & 2 deletions libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1963,8 +1963,10 @@ func (c *Container) makeBindMounts() error {
}
}

// SHM is always added when we mount the container
c.state.BindMounts["/dev/shm"] = c.config.ShmDir
if c.config.ShmDir != "" {
// If ShmDir has a value SHM is always added when we mount the container
c.state.BindMounts["/dev/shm"] = c.config.ShmDir
}

if c.config.Passwd == nil || *c.config.Passwd {
newPasswd, newGroup, err := c.generatePasswdAndGroup()
Expand Down
24 changes: 24 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,30 @@ func WithShmDir(dir string) CtrCreateOption {
}
}

// WithNOShmMount tells libpod whether to mount /dev/shm
func WithNoShm(mount bool) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}

ctr.config.NoShm = mount
return nil
}
}

// WithNoShmShare tells libpod whether to share containers /dev/shm with other containers
func WithNoShmShare(share bool) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}

ctr.config.NoShmShare = share
return nil
}
}

// WithSystemd turns on systemd mode in the container
func WithSystemd() CtrCreateOption {
return func(ctr *Container) error {
Expand Down
4 changes: 3 additions & 1 deletion libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf
return nil, errors.Wrapf(err, "converting containers.conf ShmSize %s to an int", r.config.Containers.ShmSize)
}
ctr.config.ShmSize = size
ctr.config.NoShm = false
ctr.config.NoShmShare = false
ctr.config.StopSignal = 15

ctr.config.StopTimeout = r.config.Engine.StopTimeout
Expand Down Expand Up @@ -514,7 +516,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
}
}

if !MountExists(ctr.config.Spec.Mounts, "/dev/shm") && ctr.config.ShmDir == "" {
if !MountExists(ctr.config.Spec.Mounts, "/dev/shm") && ctr.config.ShmDir == "" && !ctr.config.NoShm {
ctr.config.ShmDir = filepath.Join(ctr.bundlePath(), "shm")
if err := os.MkdirAll(ctr.config.ShmDir, 0700); err != nil {
if !os.IsExist(err) {
Expand Down
7 changes: 5 additions & 2 deletions pkg/specgen/generate/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,12 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s
case "cgroup":
specg.CgroupNS = specgen.Namespace{NSMode: specgen.Default} //default
case "ipc":
if conf.ShmDir == "/dev/shm" {
switch conf.ShmDir {
case "/dev/shm":
specg.IpcNS = specgen.Namespace{NSMode: specgen.Host}
} else {
case "":
specg.IpcNS = specgen.Namespace{NSMode: specgen.None}
default:
specg.IpcNS = specgen.Namespace{NSMode: specgen.Default} //default
}
case "uts":
Expand Down
11 changes: 10 additions & 1 deletion pkg/specgen/generate/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,17 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
if err != nil {
return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with")
}
if ipcCtr.ConfigNoCopy().NoShmShare {
return nil, errors.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID())
}
toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr))
toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir()))
if !ipcCtr.ConfigNoCopy().NoShm {
toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir()))
}
case specgen.None:
toReturn = append(toReturn, libpod.WithNoShm(true))
case specgen.Private:
toReturn = append(toReturn, libpod.WithNoShmShare(true))
}

// UTS
Expand Down
2 changes: 1 addition & 1 deletion pkg/specgen/generate/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
for sysctlKey, sysctlVal := range defaultSysctls {
// Ignore mqueue sysctls if --ipc=host
if noUseIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") {
logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to host", sysctlKey, sysctlVal)
logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to %q", sysctlKey, sysctlVal, s.IpcNS.NSMode)

continue
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/specgenutil/specgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func setNamespaces(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions)
}
}
if c.IPC != "" {
s.IpcNS, err = specgen.ParseNamespace(c.IPC)
s.IpcNS, err = specgen.ParseIPCNamespace(c.IPC)
if err != nil {
return err
}
Expand Down
70 changes: 70 additions & 0 deletions test/system/190-run-ipcns.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env bats -*- bats -*-
# shellcheck disable=SC2096
#
# Tests for podman build
#

load helpers

@test "podman --ipc=host" {
run readlink /proc/self/ns/ipc
hostipc=$output
run_podman run --rm --ipc=host $IMAGE readlink /proc/self/ns/ipc
is "$output" "$hostipc" "HostIPC and container IPC should be same"
}

@test "podman --ipc=none" {
run readlink /proc/self/ns/ipc
hostipc=$output
run_podman run --rm --ipc=none $IMAGE readlink /proc/self/ns/ipc
if [[ $output == "$hostipc" ]]; then
die "hostipc and containeripc should be different"
fi
run_podman 1 run --rm --ipc=none $IMAGE ls /dev/shm
is "$output" "ls: /dev/shm: No such file or directory" "Should fail with missing /dev/shm"
}

@test "podman --ipc=private" {
run readlink /proc/self/ns/ipc
hostipc=$output
run_podman run -d --ipc=private --name test $IMAGE sleep 100
if [[ $output == "$hostipc" ]]; then
die "hostipc and containeripc should be different"
fi
run_podman 125 run --ipc=container:test --rm $IMAGE readlink /proc/self/ns/ipc
is "$output" ".*is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)" "Containers should not share private ipc namespace"
run_podman stop -t 0 test
run_podman rm test
}

@test "podman --ipc=shareable" {
run readlink /proc/self/ns/ipc
hostipc=$output
run_podman run -d --ipc=shareable --name test $IMAGE sleep 100
if [[ $output == "$hostipc" ]]; then
die "hostipc and containeripc should be different"
fi
run_podman run --ipc=container:test --rm $IMAGE readlink /proc/self/ns/ipc
if [[ $output == "$hostipc" ]]; then
die "hostipc and containeripc should be different"
fi
run_podman stop -t 0 test
run_podman rm test
}

@test "podman --ipc=container@test" {
run readlink /proc/self/ns/ipc
hostipc=$output
run_podman run -d --name test $IMAGE sleep 100
run_podman exec test readlink /proc/self/ns/ipc
if [[ $output == "$hostipc" ]]; then
die "hostipc and containeripc should be different"
fi
testipc=$output
run_podman run --ipc=container:test --rm $IMAGE readlink /proc/self/ns/ipc
is "$output" "$testipc" "Containers should share ipc namespace"
run_podman stop -t 0 test
run_podman rm test
}

# vim: filetype=sh