fbpx

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を有効にすることで、万が一コンテナを乗っ取られた場合でも危険なシステムコールが実行できなくなり、安全性の向上につながります。
セキュアなコンテナが必要な場合は是非おためしください。

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA