2018-07-04 00:14:23 -04:00
|
|
|
package container // import "github.com/docker/docker/integration/container"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"context"
|
2018-09-13 17:51:03 -04:00
|
|
|
"io/ioutil"
|
2018-07-04 00:14:23 -04:00
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
containertypes "github.com/docker/docker/api/types/container"
|
daemon: use 'private' ipc mode by default
This changes the default ipc mode of daemon/engine to be private,
meaning the containers will not have their /dev/shm bind-mounted
from the host by default. The benefits of doing this are:
1. No leaked mounts. Eliminate a possibility to leak mounts into
other namespaces (and therefore unfortunate errors like "Unable to
remove filesystem for <ID>: remove /var/lib/docker/containers/<ID>/shm:
device or resource busy").
2. Working checkpoint/restore. Make `docker checkpoint`
not lose the contents of `/dev/shm`, but save it to
the dump, and be restored back upon `docker start --checkpoint`
(currently it is lost -- while CRIU handles tmpfs mounts,
the "shareable" mount is seen as external to container,
and thus rightfully ignored).
3. Better security. Currently any container is opened to share
its /dev/shm with any other container.
Obviously, this change will break the following usage scenario:
$ docker run -d --name donor busybox top
$ docker run --rm -it --ipc container:donor busybox sh
Error response from daemon: linux spec namespaces: can't join IPC
of container <ID>: non-shareable IPC (hint: use IpcMode:shareable
for the donor container)
The soution, as hinted by the (amended) error message, is to
explicitly enable donor sharing by using --ipc shareable:
$ docker run -d --name donor --ipc shareable busybox top
Compatibility notes:
1. This only applies to containers created _after_ this change.
Existing containers are not affected and will work fine
as their ipc mode is stored in HostConfig.
2. Old backward compatible behavior ("shareable" containers
by default) can be enabled by either using
`--default-ipc-mode shareable` daemon command line option,
or by adding a `"default-ipc-mode": "shareable"`
line in `/etc/docker/daemon.json` configuration file.
3. If an older client (API < 1.40) is used, a "shareable" container
is created. A test to check that is added.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2017-11-27 19:10:44 -05:00
|
|
|
"github.com/docker/docker/api/types/versions"
|
|
|
|
"github.com/docker/docker/client"
|
2018-07-04 00:14:23 -04:00
|
|
|
"github.com/docker/docker/integration/internal/container"
|
2019-08-29 16:52:40 -04:00
|
|
|
"github.com/docker/docker/testutil/daemon"
|
|
|
|
"github.com/docker/docker/testutil/request"
|
2020-02-07 08:39:24 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
is "gotest.tools/v3/assert/cmp"
|
|
|
|
"gotest.tools/v3/fs"
|
|
|
|
"gotest.tools/v3/skip"
|
2018-07-04 00:14:23 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// testIpcCheckDevExists checks whether a given mount (identified by its
|
|
|
|
// major:minor pair from /proc/self/mountinfo) exists on the host system.
|
|
|
|
//
|
|
|
|
// The format of /proc/self/mountinfo is like:
|
|
|
|
//
|
|
|
|
// 29 23 0:24 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
|
|
|
|
// ^^^^\
|
|
|
|
// - this is the minor:major we look for
|
|
|
|
func testIpcCheckDevExists(mm string) (bool, error) {
|
|
|
|
f, err := os.Open("/proc/self/mountinfo")
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
s := bufio.NewScanner(f)
|
|
|
|
for s.Scan() {
|
|
|
|
fields := strings.Fields(s.Text())
|
|
|
|
if len(fields) < 7 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if fields[2] == mm {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, s.Err()
|
|
|
|
}
|
|
|
|
|
|
|
|
// testIpcNonePrivateShareable is a helper function to test "none",
|
|
|
|
// "private" and "shareable" modes.
|
|
|
|
func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool, mustBeShared bool) {
|
|
|
|
defer setupTest(t)()
|
|
|
|
|
|
|
|
cfg := containertypes.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
Cmd: []string{"top"},
|
|
|
|
}
|
|
|
|
hostCfg := containertypes.HostConfig{
|
|
|
|
IpcMode: containertypes.IpcMode(mode),
|
|
|
|
}
|
2019-01-02 08:16:25 -05:00
|
|
|
client := testEnv.APIClient()
|
2018-07-04 00:14:23 -04:00
|
|
|
ctx := context.Background()
|
|
|
|
|
2020-03-19 16:54:48 -04:00
|
|
|
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
|
2018-07-04 00:14:23 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(len(resp.Warnings), 0))
|
|
|
|
|
|
|
|
err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
|
|
|
|
cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
|
|
|
|
result, err := container.Exec(ctx, client, resp.ID, []string{"sh", "-c", cmd})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
mm := result.Combined()
|
|
|
|
if !mustBeMounted {
|
|
|
|
assert.Check(t, is.Equal(mm, ""))
|
|
|
|
// no more checks to perform
|
|
|
|
return
|
|
|
|
}
|
|
|
|
assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm)))
|
|
|
|
|
|
|
|
shared, err := testIpcCheckDevExists(mm)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
t.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared)
|
|
|
|
assert.Check(t, is.Equal(shared, mustBeShared))
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestIpcModeNone checks the container "none" IPC mode
|
|
|
|
// (--ipc none) works as expected. It makes sure there is no
|
|
|
|
// /dev/shm mount inside the container.
|
|
|
|
func TestIpcModeNone(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-07-04 00:14:23 -04:00
|
|
|
|
|
|
|
testIpcNonePrivateShareable(t, "none", false, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestAPIIpcModePrivate checks the container private IPC mode
|
|
|
|
// (--ipc private) works as expected. It gets the minor:major pair
|
|
|
|
// of /dev/shm mount from the container, and makes sure there is no
|
|
|
|
// such pair on the host.
|
|
|
|
func TestIpcModePrivate(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-07-04 00:14:23 -04:00
|
|
|
|
|
|
|
testIpcNonePrivateShareable(t, "private", true, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestAPIIpcModeShareable checks the container shareable IPC mode
|
|
|
|
// (--ipc shareable) works as expected. It gets the minor:major pair
|
|
|
|
// of /dev/shm mount from the container, and makes sure such pair
|
|
|
|
// also exists on the host.
|
|
|
|
func TestIpcModeShareable(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2020-02-18 04:43:56 -05:00
|
|
|
skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
|
2018-07-04 00:14:23 -04:00
|
|
|
|
|
|
|
testIpcNonePrivateShareable(t, "shareable", true, true)
|
|
|
|
}
|
2018-07-27 03:18:12 -04:00
|
|
|
|
|
|
|
// testIpcContainer is a helper function to test --ipc container:NNN mode in various scenarios
|
|
|
|
func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
defer setupTest(t)()
|
|
|
|
|
|
|
|
cfg := containertypes.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
Cmd: []string{"top"},
|
|
|
|
}
|
|
|
|
hostCfg := containertypes.HostConfig{
|
|
|
|
IpcMode: containertypes.IpcMode(donorMode),
|
|
|
|
}
|
|
|
|
ctx := context.Background()
|
2019-01-02 08:16:25 -05:00
|
|
|
client := testEnv.APIClient()
|
2018-07-27 03:18:12 -04:00
|
|
|
|
|
|
|
// create and start the "donor" container
|
2020-03-19 16:54:48 -04:00
|
|
|
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
|
2018-07-27 03:18:12 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(len(resp.Warnings), 0))
|
|
|
|
name1 := resp.ID
|
|
|
|
|
|
|
|
err = client.ContainerStart(ctx, name1, types.ContainerStartOptions{})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
// create and start the second container
|
|
|
|
hostCfg.IpcMode = containertypes.IpcMode("container:" + name1)
|
2020-03-19 16:54:48 -04:00
|
|
|
resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
|
2018-07-27 03:18:12 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(len(resp.Warnings), 0))
|
|
|
|
name2 := resp.ID
|
|
|
|
|
|
|
|
err = client.ContainerStart(ctx, name2, types.ContainerStartOptions{})
|
|
|
|
if !mustWork {
|
|
|
|
// start should fail with a specific error
|
|
|
|
assert.Check(t, is.ErrorContains(err, "non-shareable IPC"))
|
|
|
|
// no more checks to perform here
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// start should succeed
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
// check that IPC is shared
|
|
|
|
// 1. create a file in the first container
|
|
|
|
_, err = container.Exec(ctx, client, name1, []string{"sh", "-c", "printf covfefe > /dev/shm/bar"})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
// 2. check it's the same file in the second one
|
|
|
|
result, err := container.Exec(ctx, client, name2, []string{"cat", "/dev/shm/bar"})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
out := result.Combined()
|
|
|
|
assert.Check(t, is.Equal(true, regexp.MustCompile("^covfefe$").MatchString(out)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestAPIIpcModeShareableAndPrivate checks that
|
|
|
|
// 1) a container created with --ipc container:ID can use IPC of another shareable container.
|
|
|
|
// 2) a container created with --ipc container:ID can NOT use IPC of another private container.
|
|
|
|
func TestAPIIpcModeShareableAndContainer(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-07-27 03:18:12 -04:00
|
|
|
|
|
|
|
testIpcContainer(t, "shareable", true)
|
|
|
|
|
|
|
|
testIpcContainer(t, "private", false)
|
|
|
|
}
|
2018-09-13 17:51:03 -04:00
|
|
|
|
|
|
|
/* TestAPIIpcModeHost checks that a container created with --ipc host
|
|
|
|
* can use IPC of the host system.
|
|
|
|
*/
|
|
|
|
func TestAPIIpcModeHost(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
|
|
|
skip.If(t, testEnv.IsUserNamespace)
|
2020-02-18 04:43:56 -05:00
|
|
|
skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
|
2018-09-13 17:51:03 -04:00
|
|
|
|
|
|
|
cfg := containertypes.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
Cmd: []string{"top"},
|
|
|
|
}
|
|
|
|
hostCfg := containertypes.HostConfig{
|
|
|
|
IpcMode: containertypes.IpcMode("host"),
|
|
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
client := testEnv.APIClient()
|
2020-03-19 16:54:48 -04:00
|
|
|
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
|
2018-09-13 17:51:03 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(len(resp.Warnings), 0))
|
|
|
|
name := resp.ID
|
|
|
|
|
|
|
|
err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
// check that IPC is shared
|
|
|
|
// 1. create a file inside container
|
|
|
|
_, err = container.Exec(ctx, client, name, []string{"sh", "-c", "printf covfefe > /dev/shm/." + name})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
// 2. check it's the same on the host
|
|
|
|
bytes, err := ioutil.ReadFile("/dev/shm/." + name)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal("covfefe", string(bytes)))
|
|
|
|
// 3. clean up
|
|
|
|
_, err = container.Exec(ctx, client, name, []string{"rm", "-f", "/dev/shm/." + name})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// testDaemonIpcPrivateShareable is a helper function to test "private" and "shareable" daemon default ipc modes.
|
|
|
|
func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...string) {
|
|
|
|
defer setupTest(t)()
|
|
|
|
|
|
|
|
d := daemon.New(t)
|
|
|
|
d.StartWithBusybox(t, arg...)
|
|
|
|
defer d.Stop(t)
|
|
|
|
|
2018-12-22 09:53:02 -05:00
|
|
|
c := d.NewClientT(t)
|
2018-09-13 17:51:03 -04:00
|
|
|
|
|
|
|
cfg := containertypes.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
Cmd: []string{"top"},
|
|
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
|
2020-03-19 16:54:48 -04:00
|
|
|
resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, nil, "")
|
2018-09-13 17:51:03 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(len(resp.Warnings), 0))
|
|
|
|
|
2018-12-22 09:53:02 -05:00
|
|
|
err = c.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
|
2018-09-13 17:51:03 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
|
|
|
|
cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
|
2018-12-22 09:53:02 -05:00
|
|
|
result, err := container.Exec(ctx, c, resp.ID, []string{"sh", "-c", cmd})
|
2018-09-13 17:51:03 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
mm := result.Combined()
|
|
|
|
assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm)))
|
|
|
|
|
|
|
|
shared, err := testIpcCheckDevExists(mm)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
t.Logf("[testDaemonIpcPrivateShareable] ipcdev: %v, shared: %v, mustBeShared: %v\n", mm, shared, mustBeShared)
|
|
|
|
assert.Check(t, is.Equal(shared, mustBeShared))
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended.
|
|
|
|
func TestDaemonIpcModeShareable(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2020-02-18 04:43:56 -05:00
|
|
|
skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
|
2018-09-13 17:51:03 -04:00
|
|
|
|
|
|
|
testDaemonIpcPrivateShareable(t, true, "--default-ipc-mode", "shareable")
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestDaemonIpcModePrivate checks that --default-ipc-mode private works as intended.
|
|
|
|
func TestDaemonIpcModePrivate(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-09-13 17:51:03 -04:00
|
|
|
|
|
|
|
testDaemonIpcPrivateShareable(t, false, "--default-ipc-mode", "private")
|
|
|
|
}
|
|
|
|
|
|
|
|
// used to check if an IpcMode given in config works as intended
|
|
|
|
func testDaemonIpcFromConfig(t *testing.T, mode string, mustExist bool) {
|
2020-03-13 09:37:09 -04:00
|
|
|
skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
|
2018-09-13 17:51:03 -04:00
|
|
|
config := `{"default-ipc-mode": "` + mode + `"}`
|
|
|
|
file := fs.NewFile(t, "test-daemon-ipc-config", fs.WithContent(config))
|
|
|
|
defer file.Remove()
|
|
|
|
|
|
|
|
testDaemonIpcPrivateShareable(t, mustExist, "--config-file", file.Path())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestDaemonIpcModePrivateFromConfig checks that "default-ipc-mode: private" config works as intended.
|
|
|
|
func TestDaemonIpcModePrivateFromConfig(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-09-13 17:51:03 -04:00
|
|
|
|
|
|
|
testDaemonIpcFromConfig(t, "private", false)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestDaemonIpcModeShareableFromConfig checks that "default-ipc-mode: shareable" config works as intended.
|
|
|
|
func TestDaemonIpcModeShareableFromConfig(t *testing.T) {
|
2019-01-03 06:32:05 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-09-13 17:51:03 -04:00
|
|
|
|
|
|
|
testDaemonIpcFromConfig(t, "shareable", true)
|
|
|
|
}
|
daemon: use 'private' ipc mode by default
This changes the default ipc mode of daemon/engine to be private,
meaning the containers will not have their /dev/shm bind-mounted
from the host by default. The benefits of doing this are:
1. No leaked mounts. Eliminate a possibility to leak mounts into
other namespaces (and therefore unfortunate errors like "Unable to
remove filesystem for <ID>: remove /var/lib/docker/containers/<ID>/shm:
device or resource busy").
2. Working checkpoint/restore. Make `docker checkpoint`
not lose the contents of `/dev/shm`, but save it to
the dump, and be restored back upon `docker start --checkpoint`
(currently it is lost -- while CRIU handles tmpfs mounts,
the "shareable" mount is seen as external to container,
and thus rightfully ignored).
3. Better security. Currently any container is opened to share
its /dev/shm with any other container.
Obviously, this change will break the following usage scenario:
$ docker run -d --name donor busybox top
$ docker run --rm -it --ipc container:donor busybox sh
Error response from daemon: linux spec namespaces: can't join IPC
of container <ID>: non-shareable IPC (hint: use IpcMode:shareable
for the donor container)
The soution, as hinted by the (amended) error message, is to
explicitly enable donor sharing by using --ipc shareable:
$ docker run -d --name donor --ipc shareable busybox top
Compatibility notes:
1. This only applies to containers created _after_ this change.
Existing containers are not affected and will work fine
as their ipc mode is stored in HostConfig.
2. Old backward compatible behavior ("shareable" containers
by default) can be enabled by either using
`--default-ipc-mode shareable` daemon command line option,
or by adding a `"default-ipc-mode": "shareable"`
line in `/etc/docker/daemon.json` configuration file.
3. If an older client (API < 1.40) is used, a "shareable" container
is created. A test to check that is added.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2017-11-27 19:10:44 -05:00
|
|
|
|
|
|
|
// TestIpcModeOlderClient checks that older client gets shareable IPC mode
|
|
|
|
// by default, even when the daemon default is private.
|
|
|
|
func TestIpcModeOlderClient(t *testing.T) {
|
|
|
|
skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "requires a daemon with DefaultIpcMode: private")
|
2019-04-25 21:48:23 -04:00
|
|
|
c := testEnv.APIClient()
|
|
|
|
skip.If(t, versions.LessThan(c.ClientVersion(), "1.40"), "requires client API >= 1.40")
|
|
|
|
|
daemon: use 'private' ipc mode by default
This changes the default ipc mode of daemon/engine to be private,
meaning the containers will not have their /dev/shm bind-mounted
from the host by default. The benefits of doing this are:
1. No leaked mounts. Eliminate a possibility to leak mounts into
other namespaces (and therefore unfortunate errors like "Unable to
remove filesystem for <ID>: remove /var/lib/docker/containers/<ID>/shm:
device or resource busy").
2. Working checkpoint/restore. Make `docker checkpoint`
not lose the contents of `/dev/shm`, but save it to
the dump, and be restored back upon `docker start --checkpoint`
(currently it is lost -- while CRIU handles tmpfs mounts,
the "shareable" mount is seen as external to container,
and thus rightfully ignored).
3. Better security. Currently any container is opened to share
its /dev/shm with any other container.
Obviously, this change will break the following usage scenario:
$ docker run -d --name donor busybox top
$ docker run --rm -it --ipc container:donor busybox sh
Error response from daemon: linux spec namespaces: can't join IPC
of container <ID>: non-shareable IPC (hint: use IpcMode:shareable
for the donor container)
The soution, as hinted by the (amended) error message, is to
explicitly enable donor sharing by using --ipc shareable:
$ docker run -d --name donor --ipc shareable busybox top
Compatibility notes:
1. This only applies to containers created _after_ this change.
Existing containers are not affected and will work fine
as their ipc mode is stored in HostConfig.
2. Old backward compatible behavior ("shareable" containers
by default) can be enabled by either using
`--default-ipc-mode shareable` daemon command line option,
or by adding a `"default-ipc-mode": "shareable"`
line in `/etc/docker/daemon.json` configuration file.
3. If an older client (API < 1.40) is used, a "shareable" container
is created. A test to check that is added.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2017-11-27 19:10:44 -05:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
// pre-check: default ipc mode in daemon is private
|
2019-06-06 07:00:37 -04:00
|
|
|
cID := container.Create(ctx, t, c, container.WithAutoRemove)
|
daemon: use 'private' ipc mode by default
This changes the default ipc mode of daemon/engine to be private,
meaning the containers will not have their /dev/shm bind-mounted
from the host by default. The benefits of doing this are:
1. No leaked mounts. Eliminate a possibility to leak mounts into
other namespaces (and therefore unfortunate errors like "Unable to
remove filesystem for <ID>: remove /var/lib/docker/containers/<ID>/shm:
device or resource busy").
2. Working checkpoint/restore. Make `docker checkpoint`
not lose the contents of `/dev/shm`, but save it to
the dump, and be restored back upon `docker start --checkpoint`
(currently it is lost -- while CRIU handles tmpfs mounts,
the "shareable" mount is seen as external to container,
and thus rightfully ignored).
3. Better security. Currently any container is opened to share
its /dev/shm with any other container.
Obviously, this change will break the following usage scenario:
$ docker run -d --name donor busybox top
$ docker run --rm -it --ipc container:donor busybox sh
Error response from daemon: linux spec namespaces: can't join IPC
of container <ID>: non-shareable IPC (hint: use IpcMode:shareable
for the donor container)
The soution, as hinted by the (amended) error message, is to
explicitly enable donor sharing by using --ipc shareable:
$ docker run -d --name donor --ipc shareable busybox top
Compatibility notes:
1. This only applies to containers created _after_ this change.
Existing containers are not affected and will work fine
as their ipc mode is stored in HostConfig.
2. Old backward compatible behavior ("shareable" containers
by default) can be enabled by either using
`--default-ipc-mode shareable` daemon command line option,
or by adding a `"default-ipc-mode": "shareable"`
line in `/etc/docker/daemon.json` configuration file.
3. If an older client (API < 1.40) is used, a "shareable" container
is created. A test to check that is added.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2017-11-27 19:10:44 -05:00
|
|
|
|
|
|
|
inspect, err := c.ContainerInspect(ctx, cID)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(string(inspect.HostConfig.IpcMode), "private"))
|
|
|
|
|
|
|
|
// main check: using older client creates "shareable" container
|
|
|
|
c = request.NewAPIClient(t, client.WithVersion("1.39"))
|
2019-06-06 07:00:37 -04:00
|
|
|
cID = container.Create(ctx, t, c, container.WithAutoRemove)
|
daemon: use 'private' ipc mode by default
This changes the default ipc mode of daemon/engine to be private,
meaning the containers will not have their /dev/shm bind-mounted
from the host by default. The benefits of doing this are:
1. No leaked mounts. Eliminate a possibility to leak mounts into
other namespaces (and therefore unfortunate errors like "Unable to
remove filesystem for <ID>: remove /var/lib/docker/containers/<ID>/shm:
device or resource busy").
2. Working checkpoint/restore. Make `docker checkpoint`
not lose the contents of `/dev/shm`, but save it to
the dump, and be restored back upon `docker start --checkpoint`
(currently it is lost -- while CRIU handles tmpfs mounts,
the "shareable" mount is seen as external to container,
and thus rightfully ignored).
3. Better security. Currently any container is opened to share
its /dev/shm with any other container.
Obviously, this change will break the following usage scenario:
$ docker run -d --name donor busybox top
$ docker run --rm -it --ipc container:donor busybox sh
Error response from daemon: linux spec namespaces: can't join IPC
of container <ID>: non-shareable IPC (hint: use IpcMode:shareable
for the donor container)
The soution, as hinted by the (amended) error message, is to
explicitly enable donor sharing by using --ipc shareable:
$ docker run -d --name donor --ipc shareable busybox top
Compatibility notes:
1. This only applies to containers created _after_ this change.
Existing containers are not affected and will work fine
as their ipc mode is stored in HostConfig.
2. Old backward compatible behavior ("shareable" containers
by default) can be enabled by either using
`--default-ipc-mode shareable` daemon command line option,
or by adding a `"default-ipc-mode": "shareable"`
line in `/etc/docker/daemon.json` configuration file.
3. If an older client (API < 1.40) is used, a "shareable" container
is created. A test to check that is added.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2017-11-27 19:10:44 -05:00
|
|
|
|
|
|
|
inspect, err = c.ContainerInspect(ctx, cID)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(string(inspect.HostConfig.IpcMode), "shareable"))
|
|
|
|
}
|