Oracle Cloud Hangout Cafe Season5 #3「Kubernetes のセキュリティ」(2022年3月9日開催)
System Hardening
ここでは、システムの強化という観点から、Kubernetesクラスタを支えるノード(ホスト)とKubernetesのセキュリティについて学びます。ノード(ホスト)のOSにおいて、カーネルのセキュリティモジュールであるAppArmorとseccompを利用してKubernetesのセキュリティを強化することを理解します。
AppArmor
AppArmorはLinuxカーネルのセキュリティモジュールです。プロファイルを作成して読み取り、書き込み、プログラムの実行やファイルシステムのマウントなどシステム機能を制御できます。例えば、コンテナからノード(ホスト)のディレクトリをbind-mountした場合に、ディレクトリ全体ではなく個別のファイルやディレクトリに権限を設定できます。
AppArmorとKubernetesクラスタとのセキュリティを理解するために、AppArmorの制御プロファイルを作成し、Kubernetesクラスタ上のPodからファイルの書き込みができない環境を構築します。
- Kubernetes Version 1.4以上であること
- ノード(ホスト)のLinuxでAppArmor Kernel moduleが有効であること
- ContainerRuntimeがAppArmorをサポートしていること
- 参考: AppArmorを使用してコンテナのリソースへのアクセスを制限する
1. AppArmorの有効確認
最初にノード(ホスト)のOSにログインし、AppArmorが有効であるかを確認します。「Y」と表示されれば有効です。
$ cat /sys/module/apparmor/parameters/enable Y
2. AppArmorプロファイルの作成
次に、AppArmorプロファイルを「/etc/apparmor.d/k8s-apparmor-example-deny-write」として作成します。
$ vim /etc/apparmor.d/k8s-apparmor-example-deny-write #include <tunables/global> profile k8s-apparmor-example-deny-write flags=(attach_disconnected) { #include <abstractions/base> file, # Deny all file writes. deny /** w, }
3. AppArmorプロファイルの登録
apparmor_parserコマンドを実行して、AppArmorプロファイルを作成します。
$ apparmor_parser -q /etc/apparmor.d/k8s-apparmor-example-deny-write
登録できたことを確認します。
$ aa-status | grep k8s-apparmor-example-deny-write k8s-apparmor-example-deny-write /usr/bin/sleep (92896) k8s-apparmor-example-deny-write
以上で、ノード(ホスト)側での作業は完了です。
4. AppArmorプロファイルを定義したマニフェストの作成
登録したAppArmorプロファイルをannotationsに定義したPodのマニフェストを作成します。
apiVersion: v1 kind: Pod metadata: name: hello-apparmor annotations: # Tell Kubernetes to apply the AppArmor profile "k8s-apparmor-example-deny-write". # Note that this is ignored if the Kubernetes node is not running version 1.4 or greater. container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write spec: containers: - name: hello image: busybox command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
5. マニフェストの適用と確認
$ kubectl apply -f hello-apparmor.yaml pod/hello-apparmor created
Pod(hello-apparmor)がRunningであるかを確認します。
$ kubectl get pods NAME READY STATUS RESTARTS AGE hello-apparmor 1/1 Running 0 26s
hello-apparmorに接続し、定義したAppArmorプロファイルで実行されていることを確認します。
$ kubectl exec hello-apparmor -- cat /proc/1/attr/current k8s-apparmor-example-deny-write (enforce)
「k8s-apparmor-example-deny-write(enforce)」と表示されれば問題ありません。
6. ファイル書き込みによる動作確認
hello-apparmorに接続し、touchコマンドを実行してファイル作成を試みます。
$ kubectl exec hello-apparmor -- touch /tmp/test touch: /tmp/file: Permission denied command terminated with exit code 1
Permission deniedとなり、ファイルを作成できません。
seccomp
seccompもLinuxカーネルのセキュリティモジュールです。プロファイルを作成して、コンテナが呼び出せるシステムコール(プロセス)を制御できます。不要なシステムコールを制限することで、カーネルの脆弱性を狙った攻撃を防ぎます。
例えば、chmodに関連するシステムコールを拒否するプロファイルを作成して、「$ docker run」コマンド実行時にオプションとしてプロファイル名を指定します。起動したコンテナ内でchmodコマンドを実行するとエラーとなり、権限設定ができません。
seccompとKubernetesクラスタとのセキュリティを理解するために、seccompの制御プロファイルを作成し、Kubernetesクラスタ上のPodからmkdirコマンドを実行してもディレクトリを作成できない環境を構築します。
1. seccompの有効確認
最初にノード(ホスト)のOSにログインし、seccompが有効であるかを確認します。「CONFIG_SECCOMP=y」と表示されれば有効です。
$ grep CONFIG_SECCOMP= /boot/config-$(uname -r) CONFIG_SECCOMP=y
2. seccompプロファイルの作成
次に、専用ディレクトリを作成してからプロファイルを作成します。
$ mkdir -p /var/lib/kubelet/seccomp/profiles $ vim /var/lib/kubelet/seccomp/profiles/prohibit-mkdir.json
{ "defaultAction": "SCMP_ACT_ALLOW", "syscalls": [ { "names": ["mkdir","mkdirat"], "action": "SCMP_ACT_ERRNO" } ] }
以上で、ノード(ホスト)側での作業は完了です。
3. seccompプロファイルを定義したマニフェストの作成と適用
作成したseccompプロファイルを定義したPodのマニフェストを作成します。
apiVersion: v1 apiVersion: v1 kind: Pod metadata: name: prohibit-mkdir spec: securityContext: seccompProfile: type: Localhost localhostProfile: profiles/prohibit-mkdir.json containers: - name: ubuntu image: ubuntu:21.10 command: - sleep - infinity
「spec.securityContext.seccompProfile」では、以下を定義できます。
- localhostProfile:ノード(ホスト)上の「/var/lib/kubelet/seccomp」ディレクトリ内のファイルをProfileとして指定
※Kubernetes 1.19以前はannotationで指定、1.19以降はsecurityContextで指定 - RuntimeDefault:CRIランタイムのデフォルトプロファイル指定
- unconfined:seccomp不使用(デフォルト)
seccompにおいて、ホワイトリストでプロファイルをカスタマイズして作成することはセキュリティを高められますが、運用の煩雑さにつながるところもあります。場合によっては、ある程度コンテナアプリケーションで利用しないsyscallを制限したProfile(RuntimeDefault)の利用を検討する余地もあります。
マニフェストを適用します。
$ kubectl apply -f prohibit-mkdir.yaml pod/prohibit-mkdir created
Pod(prohibit-mkdir)がRunningであるかを確認します。
$ kubectl get pods NAME READY STATUS RESTARTS AGE prohibit-mkdir 1/1 Running 0 16s
4. 動作確認
prohibit-mkdirに接続して、ディレクトリ作成ができないことを確認します。
$ kubectl exec -it prohibit-mkdir -- mkdir test mkdir: cannot create directory 'test': Operation not permitted command terminated with exit code 1
Kubernetes v1.25からSeccompDefaultという機能が追加され、Podのspecでprofileを指定しなかった場合にRuntimeDefaultでコンテナを動かすことができます。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- 「Inspektor Gadget」でKubernetesクラスタをデバッグする
- Kubernetesの基礎
- 認定Kubernetesアプリケーション開発者を目指そう!
- Oracle Cloud Hangout Cafe Season7 #1「Kubnernetes 超入門」(2023年6月7日開催)
- NGINX Ingress Controllerの柔軟なアプリケーション制御、具体的なユースケースと設定方法を理解する
- Kubernetes環境を構築して、実際にコンテナを動かしてみよう
- KubernetesのマニフェストをMagnumで実行する
- Oracle Cloud Hangout Cafe Season 4 #2「Kubernetesのネットワーク」(2021年5月12日開催)
- Kubernetes上のアプリケーション開発を加速させるツール(2) Telepresence
- kustomizeで復数環境のマニフェストファイルを簡単整理