Browse code

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 <justin.cormack@docker.com>

Justin Cormack authored on 2016/12/03 01:41:26
Showing 6 changed files
... ...
@@ -10,6 +10,7 @@ RUN gcc -g -Wall -static userns.c -o /usr/bin/userns-test \
10 10
 	&& gcc -g -Wall -static setuid.c -o /usr/bin/setuid-test \
11 11
 	&& gcc -g -Wall -static setgid.c -o /usr/bin/setgid-test \
12 12
 	&& gcc -g -Wall -static socket.c -o /usr/bin/socket-test \
13
-	&& gcc -g -Wall -static raw.c -o /usr/bin/raw-test
13
+	&& gcc -g -Wall -static raw.c -o /usr/bin/raw-test \
14
+	&& gcc -g -Wall -static appletalk.c -o /usr/bin/appletalk-test
14 15
 
15 16
 RUN [ "$(uname -m)" = "x86_64" ] && gcc -s -m32 -nostdlib exit32.s -o /usr/bin/exit32-test || true
16 17
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+#include <stdio.h>
1
+#include <sys/socket.h>
2
+
3
+int main() {
4
+
5
+	if (socket(AF_APPLETALK, SOCK_DGRAM, 0) != -1) {
6
+		fprintf(stderr, "Opening Appletalk socket worked, should be blocked\n");
7
+		return 1;
8
+	}
9
+
10
+	return 0;
11
+}
... ...
@@ -978,7 +978,7 @@ func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) {
978 978
 }
979 979
 
980 980
 // TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:jessie unshare --map-root-user --user sh -c whoami' with a specific profile to
981
-// deny unhare of a userns exits with operation not permitted.
981
+// deny unshare of a userns exits with operation not permitted.
982 982
 func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
983 983
 	testRequires(c, SameHostDaemon, seccompEnabled, NotArm, Apparmor)
984 984
 	// from sched.h
... ...
@@ -1015,6 +1015,18 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
1015 1015
 	})
1016 1016
 }
1017 1017
 
1018
+// TestRunSeccompProfileDenyUnusualSocketFamilies checks that rarely used socket families such as Appletalk are blocked by the default profile
1019
+func (s *DockerSuite) TestRunSeccompProfileDenyUnusualSocketFamilies(c *check.C) {
1020
+	testRequires(c, SameHostDaemon, seccompEnabled)
1021
+	ensureSyscallTest(c)
1022
+
1023
+	runCmd := exec.Command(dockerBinary, "run", "syscall-test", "appletalk-test")
1024
+	_, _, err := runCommandWithOutput(runCmd)
1025
+	if err != nil {
1026
+		c.Fatal("expected opening appletalk socket family to fail")
1027
+	}
1028
+}
1029
+
1018 1030
 // TestRunSeccompProfileDenyCloneUserns checks that 'docker run syscall-test'
1019 1031
 // with a the default seccomp profile exits with operation not permitted.
1020 1032
 func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) {
... ...
@@ -62,7 +62,7 @@ func ensureSyscallTest(c *check.C) {
62 62
 	gcc, err := exec.LookPath("gcc")
63 63
 	c.Assert(err, checker.IsNil, check.Commentf("could not find gcc"))
64 64
 
65
-	tests := []string{"userns", "ns", "acct", "setuid", "setgid", "socket", "raw"}
65
+	tests := []string{"userns", "ns", "acct", "setuid", "setgid", "socket", "raw", "appletalk"}
66 66
 	for _, test := range tests {
67 67
 		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()
68 68
 		c.Assert(err, checker.IsNil, check.Commentf(string(out)))
... ...
@@ -312,8 +312,6 @@
312 312
 				"signalfd",
313 313
 				"signalfd4",
314 314
 				"sigreturn",
315
-				"socket",
316
-				"socketcall",
317 315
 				"socketpair",
318 316
 				"splice",
319 317
 				"stat",
... ...
@@ -417,6 +415,223 @@
417 417
 		},
418 418
 		{
419 419
 			"names": [
420
+				"socket"
421
+			],
422
+			"action": "SCMP_ACT_ALLOW",
423
+			"args": [
424
+				{
425
+					"index": 0,
426
+					"value": 1,
427
+					"valueTwo": 0,
428
+					"op": "SCMP_CMP_EQ"
429
+				}
430
+			],
431
+			"comment": "",
432
+			"includes": {},
433
+			"excludes": {}
434
+		},
435
+		{
436
+			"names": [
437
+				"socket"
438
+			],
439
+			"action": "SCMP_ACT_ALLOW",
440
+			"args": [
441
+				{
442
+					"index": 0,
443
+					"value": 2,
444
+					"valueTwo": 0,
445
+					"op": "SCMP_CMP_EQ"
446
+				}
447
+			],
448
+			"comment": "",
449
+			"includes": {},
450
+			"excludes": {}
451
+		},
452
+		{
453
+			"names": [
454
+				"socket"
455
+			],
456
+			"action": "SCMP_ACT_ALLOW",
457
+			"args": [
458
+				{
459
+					"index": 0,
460
+					"value": 10,
461
+					"valueTwo": 0,
462
+					"op": "SCMP_CMP_EQ"
463
+				}
464
+			],
465
+			"comment": "",
466
+			"includes": {},
467
+			"excludes": {}
468
+		},
469
+		{
470
+			"names": [
471
+				"socket"
472
+			],
473
+			"action": "SCMP_ACT_ALLOW",
474
+			"args": [
475
+				{
476
+					"index": 0,
477
+					"value": 16,
478
+					"valueTwo": 0,
479
+					"op": "SCMP_CMP_EQ"
480
+				}
481
+			],
482
+			"comment": "",
483
+			"includes": {},
484
+			"excludes": {}
485
+		},
486
+		{
487
+			"names": [
488
+				"socket"
489
+			],
490
+			"action": "SCMP_ACT_ALLOW",
491
+			"args": [
492
+				{
493
+					"index": 0,
494
+					"value": 17,
495
+					"valueTwo": 0,
496
+					"op": "SCMP_CMP_EQ"
497
+				}
498
+			],
499
+			"comment": "",
500
+			"includes": {},
501
+			"excludes": {}
502
+		},
503
+		{
504
+			"names": [
505
+				"socketcall"
506
+			],
507
+			"action": "SCMP_ACT_ALLOW",
508
+			"args": [
509
+				{
510
+					"index": 0,
511
+					"value": 1,
512
+					"valueTwo": 0,
513
+					"op": "SCMP_CMP_GT"
514
+				}
515
+			],
516
+			"comment": "",
517
+			"includes": {},
518
+			"excludes": {}
519
+		},
520
+		{
521
+			"names": [
522
+				"socketcall"
523
+			],
524
+			"action": "SCMP_ACT_ALLOW",
525
+			"args": [
526
+				{
527
+					"index": 0,
528
+					"value": 1,
529
+					"valueTwo": 0,
530
+					"op": "SCMP_CMP_EQ"
531
+				},
532
+				{
533
+					"index": 1,
534
+					"value": 1,
535
+					"valueTwo": 0,
536
+					"op": "SCMP_CMP_EQ"
537
+				}
538
+			],
539
+			"comment": "",
540
+			"includes": {},
541
+			"excludes": {}
542
+		},
543
+		{
544
+			"names": [
545
+				"socketcall"
546
+			],
547
+			"action": "SCMP_ACT_ALLOW",
548
+			"args": [
549
+				{
550
+					"index": 0,
551
+					"value": 1,
552
+					"valueTwo": 0,
553
+					"op": "SCMP_CMP_EQ"
554
+				},
555
+				{
556
+					"index": 1,
557
+					"value": 2,
558
+					"valueTwo": 0,
559
+					"op": "SCMP_CMP_EQ"
560
+				}
561
+			],
562
+			"comment": "",
563
+			"includes": {},
564
+			"excludes": {}
565
+		},
566
+		{
567
+			"names": [
568
+				"socketcall"
569
+			],
570
+			"action": "SCMP_ACT_ALLOW",
571
+			"args": [
572
+				{
573
+					"index": 0,
574
+					"value": 1,
575
+					"valueTwo": 0,
576
+					"op": "SCMP_CMP_EQ"
577
+				},
578
+				{
579
+					"index": 1,
580
+					"value": 10,
581
+					"valueTwo": 0,
582
+					"op": "SCMP_CMP_EQ"
583
+				}
584
+			],
585
+			"comment": "",
586
+			"includes": {},
587
+			"excludes": {}
588
+		},
589
+		{
590
+			"names": [
591
+				"socketcall"
592
+			],
593
+			"action": "SCMP_ACT_ALLOW",
594
+			"args": [
595
+				{
596
+					"index": 0,
597
+					"value": 1,
598
+					"valueTwo": 0,
599
+					"op": "SCMP_CMP_EQ"
600
+				},
601
+				{
602
+					"index": 1,
603
+					"value": 16,
604
+					"valueTwo": 0,
605
+					"op": "SCMP_CMP_EQ"
606
+				}
607
+			],
608
+			"comment": "",
609
+			"includes": {},
610
+			"excludes": {}
611
+		},
612
+		{
613
+			"names": [
614
+				"socketcall"
615
+			],
616
+			"action": "SCMP_ACT_ALLOW",
617
+			"args": [
618
+				{
619
+					"index": 0,
620
+					"value": 1,
621
+					"valueTwo": 0,
622
+					"op": "SCMP_CMP_EQ"
623
+				},
624
+				{
625
+					"index": 1,
626
+					"value": 17,
627
+					"valueTwo": 0,
628
+					"op": "SCMP_CMP_EQ"
629
+				}
630
+			],
631
+			"comment": "",
632
+			"includes": {},
633
+			"excludes": {}
634
+		},
635
+		{
636
+			"names": [
420 637
 				"breakpoint",
421 638
 				"cacheflush",
422 639
 				"set_tls"
... ...
@@ -306,8 +306,6 @@ func DefaultProfile() *types.Seccomp {
306 306
 				"signalfd",
307 307
 				"signalfd4",
308 308
 				"sigreturn",
309
-				"socket",
310
-				"socketcall",
311 309
 				"socketpair",
312 310
 				"splice",
313 311
 				"stat",
... ...
@@ -389,6 +387,153 @@ func DefaultProfile() *types.Seccomp {
389 389
 			},
390 390
 		},
391 391
 		{
392
+			Names:  []string{"socket"},
393
+			Action: types.ActAllow,
394
+			Args: []*types.Arg{
395
+				{
396
+					Index: 0,
397
+					Value: syscall.AF_UNIX,
398
+					Op:    types.OpEqualTo,
399
+				},
400
+			},
401
+		},
402
+		{
403
+			Names:  []string{"socket"},
404
+			Action: types.ActAllow,
405
+			Args: []*types.Arg{
406
+				{
407
+					Index: 0,
408
+					Value: syscall.AF_INET,
409
+					Op:    types.OpEqualTo,
410
+				},
411
+			},
412
+		},
413
+		{
414
+			Names:  []string{"socket"},
415
+			Action: types.ActAllow,
416
+			Args: []*types.Arg{
417
+				{
418
+					Index: 0,
419
+					Value: syscall.AF_INET6,
420
+					Op:    types.OpEqualTo,
421
+				},
422
+			},
423
+		},
424
+		{
425
+			Names:  []string{"socket"},
426
+			Action: types.ActAllow,
427
+			Args: []*types.Arg{
428
+				{
429
+					Index: 0,
430
+					Value: syscall.AF_NETLINK,
431
+					Op:    types.OpEqualTo,
432
+				},
433
+			},
434
+		},
435
+		{
436
+			Names:  []string{"socket"},
437
+			Action: types.ActAllow,
438
+			Args: []*types.Arg{
439
+				{
440
+					Index: 0,
441
+					Value: syscall.AF_PACKET,
442
+					Op:    types.OpEqualTo,
443
+				},
444
+			},
445
+		},
446
+		// socketcall(1, ...) is equivalent to socket(...) on some architectures eg i386
447
+		{
448
+			Names:  []string{"socketcall"},
449
+			Action: types.ActAllow,
450
+			Args: []*types.Arg{
451
+				{
452
+					Index: 0,
453
+					Value: 1,
454
+					Op:    types.OpGreaterThan,
455
+				},
456
+			},
457
+		},
458
+		{
459
+			Names:  []string{"socketcall"},
460
+			Action: types.ActAllow,
461
+			Args: []*types.Arg{
462
+				{
463
+					Index: 0,
464
+					Value: 1,
465
+					Op:    types.OpEqualTo,
466
+				},
467
+				{
468
+					Index: 1,
469
+					Value: syscall.AF_UNIX,
470
+					Op:    types.OpEqualTo,
471
+				},
472
+			},
473
+		},
474
+		{
475
+			Names:  []string{"socketcall"},
476
+			Action: types.ActAllow,
477
+			Args: []*types.Arg{
478
+				{
479
+					Index: 0,
480
+					Value: 1,
481
+					Op:    types.OpEqualTo,
482
+				},
483
+				{
484
+					Index: 1,
485
+					Value: syscall.AF_INET,
486
+					Op:    types.OpEqualTo,
487
+				},
488
+			},
489
+		},
490
+		{
491
+			Names:  []string{"socketcall"},
492
+			Action: types.ActAllow,
493
+			Args: []*types.Arg{
494
+				{
495
+					Index: 0,
496
+					Value: 1,
497
+					Op:    types.OpEqualTo,
498
+				},
499
+				{
500
+					Index: 1,
501
+					Value: syscall.AF_INET6,
502
+					Op:    types.OpEqualTo,
503
+				},
504
+			},
505
+		},
506
+		{
507
+			Names:  []string{"socketcall"},
508
+			Action: types.ActAllow,
509
+			Args: []*types.Arg{
510
+				{
511
+					Index: 0,
512
+					Value: 1,
513
+					Op:    types.OpEqualTo,
514
+				},
515
+				{
516
+					Index: 1,
517
+					Value: syscall.AF_NETLINK,
518
+					Op:    types.OpEqualTo,
519
+				},
520
+			},
521
+		},
522
+		{
523
+			Names:  []string{"socketcall"},
524
+			Action: types.ActAllow,
525
+			Args: []*types.Arg{
526
+				{
527
+					Index: 0,
528
+					Value: 1,
529
+					Op:    types.OpEqualTo,
530
+				},
531
+				{
532
+					Index: 1,
533
+					Value: syscall.AF_PACKET,
534
+					Op:    types.OpEqualTo,
535
+				},
536
+			},
537
+		},
538
+		{
392 539
 			Names: []string{
393 540
 				"breakpoint",
394 541
 				"cacheflush",