Fix validation of IpcMode, PidMode, UTSMode, CgroupnsMode
These HostConfig properties were not validated until the OCI spec for the container was created, which meant that `container run` and `docker create` would accept invalid values, and the invalid value would not be detected until `start` was called, returning a 500 "internal server error", as well as errors from containerd ("cleanup: failed to delete container from containerd: no such container") in the daemon logs. As a result, a faulty container was created, and the container state remained in the `created` state. This patch: - Updates `oci.WithNamespaces()` to return the correct `errdefs.InvalidParameter` - Updates `verifyPlatformContainerSettings()` to validate these settings, so that an error is returned when _creating_ the container. Before this patch: docker run -dit --ipc=shared --name foo busybox 2a00d74e9fbb7960c4718def8f6c74fa8ee754030eeb93ee26a516e27d4d029f docker: Error response from daemon: Invalid IPC mode: shared. docker ps -a --filter name=foo CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2a00d74e9fbb busybox "sh" About a minute ago Created foo After this patch: docker run -dit --ipc=shared --name foo busybox docker: Error response from daemon: invalid IPC mode: shared. docker ps -a --filter name=foo CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES An integration test was added to verify the new validation, which can be run with: make BIND_DIR=. TEST_FILTER=TestCreateInvalidHostConfig DOCKER_GRAPHDRIVER=vfs test-integration Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
235f86270d
commit
c3d7a0c603
|
@ -660,9 +660,18 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
|
||||||
return warnings, err
|
return warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !hostConfig.IpcMode.Valid() {
|
||||||
|
return warnings, errors.Errorf("invalid IPC mode: %v", hostConfig.IpcMode)
|
||||||
|
}
|
||||||
|
if !hostConfig.PidMode.Valid() {
|
||||||
|
return warnings, errors.Errorf("invalid PID mode: %v", hostConfig.PidMode)
|
||||||
|
}
|
||||||
if hostConfig.ShmSize < 0 {
|
if hostConfig.ShmSize < 0 {
|
||||||
return warnings, fmt.Errorf("SHM size can not be less than 0")
|
return warnings, fmt.Errorf("SHM size can not be less than 0")
|
||||||
}
|
}
|
||||||
|
if !hostConfig.UTSMode.Valid() {
|
||||||
|
return warnings, errors.Errorf("invalid UTS mode: %v", hostConfig.UTSMode)
|
||||||
|
}
|
||||||
|
|
||||||
if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
|
if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
|
||||||
return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj)
|
return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
dconfig "github.com/docker/docker/daemon/config"
|
dconfig "github.com/docker/docker/daemon/config"
|
||||||
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/docker/docker/oci"
|
"github.com/docker/docker/oci"
|
||||||
"github.com/docker/docker/oci/caps"
|
"github.com/docker/docker/oci/caps"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
@ -260,12 +261,15 @@ func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts {
|
||||||
|
|
||||||
// ipc
|
// ipc
|
||||||
ipcMode := c.HostConfig.IpcMode
|
ipcMode := c.HostConfig.IpcMode
|
||||||
|
if !ipcMode.Valid() {
|
||||||
|
return errdefs.InvalidParameter(errors.Errorf("invalid IPC mode: %v", ipcMode))
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case ipcMode.IsContainer():
|
case ipcMode.IsContainer():
|
||||||
ns := specs.LinuxNamespace{Type: "ipc"}
|
ns := specs.LinuxNamespace{Type: "ipc"}
|
||||||
ic, err := daemon.getIpcContainer(ipcMode.Container())
|
ic, err := daemon.getIpcContainer(ipcMode.Container())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errdefs.InvalidParameter(errors.Wrapf(err, "invalid IPC mode: %v", ipcMode))
|
||||||
}
|
}
|
||||||
ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID())
|
ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID())
|
||||||
setNamespace(s, ns)
|
setNamespace(s, ns)
|
||||||
|
@ -284,11 +288,12 @@ func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts {
|
||||||
case ipcMode.IsPrivate(), ipcMode.IsShareable(), ipcMode.IsNone():
|
case ipcMode.IsPrivate(), ipcMode.IsShareable(), ipcMode.IsNone():
|
||||||
ns := specs.LinuxNamespace{Type: "ipc"}
|
ns := specs.LinuxNamespace{Type: "ipc"}
|
||||||
setNamespace(s, ns)
|
setNamespace(s, ns)
|
||||||
default:
|
|
||||||
return fmt.Errorf("Invalid IPC mode: %v", ipcMode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pid
|
// pid
|
||||||
|
if !c.HostConfig.PidMode.Valid() {
|
||||||
|
return errdefs.InvalidParameter(errors.Errorf("invalid PID mode: %v", c.HostConfig.PidMode))
|
||||||
|
}
|
||||||
if c.HostConfig.PidMode.IsContainer() {
|
if c.HostConfig.PidMode.IsContainer() {
|
||||||
pc, err := daemon.getPidContainer(c)
|
pc, err := daemon.getPidContainer(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -314,18 +319,20 @@ func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts {
|
||||||
setNamespace(s, ns)
|
setNamespace(s, ns)
|
||||||
}
|
}
|
||||||
// uts
|
// uts
|
||||||
|
if !c.HostConfig.UTSMode.Valid() {
|
||||||
|
return errdefs.InvalidParameter(errors.Errorf("invalid UTS mode: %v", c.HostConfig.UTSMode))
|
||||||
|
}
|
||||||
if c.HostConfig.UTSMode.IsHost() {
|
if c.HostConfig.UTSMode.IsHost() {
|
||||||
oci.RemoveNamespace(s, "uts")
|
oci.RemoveNamespace(s, "uts")
|
||||||
s.Hostname = ""
|
s.Hostname = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// cgroup
|
// cgroup
|
||||||
if !c.HostConfig.CgroupnsMode.IsEmpty() {
|
if !c.HostConfig.CgroupnsMode.Valid() {
|
||||||
cgroupNsMode := c.HostConfig.CgroupnsMode
|
return errdefs.InvalidParameter(errors.Errorf("invalid cgroup namespace mode: %v", c.HostConfig.CgroupnsMode))
|
||||||
if !cgroupNsMode.Valid() {
|
|
||||||
return fmt.Errorf("invalid cgroup namespace mode: %v", cgroupNsMode)
|
|
||||||
}
|
}
|
||||||
if cgroupNsMode.IsPrivate() {
|
if !c.HostConfig.CgroupnsMode.IsEmpty() {
|
||||||
|
if c.HostConfig.CgroupnsMode.IsPrivate() {
|
||||||
nsCgroup := specs.LinuxNamespace{Type: "cgroup"}
|
nsCgroup := specs.LinuxNamespace{Type: "cgroup"}
|
||||||
setNamespace(s, nsCgroup)
|
setNamespace(s, nsCgroup)
|
||||||
}
|
}
|
||||||
|
|
|
@ -528,3 +528,52 @@ func TestCreatePlatformSpecificImageNoPlatform(t *testing.T) {
|
||||||
)
|
)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateInvalidHostConfig(t *testing.T) {
|
||||||
|
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
||||||
|
|
||||||
|
defer setupTest(t)()
|
||||||
|
apiClient := testEnv.APIClient()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
doc string
|
||||||
|
hc containertypes.HostConfig
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
doc: "invalid IpcMode",
|
||||||
|
hc: containertypes.HostConfig{IpcMode: "invalid"},
|
||||||
|
expectedErr: "Error response from daemon: invalid IPC mode: invalid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid PidMode",
|
||||||
|
hc: containertypes.HostConfig{PidMode: "invalid"},
|
||||||
|
expectedErr: "Error response from daemon: invalid PID mode: invalid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid PidMode without container ID",
|
||||||
|
hc: containertypes.HostConfig{PidMode: "container"},
|
||||||
|
expectedErr: "Error response from daemon: invalid PID mode: container",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid UTSMode",
|
||||||
|
hc: containertypes.HostConfig{UTSMode: "invalid"},
|
||||||
|
expectedErr: "Error response from daemon: invalid UTS mode: invalid",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.doc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
cfg := container.Config{
|
||||||
|
Image: "busybox",
|
||||||
|
}
|
||||||
|
resp, err := apiClient.ContainerCreate(ctx, &cfg, &tc.hc, nil, nil, "")
|
||||||
|
assert.Check(t, is.Equal(len(resp.Warnings), 0))
|
||||||
|
assert.Check(t, errdefs.IsInvalidParameter(err), "got: %T", err)
|
||||||
|
assert.Error(t, err, tc.expectedErr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue