mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Merge pull request #13583 from rhatdan/ipc
Add support for ipc namespace modes "none, private, sharable"
This commit is contained in:
@ -219,7 +219,7 @@ $ podman container inspect foobar
|
||||
"DnsSearch": [],
|
||||
"ExtraHosts": [],
|
||||
"GroupAdd": [],
|
||||
"IpcMode": "private",
|
||||
"IpcMode": "shareable",
|
||||
"Cgroup": "",
|
||||
"Cgroups": "default",
|
||||
"Links": null,
|
||||
|
@ -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*
|
||||
|
||||
|
@ -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*
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"`
|
||||
|
@ -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 := ""
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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":
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
70
test/system/190-run-ipcns.bats
Normal file
70
test/system/190-run-ipcns.bats
Normal 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
|
Reference in New Issue
Block a user