seccompとInspektor Gadgetで目指すセキュアなKubernetes

ZOZOでSREをしている @calorie です。よろしくお願いします。

# この記事について

seccompとInspektor Gadgetを使いPodのシェルへのアクセスを制限する様子を紹介します。

# seccomp

[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html)はシステムコールを制限するためのカーネルの機能です。以下のスライドが詳しいです。

https://speakerdeck.com/mrtc0/introduction-to-seccomp

Kubernetesではノードにseccomp profileをロードし、Podから参照することで使えます。

https://kubernetes.io/docs/tutorials/security/seccomp/

seccomp profileを書いてシステムコールを制限できればセキュアなPodが手に入りますが、
人の手でシステムコールを取捨選択するのは大抵の場合難しいはずです。

なので、seccomp profileを自動生成したいです。
有名なものであれば[docker-slim](https://github.com/docker-slim/docker-slim)が挙げられますが、ドキュメントを見る限りはイメージからseccomp profileを生成しているので、実際にリクエストを流しシステムコールをトレースした上で生成できるInspektor Gadgetを紹介します。

# Inspektor Gadget

https://www.inspektor-gadget.io/

Inspektor Gadgetは[eBPF](https://ebpf.io/what-is-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` を使ってデバッグする様子が発表されました。

https://www.youtube.com/watch?v=6s109Uwr608

今回は `kubectl gadget advise` を使いseccomp profileを生成しPodのシェルへのアクセスを制限する様子を紹介します。

# seccompとInspektor Gadgetによるシステムコールの制限

以下のドキュメントに沿って実際にローカルで挙動を確認します。

https://www.inspektor-gadget.io/docs/latest/guides/advise/seccomp-profile/#integration-with-kubernetes-security-profiles-operator

## 前準備

`kubectl gadget advise` がArm64のMacで動かないためx86_64のMacにMinikubeを立てて実行します。今後に期待。

```sh
$ minikube start --driver=hyperkit -p=play-ground
```

Inspektor Gadgetをインストール、デプロイします。

```sh
$ kubectl krew install gadget
$ kubectl gadget deploy
```

https://www.inspektor-gadget.io/docs/latest/install/

```sh
$ kubectl get pods -n gadget
NAME           READY   STATUS    RESTARTS   AGE
gadget-h65xz   1/1     Running   0          22h
```

[traceloop](https://github.com/kinvolk/traceloop)などを実行するPodが立ち上がったことが確認できます。
Inspektor Gadgetの詳しいアーキテクチャについてはドキュメントから参照できます。

https://www.inspektor-gadget.io/docs/latest/architecture/

次にKubernetes Security Profiles Operatorをインストールします。

https://github.com/kubernetes-sigs/security-profiles-operator/blob/main/installation-usage.md#install-operator

デモで使うpythonアプリなどをapplyします。YAMLは[リポジトリのもの](https://github.com/inspektor-gadget/inspektor-gadget/tree/v0.11.0/docs/examples/seccomp)を参照してください。

```sh
$ kubectl apply -f docs/examples/seccomp/basic.yaml
```

## トレース

`kubectl gadget advise seccomp-profile start` でトレースを開始します。

```sh
$ kubectl gadget advise seccomp-profile start -m seccomp-profile -n seccomp-demo -p hello-python
TAyR9BXes6GU04rG
```

pythonアプリのPodを立ててリクエストを流します。

```sh
$ 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` です。

```sh
$ kubectl gadget advise seccomp-profile stop TAyR9BXes6GU04rG
$ kubectl get seccompprofile -n gadget
NAME            STATUS      AGE
hello-python                9s
```

しかし、minikubeではうまく生成できず[issue](https://github.com/kubernetes/minikube/issues/5573)もあるため、今回は直接ノードにsshし生成されたseccomp profileを書き込みました。

```sh
$ minikube ssh -p play-ground
$ sudo vi /var/lib/kubelet/seccomp/hello-python.json
```

```json: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によって弾かれることが確認できます。

```sh
$ 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` にするとシステムコールは実行しつつログに吐き出すようにもできます。

https://kubernetes.io/docs/tutorials/security/seccomp/#create-a-pod-with-a-seccomp-profile-for-syscall-auditing

# おわりに

seccompとInspektor GadgetでPodから実行されるシステムコールを制限する様子を紹介しました。
次はBPFの理解を深めるため880ページあるこちらの本を読みたいと思います。

https://www.amazon.co.jp/dp/B081ZDXNL3