seccomp (Secure computing mode) はプロセスに対してシステムコールを制限する Linux kernel の機能で,今回は「Docker と seccomp」を組み合わせて試す.ドキュメントは以下にある.
seccomp デフォルトプロファイル
まず,Docker はデフォルトで seccomp をサポートしている.Docker (Moby) の GitHub リポジトリにデフォルトプロファイル default.json
がある.プロファイルは長く感じるけど,比較的簡単に読める.基本的に SCMP_ACT_ERRNO(拒否)
となり,ホワイトリストとして列挙したシステムコールを SCMP_ACT_ALLOW(許可)
としている.
基本的には制限しすぎず,ホスト側に影響を及ぼすシステムコールなどは禁止されている.ドキュメントにも「It is moderately protective while providing wide application compatibility(幅広いアプリケーション互換性を提供しながら適度に保護する)」と書いてある.
--security-opt
オプション
docker run
コマンドには --security-opt
オプションがあり,--security-opt="seccomp=profile.json"
のように seccomp プロファイルを指定できる.また unconfined
を指定するとデフォルトプロファイルを無効化できる.
--security-opt="seccomp=unconfined" Turn off seccomp confinement for the container --security-opt="seccomp=profile.json" White-listed syscalls seccomp Json file to be used as a seccomp filter
検証 : mkdir
システムコールを制限する
単純な例として,Ubuntu で mkdir
システムコールを制限して,mkdir
コマンドの挙動を確認する.デフォルトプロファイルでは mkdir
システムコールを許可しているため,デフォルトでは以下のように問題なく使える.
$ docker run --rm -d -it ubuntu:21.10 ad3a814faf29ce6407c44723f8a3330c46a425259507dcd4e7673cbaf0f9b2dd $ docker exec -it ad3a814faf29 /bin/sh # mkdir sample-dir
次に seccomp を試す.まず,GitHub から default.json
を取得して,mkdir
のエントリーを削除した deny-mkdir.json
を作る.そして docker run
コマンドに --security-opt seccomp=deny-mkdir.json
オプションを指定すると,mkdir
コマンドを実行できなくなった.実際に Operation not permitted
とエラーになった.
$ wget https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json $ diff -u default.json deny-mkdir.json @@ -200,7 +200,6 @@ "membarrier", "memfd_create", "mincore", - "mkdir", "mkdirat", "mknod", "mknodat", $ docker run --rm -d -it --security-opt seccomp=deny-mkdir.json ubuntu:21.10 4e24d4448c43169ef15a928080a6d82fc7732ee3b782234dfb9b00b0564689c8 $ docker exec -it 4e24d4448c43 /bin/sh # mkdir sample-dir mkdir: cannot create directory 'sample-dir': Operation not permitted
検証 : strace
システムコールを制限する
関連する例として,今度は Ubuntu で ptrace
システムコールを制限して,strace
コマンドの挙動を確認する.デフォルトプロファイルでは,以下のように Linux Kernel 4.8 で ptrace
を許可している.
{ "names": [ "process_vm_readv", "process_vm_writev", "ptrace" ], "action": "SCMP_ACT_ALLOW", "args": null, "comment": "", "includes": { "minKernel": "4.8" }, "excludes": {} },
よって,デフォルトでは以下のように strace
コマンドを問題なく使える.
$ docker run --rm -d -it ubuntu:21.10 14c033b88ab4fc361561c37d271dc9d9a3cb98833d570c7c5e86e8cd94db2281 $ docker exec -it 14c033b88ab4 /bin/sh # uname -r 5.10.25-linuxkit # apt-get -y update # apt-get -y install strace # strace ls execve("/usr/bin/ls", ["ls"], 0x7ffc3ae41460 /* 5 vars */) = 0 brk(NULL) = 0x561158e2e000 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd05d87490) = -1 EINVAL (Invalid argument) access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=5826, ...}, AT_EMPTY_PATH) = 0 mmap(NULL, 5826, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe3dd13d000 close(3) (中略) # strace -cw ls bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 17.22 0.002433 2432 1 munmap 16.71 0.002361 90 26 mmap 12.49 0.001765 176 10 close 8.47 0.001197 132 9 newfstatat 6.58 0.000930 116 8 openat 6.29 0.000889 127 7 read 4.33 0.000612 101 6 pread64 4.19 0.000592 84 7 mprotect 3.72 0.000526 262 2 getdents64 2.99 0.000423 423 1 execve 2.10 0.000297 297 1 set_tid_address 2.08 0.000294 146 2 ioctl 1.99 0.000281 280 1 set_robust_list 1.87 0.000264 132 2 rt_sigaction 1.78 0.000252 126 2 2 statfs 1.72 0.000243 81 3 brk 1.36 0.000193 192 1 write 1.32 0.000186 92 2 1 arch_prctl 1.22 0.000172 86 2 2 access 0.81 0.000114 113 1 prlimit64 0.78 0.000110 109 1 rt_sigprocmask ------ ----------- ----------- --------- --------- ---------------- 100.00 0.014131 148 95 5 total
次に ptrace
のエントリーを削除した deny-ptrace.json
を作る.そして docker run
コマンドに --security-opt seccomp=deny-ptrace.json
オプションを指定すると,strace
コマンドを実行できなくなった.
$ diff -u default.json deny-ptrace.json @@ -402,8 +402,7 @@ { "names": [ "process_vm_readv", - "process_vm_writev", - "ptrace" + "process_vm_writev" ], "action": "SCMP_ACT_ALLOW", "args": null, $ docker run --rm -d -it --security-opt seccomp=deny-ptrace.json ubuntu:21.10 b78b00e8ce7b02b7635a19478fa089caeff1ebc85446ea4013e07696331ca9c6 $ docker exec -it b78b00e8ce7b /bin/sh # uname -r 5.10.25-linuxkit # apt-get -y update # apt-get -y install strace # strace ls strace: test_ptrace_get_syscall_info: PTRACE_TRACEME: Operation not permitted strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted strace: PTRACE_SETOPTIONS: Operation not permitted strace: detach: waitpid(262): No child processes strace: Process 262 detached # strace -cw ls strace: test_ptrace_get_syscall_info: PTRACE_TRACEME: Operation not permitted strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted strace: PTRACE_SETOPTIONS: Operation not permitted strace: detach: waitpid(266): No child processes strace: Process 266 detached
seccomp と DockerSlim (docker-slim)
実際にプロファイルを作るときに必要最低限なシステムコールを洗い出すのは難しく,strace
コマンドを使うこともできるけど,「DockerSlim (docker-slim)」を使うとお手軽にプロファイルを作ることができる.
「Docker/Kubernetes開発・運用のためのセキュリティ実践ガイド」を読んでいたら載っていた.「DockerSlim」は今度試す予定!
参考資料 : Introduction to Seccomp
seccomp を詳しく解説したスライドがあってとてもわかりやすかった💡後半は僕には難しかったけど...!
まとめ
コンテナワークロードのセキュリティ対策として,今回は「Docker と seccomp」を組み合わせて試した.プロファイルを編集してシステムコールを制限することができた.次は「Kubernetes と seccomp」を組み合わせて試すぞー!