k0sでSeccompを使ってみよう #k0s #mirantis #kubernetes #k8s #seccomp
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
k0sとは、軽量かつ使いやすい、100%オープンソースのKubernetesディストリビューションです。主な特徴としては、
- フル機能のKubernetesを構築するために必要なすべてを単一バイナリに同梱
- k0s特有の改変を加えていない、CNCF認定の純正なKubernetesをデプロイ
- 最低必要リソース1vCPU・1GBメモリ・2GBストレージのシングルノードから、HA構成の大規模クラスタまでサポート
- Konnectivityをデフォルトで有効化
などが挙げられます。より詳しい情報は公式ドキュメントをご覧ください。
本稿ではこのk0sで、「Dockerが採用するセキュリティ機構「Seccomp」とは何か?」で触れたSeccompを有効にしてみます。
Seccompとは
Seccompとは、Linuxカーネルが持つセキュリティ機構の一つで、Secure Computing Modeの略です。簡単に言うと、Seccompはシステムコールの許可・不許可を設定できるようにし、危険なシステムコールを実行できなくするためのものです。
詳しくは「Dockerが採用するセキュリティ機構「Seccomp」とは何か?」をご覧ください。
containerdにおけるSeccomp
containerd (およびrunc)もSeccompに対応しており、Dockerの場合と同じように利用することができます。k0sでのSeccompを見てみる前に、containerdの場合を見てみましょう。containerdのバージョンは1.3.9です。
[vagrant@master ~]$ sudo ctr version Client: Version: 1.3.9 Revision: ea765aba0d05254012b0b9e595e995c09186427f Server: Version: 1.3.9 Revision: ea765aba0d05254012b0b9e595e995c09186427f UUID: db4bdc57-8218-47ed-8d3d-4fa57f323382
まずstraceコマンドを実行できるsjourdan/straceイメージを取得します。
[vagrant@master ~]$ sudo ctr image pull docker.io/sjourdan/strace:latest (略)
ctr runコマンドに特にオプションを付与せずに実行するとSeccompは無効なので、straceコマンドは正常に実行できます。
[vagrant@master ~]$ sudo ctr run -t --rm docker.io/sjourdan/strace:latest strace /bin/sh / # strace ls -l execve("/bin/ls", ["ls", "-l"], [/* 5 vars <em>/]) = 0 arch_prctl(ARCH_SET_FS, 0x7f28dfe16b48) = 0 set_tid_address(0x7f28dfe16b80) = 9 mprotect(0x7f28dfe13000, 4096, PROT_READ) = 0 mprotect(0x560dc8877000, 16384, PROT_READ) = 0 getuid() = 0 ioctl(0, TIOCGWINSZ, {ws_row=24, ws_col=80, ws_xpixel=0, ws_ypixel=0}) = 0 ioctl(1, TIOCGWINSZ, {ws_row=24, ws_col=80, ws_xpixel=0, ws_ypixel=0}) = 0 lstat(".", {st_mode=S_IFDIR|0755, st_size=18, ...}) = 0 open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 3 fcntl(3, F_SETFD, FD_CLOEXEC) = 0 getdents64(3, /</em> 18 entries */, 2048) = 440 lstat("./bin", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("./dev", {st_mode=S_IFDIR|0755, st_size=360, ...}) = 0 lstat("./etc", {st_mode=S_IFDIR|0755, st_size=17, ...}) = 0 (略)
ctr runコマンドに「--seccomp」オプションを付与してコンテナを起動すると、Seccompが有効になります。この状態でstraceコマンドを実行してみましょう。
[vagrant@master ~]$ sudo ctr run -t --rm --seccomp docker.io/sjourdan/strace:latest strace /bin/sh / # strace ls -l strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted +++ exited with 1 +++
想定通り、straceコマンドの実行に失敗しました。Dockerと同じくcontainerdも内蔵のSeccompプロファイルを持っており、デフォルトではptraceシステムコールが禁止されているからです。詳細はこちらをご覧ください。
k0sでのSeccomp
k0sでKubernetesをインストールすると、利用するコンテナランタイムとしてcontainerdも一緒にインストールされます。そしてKubernetesもSeccompに対応しているので、k0sではすぐにSeccompを使うことができます。
Virtualbox/Vagrantで用意した2ノードの仮想マシンにk0sをインストールして確認してみましょう。実際のインストール手順はここでは省略しますので「k0sでKubernetesをVirtualbox/Vagrantにインストールしてみよう」を参照してください。
k0sでKubernetesがインストールできたとして、次のマニフェスト「nginx-with-seccomp.yaml」を準備します。
apiVersion: v1 kind: Pod metadata: name: nginx spec: securityContext: seccompProfile: type: RuntimeDefault containers: - name: nginx image: nginx:1.20.2
この「sepc.securityContext.seccompProfile.type: RuntimeDefault」に注目してください。これはコンテナランタイムのデフォルトのSeccompプロファイルを適用するという意味です。
Podをデプロイし、straceコマンドが実行できるか見てみます。
vagrant@master:~$ k0s kubectl apply -f nginx-with-seccomp.yaml pod/nginx created vagrant@master:~$ k0s kubectl describe pod nginx Name: nginx Namespace: default Priority: 0 Node: node01/192.168.123.201 Start Time: Tue, 30 Nov 2021 02:52:29 +0000 Labels: Annotations: kubernetes.io/psp: 00-k0s-privileged seccomp.security.alpha.kubernetes.io/pod: runtime/default Status: Running (略) vagrant@master:~$ k0s kubectl exec -it nginx -- bash root@nginx:/# apt update && apt install -y strace (略) root@nginx:/# strace ls -l 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(330): No child processes strace: Process 330 detached
先のcontainerdでの例で見たように、containerdのデフォルトプロファイルではptraceシステムコールが禁止されているため、想定通りにstraceコマンドが実行できませんでした。
デフォルト以外のSeccompプロファイルを利用したい場合はどうすればよいでしょうか? まずはKubeletが動作している全ワーカーノードの指定の場所にプロファイルを設置する必要があります。今回使用した k0s-v1.22.4+k0s.0-amd64 では /var/lib/kubelet/seccomp/ ディレクトリになります。
Docker組み込みのデフォルトプロファイルをダウンロードし、statxシステムコールを許可リストから削除し、/var/lib/kubelet/seccomp/ ディレクトリに no-statx.json として保存します。
vagrant@node01:~$ wget https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json vagrant@node01:~$ sudo mkdir /var/lib/kubelet/seccomp vagrant@node01:~$ sed '/statx/d' default.json | sudo tee /var/lib/kubelet/seccomp/no-statx.json
vagrant@node01:~$ diff -u default.json /var/lib/kubelet/seccomp/no-statx.json --- default.json 2021-11-30 03:54:49.153299783 +0000 +++ /var/lib/kubelet/seccomp/no-statx.json 2021-11-30 03:56:26.385299783 +0000 @@ -351,7 +351,6 @@ "stat64", "statfs", "statfs64", - "statx", "symlink", "symlinkat", "sync", vagrant@node01:~$
このプロファイルを利用するマニフェストは次のようになります。
apiVersion: v1 kind: Pod metadata: name: nginx spec: securityContext: seccompProfile: type: Localhost localhostProfile: no-statx.json containers: - name: nginx image: nginx:1.20.2
「sepc.securityContext.seccompProfile.type: Localhost」と「sepc.securityContext.seccompProfile.localhostProfile: no-statx.json」に注目してください。これはワーカーノード上の指定されたSeccompプロファイルを適用するという意味になります。
Podをデプロイし、statxシステムコールを利用するlsコマンドが実行できるか見てみます。
vagrant@master:~$ k0s kubectl apply -f nginx-with-no-statx.yaml pod/nginx created vagrant@master:~$ k0s kubectl describe pod nginx Name: nginx Namespace: default Priority: 0 Node: node01/192.168.123.201 Start Time: Tue, 30 Nov 2021 03:57:59 +0000 Labels: Annotations: kubernetes.io/psp: 00-k0s-privileged seccomp.security.alpha.kubernetes.io/pod: localhost/no-statx.json Status: Running (略) vagrant@master:~$ k0s kubectl exec -it nginx -- bash root@nginx:/# ls -l ls: cannot access 'tmp': Operation not permitted ls: cannot access 'lib64': Operation not permitted ls: cannot access 'media': Operation not permitted ls: cannot access 'etc': Operation not permitted ls: cannot access 'root': Operation not permitted ls: cannot access 'opt': Operation not permitted ls: cannot access 'srv': Operation not permitted ls: cannot access 'bin': Operation not permitted ls: cannot access 'dev': Operation not permitted ls: cannot access 'sys': Operation not permitted ls: cannot access 'mnt': Operation not permitted ls: cannot access 'run': Operation not permitted ls: cannot access 'lib': Operation not permitted ls: cannot access 'proc': Operation not permitted ls: cannot access 'home': Operation not permitted ls: cannot access 'usr': Operation not permitted ls: cannot access 'sbin': Operation not permitted ls: cannot access 'boot': Operation not permitted ls: cannot access 'var': Operation not permitted ls: cannot access 'docker-entrypoint.d': Operation not permitted ls: cannot access 'docker-entrypoint.sh': Operation not permitted total 0 d????????? ? ? ? ? ? bin d????????? ? ? ? ? ? boot d????????? ? ? ? ? ? dev d????????? ? ? ? ? ? docker-entrypoint.d -????????? ? ? ? ? ? docker-entrypoint.sh d????????? ? ? ? ? ? etc d????????? ? ? ? ? ? home d????????? ? ? ? ? ? lib d????????? ? ? ? ? ? lib64 d????????? ? ? ? ? ? media d????????? ? ? ? ? ? mnt d????????? ? ? ? ? ? opt d????????? ? ? ? ? ? proc d????????? ? ? ? ? ? root d????????? ? ? ? ? ? run d????????? ? ? ? ? ? sbin d????????? ? ? ? ? ? srv d????????? ? ? ? ? ? sys d????????? ? ? ? ? ? tmp d????????? ? ? ? ? ? usr d????????? ? ? ? ? ? var root@nginx:/#
想定通り、statxシステムコールが禁止されたので、ファイルの情報が取得できなくなりました。
念のため確認として、同じSeccompプロファイルを適用して、statxではなくlstatシステムコールを利用するlsコマンドが実行できるかも見てみましょう。
apiVersion: v1 kind: Pod metadata: name: nginx spec: securityContext: seccompProfile: type: Localhost localhostProfile: no-statx.json containers: - name: nginx image: nginx:1.20.1
vagrant@master:~$ k0s kubectl apply -f nginx-old-with-no-statx.yaml pod/nginx created vagrant@master:~$ k0s kubectl exec -it nginx -- bash root@nginx:/# ls -l total 80 drwxr-xr-x 2 root root 4096 Oct 11 00:00 bin drwxr-xr-x 2 root root 4096 Oct 3 09:00 boot drwxr-xr-x 5 root root 360 Nov 30 04:02 dev drwxr-xr-x 1 root root 4096 Oct 12 02:04 docker-entrypoint.d -rwxrwxr-x 1 root root 1202 Oct 12 02:04 docker-entrypoint.sh drwxr-xr-x 1 root root 4096 Nov 30 04:02 etc drwxr-xr-x 2 root root 4096 Oct 3 09:00 home drwxr-xr-x 1 root root 4096 Oct 12 02:04 lib drwxr-xr-x 2 root root 4096 Oct 11 00:00 lib64 drwxr-xr-x 2 root root 4096 Oct 11 00:00 media drwxr-xr-x 2 root root 4096 Oct 11 00:00 mnt drwxr-xr-x 2 root root 4096 Oct 11 00:00 opt dr-xr-xr-x 133 root root 0 Nov 30 04:02 proc drwx------ 2 root root 4096 Oct 11 00:00 root drwxr-xr-x 1 root root 4096 Nov 30 04:02 run drwxr-xr-x 2 root root 4096 Oct 11 00:00 sbin drwxr-xr-x 2 root root 4096 Oct 11 00:00 srv dr-xr-xr-x 13 root root 0 Nov 30 02:48 sys drwxrwxrwt 1 root root 4096 Oct 12 02:04 tmp drwxr-xr-x 1 root root 4096 Oct 11 00:00 usr drwxr-xr-x 1 root root 4096 Oct 11 00:00 var root@nginx:/#
想定通り、正常に動作しました。
まとめ
本稿ではk0sでインストールしたKubernetesに、Seccompを有効にしたPodをデプロイしてみました。
Seccompを有効にすることで、万が一コンテナを乗っ取られた場合でも危険なシステムコールが実行できなくなり、安全性の向上につながります。
セキュアなコンテナが必要な場合は是非おためしください。