Skip to content

Commit a01c4dc

Browse files
committed
Align default seccomp profile with selected capabilities
Currently the default seccomp profile is fixed. This changes it so that it varies depending on the Linux capabilities selected with the --cap-add and --cap-drop options. Without this, if a user adds privileges, eg to allow ptrace with --cap-add sys_ptrace then still cannot actually use ptrace as it is still blocked by seccomp, so they will probably disable seccomp or use --privileged. With this change the syscalls that are needed for the capability are also allowed by the seccomp profile based on the selected capabilities. While this patch makes it easier to do things with for example cap_sys_admin enabled, as it will now allow creating new namespaces and use of mount, it still allows less than --cap-add cap_sys_admin --security-opt seccomp:unconfined would have previously. It is not recommended that users run containers with cap_sys_admin as this does give full access to the host machine. It also cleans up some architecture specific system calls to be only selected when needed. Signed-off-by: Justin Cormack <[email protected]>
1 parent af60a9e commit a01c4dc

8 files changed

Lines changed: 464 additions & 197 deletions

File tree

daemon/seccomp_linux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
3535
return err
3636
}
3737
} else {
38-
profile, err = seccomp.GetDefaultProfile()
38+
profile, err = seccomp.GetDefaultProfile(rs)
3939
if err != nil {
4040
return err
4141
}

docs/reference/run.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,14 +1071,6 @@ one can use this flag:
10711071
--privileged=false: Give extended privileges to this container
10721072
--device=[]: Allows you to run devices inside the container without the --privileged flag.
10731073

1074-
> **Note:**
1075-
> With Docker 1.10 and greater, the default seccomp profile will also block
1076-
> syscalls, regardless of `--cap-add` passed to the container. We recommend in
1077-
> these cases to create your own custom seccomp profile based off our
1078-
> [default](https://github.com/docker/docker/blob/master/profiles/seccomp/default.json).
1079-
> Or if you don't want to run with the default seccomp profile, you can pass
1080-
> `--security-opt=seccomp=unconfined` on run.
1081-
10821074
By default, Docker containers are "unprivileged" and cannot, for
10831075
example, run a Docker daemon inside a Docker container. This is because
10841076
by default a container is not allowed to access any devices, but a
@@ -1196,6 +1188,11 @@ To mount a FUSE based filesystem, you need to combine both `--cap-add` and
11961188
-rw-rw-r-- 1 1000 1000 461 Dec 4 06:08 .gitignore
11971189
....
11981190

1191+
The default seccomp profile will adjust to the selected capabilities, in order to allow
1192+
use of facilities allowed by the capabilities, so you should not have to adjust this,
1193+
since Docker 1.12. In Docker 1.10 and 1.11 this did not happen and it may be necessary
1194+
to use a custom seccomp profile or use `--security-opt seccomp=unconfined` when adding
1195+
capabilities.
11991196

12001197
## Logging drivers (--log-driver)
12011198

integration-cli/docker_cli_run_unix_test.go

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -948,24 +948,80 @@ func (s *DockerSuite) TestRunSeccompDefaultProfile(c *check.C) {
948948
testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
949949

950950
var group sync.WaitGroup
951-
group.Add(4)
951+
group.Add(11)
952952
errChan := make(chan error, 4)
953953
go func() {
954-
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "acct-test")
954+
out, _, err := dockerCmdWithError("run", "syscall-test", "acct-test")
955955
if err == nil || !strings.Contains(out, "Operation not permitted") {
956956
errChan <- fmt.Errorf("expected Operation not permitted, got: %s", out)
957957
}
958958
group.Done()
959959
}()
960960

961961
go func() {
962-
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "ns-test", "echo", "hello")
962+
out, _, err := dockerCmdWithError("run", "--cap-add", "sys_admin", "syscall-test", "acct-test")
963963
if err == nil || !strings.Contains(out, "Operation not permitted") {
964964
errChan <- fmt.Errorf("expected Operation not permitted, got: %s", out)
965965
}
966966
group.Done()
967967
}()
968968

969+
go func() {
970+
out, _, err := dockerCmdWithError("run", "--cap-add", "sys_pacct", "syscall-test", "acct-test")
971+
if err == nil || !strings.Contains(out, "No such file or directory") {
972+
errChan <- fmt.Errorf("expected No such file or directory, got: %s", out)
973+
}
974+
group.Done()
975+
}()
976+
977+
go func() {
978+
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "acct-test")
979+
if err == nil || !strings.Contains(out, "No such file or directory") {
980+
errChan <- fmt.Errorf("expected No such file or directory, got: %s", out)
981+
}
982+
group.Done()
983+
}()
984+
985+
go func() {
986+
out, _, err := dockerCmdWithError("run", "--cap-drop", "ALL", "--cap-add", "sys_pacct", "syscall-test", "acct-test")
987+
if err == nil || !strings.Contains(out, "No such file or directory") {
988+
errChan <- fmt.Errorf("expected No such file or directory, got: %s", out)
989+
}
990+
group.Done()
991+
}()
992+
993+
go func() {
994+
out, _, err := dockerCmdWithError("run", "syscall-test", "ns-test", "echo", "hello0")
995+
if err == nil || !strings.Contains(out, "Operation not permitted") {
996+
errChan <- fmt.Errorf("expected Operation not permitted, got: %s", out)
997+
}
998+
group.Done()
999+
}()
1000+
1001+
go func() {
1002+
out, _, err := dockerCmdWithError("run", "--cap-add", "sys_admin", "syscall-test", "ns-test", "echo", "hello1")
1003+
if err != nil || !strings.Contains(out, "hello1") {
1004+
errChan <- fmt.Errorf("expected hello1, got: %s, %v", out, err)
1005+
}
1006+
group.Done()
1007+
}()
1008+
1009+
go func() {
1010+
out, _, err := dockerCmdWithError("run", "--cap-drop", "all", "--cap-add", "sys_admin", "syscall-test", "ns-test", "echo", "hello2")
1011+
if err != nil || !strings.Contains(out, "hello2") {
1012+
errChan <- fmt.Errorf("expected hello2, got: %s, %v", out, err)
1013+
}
1014+
group.Done()
1015+
}()
1016+
1017+
go func() {
1018+
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "ns-test", "echo", "hello3")
1019+
if err != nil || !strings.Contains(out, "hello3") {
1020+
errChan <- fmt.Errorf("expected hello3, got: %s, %v", out, err)
1021+
}
1022+
group.Done()
1023+
}()
1024+
9691025
go func() {
9701026
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp=unconfined", "syscall-test", "acct-test")
9711027
if err == nil || !strings.Contains(out, "No such file or directory") {
@@ -975,9 +1031,9 @@ func (s *DockerSuite) TestRunSeccompDefaultProfile(c *check.C) {
9751031
}()
9761032

9771033
go func() {
978-
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp=unconfined", "syscall-test", "ns-test", "echo", "hello")
979-
if err != nil || !strings.Contains(out, "hello") {
980-
errChan <- fmt.Errorf("expected hello, got: %s, %v", out, err)
1034+
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp=unconfined", "syscall-test", "ns-test", "echo", "hello4")
1035+
if err != nil || !strings.Contains(out, "hello4") {
1036+
errChan <- fmt.Errorf("expected hello4, got: %s, %v", out, err)
9811037
}
9821038
group.Done()
9831039
}()

profiles/seccomp/default.json

Lines changed: 50 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@
2626
"action": "SCMP_ACT_ALLOW",
2727
"args": []
2828
},
29-
{
30-
"name": "arch_prctl",
31-
"action": "SCMP_ACT_ALLOW",
32-
"args": []
33-
},
3429
{
3530
"name": "bind",
3631
"action": "SCMP_ACT_ALLOW",
@@ -61,21 +56,6 @@
6156
"action": "SCMP_ACT_ALLOW",
6257
"args": []
6358
},
64-
{
65-
"name": "chown",
66-
"action": "SCMP_ACT_ALLOW",
67-
"args": []
68-
},
69-
{
70-
"name": "chown32",
71-
"action": "SCMP_ACT_ALLOW",
72-
"args": []
73-
},
74-
{
75-
"name": "chroot",
76-
"action": "SCMP_ACT_ALLOW",
77-
"args": []
78-
},
7959
{
8060
"name": "clock_getres",
8161
"action": "SCMP_ACT_ALLOW",
@@ -91,18 +71,6 @@
9171
"action": "SCMP_ACT_ALLOW",
9272
"args": []
9373
},
94-
{
95-
"name": "clone",
96-
"action": "SCMP_ACT_ALLOW",
97-
"args": [
98-
{
99-
"index": 0,
100-
"value": 2080505856,
101-
"valueTwo": 0,
102-
"op": "SCMP_CMP_MASKED_EQ"
103-
}
104-
]
105-
},
10674
{
10775
"name": "close",
10876
"action": "SCMP_ACT_ALLOW",
@@ -223,11 +191,6 @@
223191
"action": "SCMP_ACT_ALLOW",
224192
"args": []
225193
},
226-
{
227-
"name": "fanotify_init",
228-
"action": "SCMP_ACT_ALLOW",
229-
"args": []
230-
},
231194
{
232195
"name": "fanotify_mark",
233196
"action": "SCMP_ACT_ALLOW",
@@ -248,21 +211,6 @@
248211
"action": "SCMP_ACT_ALLOW",
249212
"args": []
250213
},
251-
{
252-
"name": "fchown",
253-
"action": "SCMP_ACT_ALLOW",
254-
"args": []
255-
},
256-
{
257-
"name": "fchown32",
258-
"action": "SCMP_ACT_ALLOW",
259-
"args": []
260-
},
261-
{
262-
"name": "fchownat",
263-
"action": "SCMP_ACT_ALLOW",
264-
"args": []
265-
},
266214
{
267215
"name": "fcntl",
268216
"action": "SCMP_ACT_ALLOW",
@@ -608,16 +556,6 @@
608556
"action": "SCMP_ACT_ALLOW",
609557
"args": []
610558
},
611-
{
612-
"name": "lchown",
613-
"action": "SCMP_ACT_ALLOW",
614-
"args": []
615-
},
616-
{
617-
"name": "lchown32",
618-
"action": "SCMP_ACT_ALLOW",
619-
"args": []
620-
},
621559
{
622560
"name": "lgetxattr",
623561
"action": "SCMP_ACT_ALLOW",
@@ -1164,11 +1102,6 @@
11641102
"action": "SCMP_ACT_ALLOW",
11651103
"args": []
11661104
},
1167-
{
1168-
"name": "setdomainname",
1169-
"action": "SCMP_ACT_ALLOW",
1170-
"args": []
1171-
},
11721105
{
11731106
"name": "setfsgid",
11741107
"action": "SCMP_ACT_ALLOW",
@@ -1209,11 +1142,6 @@
12091142
"action": "SCMP_ACT_ALLOW",
12101143
"args": []
12111144
},
1212-
{
1213-
"name": "sethostname",
1214-
"action": "SCMP_ACT_ALLOW",
1215-
"args": []
1216-
},
12171145
{
12181146
"name": "setitimer",
12191147
"action": "SCMP_ACT_ALLOW",
@@ -1579,23 +1507,70 @@
15791507
"action": "SCMP_ACT_ALLOW",
15801508
"args": []
15811509
},
1510+
{
1511+
"name": "arch_prctl",
1512+
"action": "SCMP_ACT_ALLOW",
1513+
"args": []
1514+
},
15821515
{
15831516
"name": "modify_ldt",
15841517
"action": "SCMP_ACT_ALLOW",
15851518
"args": []
15861519
},
15871520
{
1588-
"name": "breakpoint",
1521+
"name": "chown",
1522+
"action": "SCMP_ACT_ALLOW",
1523+
"args": []
1524+
},
1525+
{
1526+
"name": "chown32",
15891527
"action": "SCMP_ACT_ALLOW",
15901528
"args": []
15911529
},
15921530
{
1593-
"name": "cacheflush",
1531+
"name": "fchown",
15941532
"action": "SCMP_ACT_ALLOW",
15951533
"args": []
15961534
},
15971535
{
1598-
"name": "set_tls",
1536+
"name": "fchown32",
1537+
"action": "SCMP_ACT_ALLOW",
1538+
"args": []
1539+
},
1540+
{
1541+
"name": "fchownat",
1542+
"action": "SCMP_ACT_ALLOW",
1543+
"args": []
1544+
},
1545+
{
1546+
"name": "lchown",
1547+
"action": "SCMP_ACT_ALLOW",
1548+
"args": []
1549+
},
1550+
{
1551+
"name": "lchown32",
1552+
"action": "SCMP_ACT_ALLOW",
1553+
"args": []
1554+
},
1555+
{
1556+
"name": "chroot",
1557+
"action": "SCMP_ACT_ALLOW",
1558+
"args": []
1559+
},
1560+
{
1561+
"name": "clone",
1562+
"action": "SCMP_ACT_ALLOW",
1563+
"args": [
1564+
{
1565+
"index": 0,
1566+
"value": 2080505856,
1567+
"valueTwo": 0,
1568+
"op": "SCMP_CMP_MASKED_EQ"
1569+
}
1570+
]
1571+
},
1572+
{
1573+
"name": "fchown",
15991574
"action": "SCMP_ACT_ALLOW",
16001575
"args": []
16011576
}

profiles/seccomp/generate.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"path/filepath"
1010

11+
"github.com/docker/docker/oci"
1112
"github.com/docker/docker/profiles/seccomp"
1213
)
1314

@@ -20,8 +21,10 @@ func main() {
2021
}
2122
f := filepath.Join(wd, "default.json")
2223

24+
rs := oci.DefaultSpec()
25+
2326
// write the default profile to the file
24-
b, err := json.MarshalIndent(seccomp.DefaultProfile, "", "\t")
27+
b, err := json.MarshalIndent(seccomp.DefaultProfile(&rs), "", "\t")
2528
if err != nil {
2629
panic(err)
2730
}

profiles/seccomp/seccomp.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import (
1313
//go:generate go run -tags 'seccomp' generate.go
1414

1515
// GetDefaultProfile returns the default seccomp profile.
16-
func GetDefaultProfile() (*specs.Seccomp, error) {
17-
return setupSeccomp(DefaultProfile)
16+
func GetDefaultProfile(rs *specs.Spec) (*specs.Seccomp, error) {
17+
return setupSeccomp(DefaultProfile(rs))
1818
}
1919

2020
// LoadProfile takes a file path and decodes the seccomp profile.

0 commit comments

Comments
 (0)