diff --git a/daemon/seccomp_linux.go b/daemon/seccomp_linux.go index 019400082443a..0d14022db9d02 100644 --- a/daemon/seccomp_linux.go +++ b/daemon/seccomp_linux.go @@ -22,7 +22,11 @@ func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts { return nil } if c.HostConfig.Privileged { - return nil + var err error + if c.SeccompProfile != "" { + s.Linux.Seccomp, err = seccomp.LoadProfile(c.SeccompProfile, s) + } + return err } if !daemon.RawSysInfo().Seccomp { if c.SeccompProfile != "" && c.SeccompProfile != dconfig.SeccompProfileDefault { diff --git a/daemon/seccomp_linux_test.go b/daemon/seccomp_linux_test.go index 63c2201924411..9d0928933dbe6 100644 --- a/daemon/seccomp_linux_test.go +++ b/daemon/seccomp_linux_test.go @@ -40,7 +40,9 @@ func TestWithSeccomp(t *testing.T) { outSpec: oci.DefaultLinuxSpec(), }, { - comment: "privileged container w/ custom profile runs unconfined", + // Prior to Docker v27, it had resulted in unconfined. + // https://github.com/moby/moby/pull/47500 + comment: "privileged container w/ custom profile", daemon: &Daemon{ sysInfo: &sysinfo.SysInfo{Seccomp: true}, }, @@ -50,8 +52,15 @@ func TestWithSeccomp(t *testing.T) { Privileged: true, }, }, - inSpec: oci.DefaultLinuxSpec(), - outSpec: oci.DefaultLinuxSpec(), + inSpec: oci.DefaultLinuxSpec(), + outSpec: func() coci.Spec { + s := oci.DefaultLinuxSpec() + profile := &specs.LinuxSeccomp{ + DefaultAction: specs.LinuxSeccompAction("SCMP_ACT_LOG"), + } + s.Linux.Seccomp = profile + return s + }(), }, { comment: "privileged container w/ default runs unconfined", diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go index 867544705d829..639afa4532384 100644 --- a/integration/container/run_linux_test.go +++ b/integration/container/run_linux_test.go @@ -353,6 +353,51 @@ func TestWorkingDirNormalization(t *testing.T) { assert.Check(t, is.Equal(inspect.Config.WorkingDir, "/tmp")) }) + } +} + +func TestSeccomp(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + + ctx := setupTest(t) + apiClient := testEnv.APIClient() + const confined = `{ + "defaultAction": "SCMP_ACT_ALLOW", + "syscalls": [ { "names": [ "chown" ], "action": "SCMP_ACT_ERRNO" } ] +} +` + type testCase struct { + ops []func(*container.TestContainerConfig) + expectedExitCode int + } + testCases := []testCase{ + { + ops: nil, + expectedExitCode: 0, + }, + { + ops: []func(*container.TestContainerConfig){container.WithPrivileged(true)}, + expectedExitCode: 0, + }, + { + ops: []func(*container.TestContainerConfig){container.WithSecurityOpt("seccomp=" + confined)}, + expectedExitCode: 1, + }, + { + // A custom profile should be still enabled, even when --privileged is set + // https://github.com/moby/moby/issues/47499 + ops: []func(*container.TestContainerConfig){container.WithPrivileged(true), container.WithSecurityOpt("seccomp=" + confined)}, + expectedExitCode: 1, + }, + } + for _, tc := range testCases { + cID := container.Run(ctx, t, apiClient, tc.ops...) + res, err := container.Exec(ctx, apiClient, cID, []string{"chown", "42", "/bin/true"}) + assert.NilError(t, err) + assert.Equal(t, tc.expectedExitCode, res.ExitCode) + if tc.expectedExitCode != 0 { + assert.Check(t, is.Contains(res.Stderr(), "Operation not permitted")) + } } }