Merge pull request #13583 from rhatdan/ipc

Add support for ipc namespace modes "none, private, sharable"
This commit is contained in:
OpenShift Merge Robot
2022-04-16 12:30:01 -04:00
committed by GitHub
15 changed files with 177 additions and 45 deletions

View File

@ -219,7 +219,7 @@ $ podman container inspect foobar
"DnsSearch": [],
"ExtraHosts": [],
"GroupAdd": [],
"IpcMode": "private",
"IpcMode": "shareable",
"Cgroup": "",
"Cgroups": "default",
"Links": null,

View File

@ -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
- **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*

View File

@ -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
- **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*

View File

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

View File

@ -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"`

View File

@ -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 := ""

View File

@ -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 !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)
}
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)
}
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

View File

@ -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()

View File

@ -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 {

View File

@ -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
@ -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) {

View File

@ -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":

View File

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

View File

@ -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
}

View File

@ -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
}

View File

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