From 7e3a596a63fd8d0ab958132901b6ded81f8b44c0 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Fri, 2 Dec 2016 16:41:26 +0000 Subject: [PATCH] Block obsolete socket families in the default seccomp profile Linux supports many obsolete address families, which are usually available in common distro kernels, but they are less likely to be properly audited and may have security issues This blocks all socket families in the socket (and socketcall where applicable) syscall except - AF_UNIX - Unix domain sockets - AF_INET - IPv4 - AF_INET6 - IPv6 - AF_NETLINK - Netlink sockets for communicating with the ekrnel - AF_PACKET - raw sockets, which are only allowed with CAP_NET_RAW All other socket families are blocked, including Appletalk (native, not over IP), IPX (remember that!), VSOCK and HVSOCK, which should not generally be used in containers, etc. Note that users can of course provide a profile per container or in the daemon config if they have unusual use cases that require these. Signed-off-by: Justin Cormack --- contrib/syscall-test/Dockerfile | 3 +- contrib/syscall-test/appletalk.c | 12 + integration-cli/docker_cli_run_unix_test.go | 14 +- integration-cli/fixtures_linux_daemon_test.go | 2 +- profiles/seccomp/default.json | 219 +++++++++++++++++- profiles/seccomp/seccomp_default.go | 149 +++++++++++- 6 files changed, 392 insertions(+), 7 deletions(-) create mode 100644 contrib/syscall-test/appletalk.c diff --git a/contrib/syscall-test/Dockerfile b/contrib/syscall-test/Dockerfile index f95f1758c0..fcf5892be4 100644 --- a/contrib/syscall-test/Dockerfile +++ b/contrib/syscall-test/Dockerfile @@ -10,6 +10,7 @@ RUN gcc -g -Wall -static userns.c -o /usr/bin/userns-test \ && gcc -g -Wall -static setuid.c -o /usr/bin/setuid-test \ && gcc -g -Wall -static setgid.c -o /usr/bin/setgid-test \ && gcc -g -Wall -static socket.c -o /usr/bin/socket-test \ - && gcc -g -Wall -static raw.c -o /usr/bin/raw-test + && gcc -g -Wall -static raw.c -o /usr/bin/raw-test \ + && gcc -g -Wall -static appletalk.c -o /usr/bin/appletalk-test RUN [ "$(uname -m)" = "x86_64" ] && gcc -s -m32 -nostdlib exit32.s -o /usr/bin/exit32-test || true diff --git a/contrib/syscall-test/appletalk.c b/contrib/syscall-test/appletalk.c new file mode 100644 index 0000000000..0001dd4247 --- /dev/null +++ b/contrib/syscall-test/appletalk.c @@ -0,0 +1,12 @@ +#include +#include + +int main() { + + if (socket(AF_APPLETALK, SOCK_DGRAM, 0) != -1) { + fprintf(stderr, "Opening Appletalk socket worked, should be blocked\n"); + return 1; + } + + return 0; +} diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go index 172368e793..147ca598f0 100644 --- a/integration-cli/docker_cli_run_unix_test.go +++ b/integration-cli/docker_cli_run_unix_test.go @@ -978,7 +978,7 @@ func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) { } // TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:jessie unshare --map-root-user --user sh -c whoami' with a specific profile to -// deny unhare of a userns exits with operation not permitted. +// deny unshare of a userns exits with operation not permitted. func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) { testRequires(c, SameHostDaemon, seccompEnabled, NotArm, Apparmor) // from sched.h @@ -1015,6 +1015,18 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) { }) } +// TestRunSeccompProfileDenyUnusualSocketFamilies checks that rarely used socket families such as Appletalk are blocked by the default profile +func (s *DockerSuite) TestRunSeccompProfileDenyUnusualSocketFamilies(c *check.C) { + testRequires(c, SameHostDaemon, seccompEnabled) + ensureSyscallTest(c) + + runCmd := exec.Command(dockerBinary, "run", "syscall-test", "appletalk-test") + _, _, err := runCommandWithOutput(runCmd) + if err != nil { + c.Fatal("expected opening appletalk socket family to fail") + } +} + // TestRunSeccompProfileDenyCloneUserns checks that 'docker run syscall-test' // with a the default seccomp profile exits with operation not permitted. func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) { diff --git a/integration-cli/fixtures_linux_daemon_test.go b/integration-cli/fixtures_linux_daemon_test.go index 9b76724194..ed3db7fe4b 100644 --- a/integration-cli/fixtures_linux_daemon_test.go +++ b/integration-cli/fixtures_linux_daemon_test.go @@ -62,7 +62,7 @@ func ensureSyscallTest(c *check.C) { gcc, err := exec.LookPath("gcc") c.Assert(err, checker.IsNil, check.Commentf("could not find gcc")) - tests := []string{"userns", "ns", "acct", "setuid", "setgid", "socket", "raw"} + tests := []string{"userns", "ns", "acct", "setuid", "setgid", "socket", "raw", "appletalk"} for _, test := range tests { out, err := exec.Command(gcc, "-g", "-Wall", "-static", fmt.Sprintf("../contrib/syscall-test/%s.c", test), "-o", fmt.Sprintf("%s/%s-test", tmp, test)).CombinedOutput() c.Assert(err, checker.IsNil, check.Commentf(string(out))) diff --git a/profiles/seccomp/default.json b/profiles/seccomp/default.json index 804d8e5c68..066dc9a7c5 100755 --- a/profiles/seccomp/default.json +++ b/profiles/seccomp/default.json @@ -312,8 +312,6 @@ "signalfd", "signalfd4", "sigreturn", - "socket", - "socketcall", "socketpair", "splice", "stat", @@ -415,6 +413,223 @@ "includes": {}, "excludes": {} }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 10, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 16, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 17, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socketcall" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_GT" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socketcall" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + }, + { + "index": 1, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socketcall" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + }, + { + "index": 1, + "value": 2, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socketcall" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + }, + { + "index": 1, + "value": 10, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socketcall" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + }, + { + "index": 1, + "value": 16, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "socketcall" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 1, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + }, + { + "index": 1, + "value": 17, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, { "names": [ "breakpoint", diff --git a/profiles/seccomp/seccomp_default.go b/profiles/seccomp/seccomp_default.go index b392640c92..9527793a45 100644 --- a/profiles/seccomp/seccomp_default.go +++ b/profiles/seccomp/seccomp_default.go @@ -306,8 +306,6 @@ func DefaultProfile() *types.Seccomp { "signalfd", "signalfd4", "sigreturn", - "socket", - "socketcall", "socketpair", "splice", "stat", @@ -388,6 +386,153 @@ func DefaultProfile() *types.Seccomp { }, }, }, + { + Names: []string{"socket"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: syscall.AF_UNIX, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socket"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: syscall.AF_INET, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socket"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: syscall.AF_INET6, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socket"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: syscall.AF_NETLINK, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socket"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: syscall.AF_PACKET, + Op: types.OpEqualTo, + }, + }, + }, + // socketcall(1, ...) is equivalent to socket(...) on some architectures eg i386 + { + Names: []string{"socketcall"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: 1, + Op: types.OpGreaterThan, + }, + }, + }, + { + Names: []string{"socketcall"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: 1, + Op: types.OpEqualTo, + }, + { + Index: 1, + Value: syscall.AF_UNIX, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socketcall"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: 1, + Op: types.OpEqualTo, + }, + { + Index: 1, + Value: syscall.AF_INET, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socketcall"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: 1, + Op: types.OpEqualTo, + }, + { + Index: 1, + Value: syscall.AF_INET6, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socketcall"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: 1, + Op: types.OpEqualTo, + }, + { + Index: 1, + Value: syscall.AF_NETLINK, + Op: types.OpEqualTo, + }, + }, + }, + { + Names: []string{"socketcall"}, + Action: types.ActAllow, + Args: []*types.Arg{ + { + Index: 0, + Value: 1, + Op: types.OpEqualTo, + }, + { + Index: 1, + Value: syscall.AF_PACKET, + Op: types.OpEqualTo, + }, + }, + }, { Names: []string{ "breakpoint",