ZOZOでSREをしている @calorie です。よろしくお願いします。
この記事について
seccompとInspektor Gadgetを使いPodのシェルへのアクセスを制限する様子を紹介します。
seccomp
seccompはシステムコールを制限するためのカーネルの機能です。以下のスライドが詳しいです。
Kubernetesではノードにseccomp profileをロードし、Podから参照することで使えます。
seccomp profileを書いてシステムコールを制限できればセキュアなPodが手に入りますが、
人の手でシステムコールを取捨選択するのは大抵の場合難しいはずです。
なので、seccomp profileを自動生成したいです。
有名なものであればdocker-slimが挙げられますが、ドキュメントを見る限りはイメージからseccomp profileを生成しているので、実際にリクエストを流しシステムコールをトレースした上で生成できるInspektor Gadgetを紹介します。
Inspektor Gadget
Inspektor GadgetはeBPFをベースとしたKubernetesのリソースをデバッグするためのツール群です。
kubectlを拡張するpluginとして提供されるため手軽に扱うことができます。
以下のような機能が含まれます。
- network policyやseccomp profileを生成する
kubectl gadget advise
- kubectl topではモニタリングできないカーネルレベルのBPFプログラムのリソース使用状況を表示する
kubectl gadget top ebpf
- tcpdumpなしでTCPをトレースする
kubectl gadget trace tcp
など他にも多くの機能があります。
最近のKubeConのサイドイベントでは kubectl gadget trace tcp
を使ってデバッグする様子が発表されました。
今回は kubectl gadget advise
を使いseccomp profileを生成しPodのシェルへのアクセスを制限する様子を紹介します。
seccompとInspektor Gadgetによるシステムコールの制限
以下のドキュメントに沿って実際にローカルで挙動を確認します。
前準備
kubectl gadget advise
がArm64のMacで動かないためx86_64のMacにMinikubeを立てて実行します。今後に期待。
$ minikube start --driver=hyperkit -p=play-ground
Inspektor Gadgetをインストール、デプロイします。
$ kubectl krew install gadget
$ kubectl gadget deploy
$ kubectl get pods -n gadget
NAME READY STATUS RESTARTS AGE
gadget-h65xz 1/1 Running 0 22h
traceloopなどを実行するPodが立ち上がったことが確認できます。
Inspektor Gadgetの詳しいアーキテクチャについてはドキュメントから参照できます。
次にKubernetes Security Profiles Operatorをインストールします。
デモで使うpythonアプリなどをapplyします。YAMLはリポジトリのものを参照してください。
$ kubectl apply -f docs/examples/seccomp/basic.yaml
トレース
kubectl gadget advise seccomp-profile start
でトレースを開始します。
$ kubectl gadget advise seccomp-profile start -m seccomp-profile -n seccomp-demo -p hello-python
TAyR9BXes6GU04rG
pythonアプリのPodを立ててリクエストを流します。
$ kubectl apply -f docs/examples/seccomp/unconfined.yaml
$ kubectl port-forward service/hello-python-service -n seccomp-demo 8080:6000 &
$ curl localhost:8080
$ kill %1
seccomp profileの生成
トレースを止めるとseccomp profileがノードの /var/lib/kubelet/seccomp/operator/{namespace}/{pod_name}.json
に生成されます。今回の例では /var/lib/kubelet/seccomp/operator/gadget/hello-python.json
です。
$ kubectl gadget advise seccomp-profile stop TAyR9BXes6GU04rG
$ kubectl get seccompprofile -n gadget
NAME STATUS AGE
hello-python 9s
しかし、minikubeではうまく生成できずissueもあるため、今回は直接ノードにsshし生成されたseccomp profileを書き込みました。
$ minikube ssh -p play-ground
$ sudo vi /var/lib/kubelet/seccomp/hello-python.json
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"accept4",
"access",
"arch_prctl",
"bind",
"brk",
"capget",
"capset",
"chdir",
"chmod",
"chown",
"clock_gettime",
"clone",
"close",
"connect",
"dup",
"dup2",
"epoll_create",
"epoll_create1",
"epoll_ctl",
"epoll_pwait",
"epoll_wait",
"eventfd2",
"execve",
"exit_group",
"fcntl",
"fstat",
"fstatfs",
"futex",
"getcwd",
"getdents64",
"getegid",
"geteuid",
"getgid",
"getpid",
"getppid",
"getrandom",
"getsockname",
"getsockopt",
"gettid",
"gettimeofday",
"getuid",
"io_setup",
"ioctl",
"link",
"listen",
"lseek",
"lstat",
"mkdir",
"mmap",
"mprotect",
"munmap",
"nanosleep",
"newfstatat",
"openat",
"pipe2",
"poll",
"prctl",
"pread64",
"prlimit64",
"pwrite64",
"read",
"readlink",
"readv",
"recvfrom",
"rename",
"rt_sigaction",
"rt_sigprocmask",
"rt_sigreturn",
"rt_sigsuspend",
"set_robust_list",
"set_tid_address",
"setgid",
"setgroups",
"setpgid",
"setsockopt",
"setuid",
"socket",
"socketpair",
"stat",
"sysinfo",
"tgkill",
"uname",
"unlink",
"vfork",
"wait4",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
docs/examples/seccomp/confined.yaml
の localhostProfile
を hello-python.json
に変更しapplyした後bashでログインしようとすると、seccompによって弾かれることが確認できます。
$ kubectl apply -f docs/examples/seccomp/confined.yaml
$ kubectl exec -it -n seccomp-demo hello-python -- /bin/bash
bash: initialize_job_control: getpgrp failed: Success
command terminated with exit code 1
上記の hello-python.json
において SCMP_ACT_ERRNO
を SCMP_ACT_LOG
にするとシステムコールは実行しつつログに吐き出すようにもできます。
おわりに
seccompとInspektor GadgetでPodから実行されるシステムコールを制限する様子を紹介しました。
次はBPFの理解を深めるため880ページあるこちらの本を読みたいと思います。