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": [],
|
"DnsSearch": [],
|
||||||
"ExtraHosts": [],
|
"ExtraHosts": [],
|
||||||
"GroupAdd": [],
|
"GroupAdd": [],
|
||||||
"IpcMode": "private",
|
"IpcMode": "shareable",
|
||||||
"Cgroup": "",
|
"Cgroup": "",
|
||||||
"Cgroups": "default",
|
"Cgroups": "default",
|
||||||
"Links": null,
|
"Links": null,
|
||||||
|
@ -504,10 +504,16 @@ To specify multiple static IPv6 addresses per container, set multiple networks u
|
|||||||
|
|
||||||
#### **--ipc**=*ipc*
|
#### **--ipc**=*ipc*
|
||||||
|
|
||||||
Default is to create a private IPC namespace (POSIX SysV IPC) for the container
|
Set the IPC namespace mode for a container. The default is to create
|
||||||
`container:<name|id>`: reuses another container shared memory, semaphores and message queues
|
a private IPC namespace.
|
||||||
`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.
|
- "": 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*
|
#### **--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
|
Set the IPC namespace mode for a container. The default is to create
|
||||||
a private IPC namespace.
|
a private IPC namespace.
|
||||||
|
|
||||||
|
- "": Use Podman's default, defined in containers.conf.
|
||||||
- **container:**_id_: reuses another container shared memory, semaphores and message queues
|
- **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.
|
- **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.
|
- **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*
|
#### **--label**, **-l**=*key*=*value*
|
||||||
|
|
||||||
|
@ -291,6 +291,13 @@ func (c *Container) Config() *ContainerConfig {
|
|||||||
return returnConfig
|
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
|
// DeviceHostSrc returns the user supplied device to be passed down in the pod
|
||||||
func (c *Container) DeviceHostSrc() []spec.LinuxDevice {
|
func (c *Container) DeviceHostSrc() []spec.LinuxDevice {
|
||||||
return c.config.DeviceHostSrc
|
return c.config.DeviceHostSrc
|
||||||
|
@ -120,6 +120,10 @@ type ContainerRootFSConfig struct {
|
|||||||
// with the size specified in ShmSize and populate this with the path of
|
// with the size specified in ShmSize and populate this with the path of
|
||||||
// said tmpfs.
|
// said tmpfs.
|
||||||
ShmDir string `json:"ShmDir,omitempty"`
|
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
|
// ShmSize is the size of the container's SHM. Only used if ShmDir was
|
||||||
// not set manually at time of creation.
|
// not set manually at time of creation.
|
||||||
ShmSize int64 `json:"shmSize"`
|
ShmSize int64 `json:"shmSize"`
|
||||||
|
@ -703,32 +703,31 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
|
|||||||
}
|
}
|
||||||
hostConfig.CapAdd = capAdd
|
hostConfig.CapAdd = capAdd
|
||||||
hostConfig.CapDrop = capDrop
|
hostConfig.CapDrop = capDrop
|
||||||
|
switch {
|
||||||
// IPC Namespace mode
|
case c.config.IPCNsCtr != "":
|
||||||
ipcMode := ""
|
hostConfig.IpcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
|
||||||
if c.config.IPCNsCtr != "" {
|
case ctrSpec.Linux != nil:
|
||||||
ipcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
|
|
||||||
} else if ctrSpec.Linux != nil {
|
|
||||||
// Locate the spec's IPC namespace.
|
// Locate the spec's IPC namespace.
|
||||||
// If there is none, it's ipc=host.
|
// If there is none, it's ipc=host.
|
||||||
// If there is one and it has a path, it's "ns:".
|
// If there is one and it has a path, it's "ns:".
|
||||||
// If no path, it's default - the empty string.
|
// If no path, it's default - the empty string.
|
||||||
|
|
||||||
for _, ns := range ctrSpec.Linux.Namespaces {
|
for _, ns := range ctrSpec.Linux.Namespaces {
|
||||||
if ns.Type == spec.IPCNamespace {
|
if ns.Type == spec.IPCNamespace {
|
||||||
if ns.Path != "" {
|
if ns.Path != "" {
|
||||||
ipcMode = fmt.Sprintf("ns:%s", ns.Path)
|
hostConfig.IpcMode = fmt.Sprintf("ns:%s", ns.Path)
|
||||||
} else {
|
} 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
|
// Cgroup namespace mode
|
||||||
cgroupMode := ""
|
cgroupMode := ""
|
||||||
|
@ -1507,6 +1507,7 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
|
|||||||
return c.state.Mountpoint, nil
|
return c.state.Mountpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !c.config.NoShm {
|
||||||
mounted, err := mount.Mounted(c.config.ShmDir)
|
mounted, err := mount.Mounted(c.config.ShmDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir)
|
return "", errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir)
|
||||||
@ -1528,6 +1529,7 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We need to mount the container before volumes - to ensure the copyup
|
// We need to mount the container before volumes - to ensure the copyup
|
||||||
// works properly.
|
// works properly.
|
||||||
|
@ -1963,8 +1963,10 @@ func (c *Container) makeBindMounts() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SHM is always added when we mount the container
|
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
|
c.state.BindMounts["/dev/shm"] = c.config.ShmDir
|
||||||
|
}
|
||||||
|
|
||||||
if c.config.Passwd == nil || *c.config.Passwd {
|
if c.config.Passwd == nil || *c.config.Passwd {
|
||||||
newPasswd, newGroup, err := c.generatePasswdAndGroup()
|
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
|
// WithSystemd turns on systemd mode in the container
|
||||||
func WithSystemd() CtrCreateOption {
|
func WithSystemd() CtrCreateOption {
|
||||||
return func(ctr *Container) error {
|
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)
|
return nil, errors.Wrapf(err, "converting containers.conf ShmSize %s to an int", r.config.Containers.ShmSize)
|
||||||
}
|
}
|
||||||
ctr.config.ShmSize = size
|
ctr.config.ShmSize = size
|
||||||
|
ctr.config.NoShm = false
|
||||||
|
ctr.config.NoShmShare = false
|
||||||
ctr.config.StopSignal = 15
|
ctr.config.StopSignal = 15
|
||||||
|
|
||||||
ctr.config.StopTimeout = r.config.Engine.StopTimeout
|
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")
|
ctr.config.ShmDir = filepath.Join(ctr.bundlePath(), "shm")
|
||||||
if err := os.MkdirAll(ctr.config.ShmDir, 0700); err != nil {
|
if err := os.MkdirAll(ctr.config.ShmDir, 0700); err != nil {
|
||||||
if !os.IsExist(err) {
|
if !os.IsExist(err) {
|
||||||
|
@ -428,9 +428,12 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s
|
|||||||
case "cgroup":
|
case "cgroup":
|
||||||
specg.CgroupNS = specgen.Namespace{NSMode: specgen.Default} //default
|
specg.CgroupNS = specgen.Namespace{NSMode: specgen.Default} //default
|
||||||
case "ipc":
|
case "ipc":
|
||||||
if conf.ShmDir == "/dev/shm" {
|
switch conf.ShmDir {
|
||||||
|
case "/dev/shm":
|
||||||
specg.IpcNS = specgen.Namespace{NSMode: specgen.Host}
|
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
|
specg.IpcNS = specgen.Namespace{NSMode: specgen.Default} //default
|
||||||
}
|
}
|
||||||
case "uts":
|
case "uts":
|
||||||
|
@ -134,9 +134,18 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with")
|
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.WithIPCNSFrom(ipcCtr))
|
||||||
|
if !ipcCtr.ConfigNoCopy().NoShm {
|
||||||
toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir()))
|
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
|
// UTS
|
||||||
switch s.UtsNS.NSMode {
|
switch s.UtsNS.NSMode {
|
||||||
|
@ -222,7 +222,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
|
|||||||
for sysctlKey, sysctlVal := range defaultSysctls {
|
for sysctlKey, sysctlVal := range defaultSysctls {
|
||||||
// Ignore mqueue sysctls if --ipc=host
|
// Ignore mqueue sysctls if --ipc=host
|
||||||
if noUseIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ func setNamespaces(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.IPC != "" {
|
if c.IPC != "" {
|
||||||
s.IpcNS, err = specgen.ParseNamespace(c.IPC)
|
s.IpcNS, err = specgen.ParseIPCNamespace(c.IPC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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