AppArmorをKubernetes上で使う

Tetsuya Isogai
6 min readDec 12, 2020

AppArmorを使ってコンテナが実行するシステムコールを制限する

What is AppArmor

AppArmorはLinuxカーネルセキュリティモジュールであり、標準のアクセス許可(Linuxユーザおよびグループベースのアクセス許可)に加えてプログラムのアクセスを限られたリソースに制限するための機能。カーネルが管理するリソース(ファイルシステム、プロセス間通信、メモリ等の記憶領域、ネットワーク、デバイスドライバ等)を特定のプログラムまたはコンテナに対してプロファイルを通じてアクセス許可の制御を行う。

プロファイルのモードは Enforcing (許可されていないリソースへのアクセスをブロックする)、 Complain (許可されていないリソースへのアクセスの報告)のどちらかで動作させることができる。

Why Apparmor in Kubernetes

AppArmorを使用することで、コンテナに許可する動作(システムコール)をより細かく制御し、また必要に応じて監査できるようにすることでKubernetesクラスタをよりセキュアに保つことができる。
(注:当然ながらAppArmorは銀の弾丸ではなくDefense in Depthの考えの基でセキュリティを確保することが重要)

ApparmorポリシーをLinuxサーバ上で使う:サマリ

  1. サーバ上でAppArmorをインストールする
  2. プロファイルを作成する(後述A)
  3. プロファイルをロードする(後述B)
  4. (補足)ロードされている状態を確認する

A: プロファイルの作成

プロファイルはデフォルトで用意されているものに加えて自分で作成することもできる。 aa-genprof <プログラム名> とすることで大まかなプロファイルを作成することができ、作成されたプロファイルは /etc/apparmor.d/ ディレクトリの下に配置される。ほとんどの場合genprofした状態ではプログラムが正しく動作しないため、 aa-logprofを使って必要なシステムコールのみ許可するようチューニングする。
詳しくは以下を参照。
https://www.belbel.or.jp/opensuse-manuals_ja/cha-apparmor-commandline.html

B: プロファイルのロード

作成したプロファイルは専用のコマンド(apparmor_parser)でサーバ上にロード(カーネルへAppArmorの定義を入れる)する。

C: ロードされているApparmorポリシーの確認

aa-statusコマンドで現在の状態(ロードされているプロファイル、プロファイルを使っているプロセスなど)を確認できる。

$ sudo aa-status
apparmor module is loaded.
25 profiles are loaded.
25 profiles are in enforce mode.
/sbin/dhclient
...
0 profiles are in complain mode.
66 processes have profiles defined.
66 processes are in enforce mode.
cri-containerd.apparmor.d (2541)
...
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.

AppArmorをKubernetes上で使う

AppArmorをKubernetesクラスタ上で使うためには前述のLinuxサーバ上で使うための準備に加えて以下を行う。

  1. 全てのワーカーノードでAppArmorが有効であることを確認する。
kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {.status.conditions[?(@.reason=="KubeletReady")].message}\n{end}'

2. 作成したプロファイルを全てのワーカーノードでロードする。

sudo apparmor_parser <profile-path>

3. コンテナに対してAppArmorプロファイルを適用させる。(Podマニフェストのアノテーションで指定する)
container.apparmor.security.beta.kubernetes.io/<container_name>: <profile_ref>

apiVersion: v1
kind: Pod
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-allow-write
...

この指定方法の通り、プロファイルはコンテナ単位で指定する。

<profile_ref>の指定については、自分が作成するなどしたカスタムプロファイルの場合は localhost/<profile-name> 、ランタイムのデフォルトプロファイルを使う場合は runtime/default を指定する。<profile_name>aa-status で表示されるプロファイル名を指定する。

ロードされているプロファイルを使う限りにおいてはKubernetes的には上記のannotaionを追記するだけでコンテナにAppArmorプロファイルを適用することができる。

適用後の確認

コンテナにAppArmorプロファイルが正しく適用されたかはcat /proc/1/attr/current をコンテナ内で実行すると確認ができる。

$ k exec apparomr-test --  cat /proc/1/attr/current
docker-nginx (enforce)

プロファイルが存在しない場合など、正しく適用できない場合はPodのステータスがBlockedになる。

$ k get pods
NAME READY STATUS RESTARTS AGE
apparomr-test-noprof 0/1 Blocked 0 2s

補足:User Space(ユーザ空間)とKernel Space(カーネル空間)

ユーザ空間はLinux OS上でアプリケーションが使用する計算リソースであり、カーネル空間はカーネルが使用する計算リソース。両社は明確に分離されており、カーネル空間にはユーザプロセスが直接アクセスすることができない。そのためユーザプロセスはシステムコールを通じてカーネル空間にアクセスをする。時にはユーザプログラムは直接的に、時にはライブラリを通じてシステムコールを呼び出す。

--

--

Tetsuya Isogai

Working at Microsoft/Cloud Solution Architect/Azure Core Infra