ここ数日のAppArmorに関する調査をまとめておく。アドベントカレンダーの季節だけど、特に該当しそうなカレンダーがなかったのでただの記事を書きます...
突っ込みどころはあると思うので、修正指摘等お気軽に。
AppArmor とは
AppArmorはMAC(Mandatory Access Control - 強制アクセス制御)を実現するためのミドルウェアの一つ。同じようにMACを実現するものにはSELinuxなどがあるが、AppArmorはシステム全体というよりはプログラム単位で、ファイル別またはプロセスごとにセキュリティプロファイルを紐づけることができる。
そのプロファイルでは:
- どのファイルにどのようにアクセスできるか/できないか(読み書き実行など)
- ネットワークやRaw Socketなどへのアクセスができるか
以上についてUNIXのファイル権限やKernel Capabilityに関係なく強制的な制限をかけることができる。
UbuntuのCanonical社により開発がされており、Ubuntuではデフォルトで統合されている。その他サポートしているディストロはDebian、Arch Linux、OpenSUSEなど(CentOSは...)。詳細はWikiに。
Dockerと統合して使う
AppArmorが使える環境では、DockerはAppArmorのプロファイルをシステムにインストールし、デフォルトで立ち上げるコンテナでは必ず使うようにしている。
なおこれから以下の手順は、Ubuntu Xenial/Docker 1.12.1/AppArmor 2.10.95(-0ubuntu2.5) で検証している。
デフォルトのプロファイル
まず、適当なコンテナを立ち上げてみる。
vagrant@localhost:~$ docker create centos:7 9a134788d4f052706faeeb0e23f9212d31597931288c960d52500dc9d0dc7d07 vagrant@localhost:~$ docker run --rm -ti centos:7 /bin/bash [root@811ac6c528e7 /]# # ...inside the container
別のターミナルから aa-status
コマンドを実行すると、 docker-default
の適用されたプロセスが存在することがわかる。
vagrant@localhost:~$ sudo aa-status apparmor module is loaded. 13 profiles are loaded. 13 profiles are in enforce mode. /sbin/dhclient /usr/bin/lxc-start /usr/lib/NetworkManager/nm-dhcp-client.action /usr/lib/NetworkManager/nm-dhcp-helper /usr/lib/connman/scripts/dhclient-script /usr/sbin/ntpd /usr/sbin/tcpdump docker-bash-test docker-default lxc-container-default lxc-container-default-cgns lxc-container-default-with-mounting lxc-container-default-with-nesting 0 profiles are in complain mode. 2 processes have profiles defined. 2 processes are in enforce mode. /sbin/dhclient (7038) docker-default (9284) 0 processes are in complain mode. 0 processes are unconfined but have a profile defined.
コンテナをたくさん立ち上げると、それだけ docker-default
が当たっているプロセスも増えることがわかる。
6 processes are in enforce mode. /sbin/dhclient (7038) docker-default (9429) docker-default (9517) docker-default (9608) docker-default (9694) docker-default (9785) ...
ちなみにAppArmorのプロファイルには2種類のモードがあり、一つはenforce modeで、実際にプロファイルの内容でアクセス制限がかかる。もう一つのcomplain modeは、実際のプログラムの挙動の学習のために使われるモードで、制限は掛けないがどのようなファイルアクセスなどがあったか記録してくれるそうだ。詳細は こちらの記事などに例がある 。
カスタムプロファイルを試す
さて、ここから、プロファイルを自分でカスタマイズしてみる。
/etc/apparmor.d/containers/docker-profile-test
という名前で以下のようなファイルを作成した。
#include <tunables/global> profile docker-profile-test flags=(attach_disconnected,mediate_deleted) { #include <abstractions/base> network inet tcp, network inet udp, network inet icmp, network, capability, file, umount, /** r, audit /usr/bin/ls mrwklix, deny /usr/bin/top mrwklx, # from Docker default deny @{PROC}/{*,**^[0-9*],sys/kernel/shm*} wkx, deny @{PROC}/sysrq-trigger rwklx, deny @{PROC}/mem rwklx, deny @{PROC}/kmem rwklx, deny @{PROC}/kcore rwklx, deny mount, deny /sys/[^f]*/** wklx, deny /sys/f[^s]*/** wklx, deny /sys/fs/[^c]*/** wklx, deny /sys/fs/c[^g]*/** wklx, deny /sys/fs/cg[^r]*/** wklx, deny /sys/firmware/efi/efivars/** rwklx, deny /sys/kernel/security/** rwklx, }
このプロファイルをAppArmorに登録する。
vagrant@localhost:~$ sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-profile-test vagrant@localhost:~$ sudo aa-status apparmor module is loaded. 14 profiles are loaded. 14 profiles are in enforce mode. /sbin/dhclient /usr/bin/lxc-start /usr/lib/NetworkManager/nm-dhcp-client.action /usr/lib/NetworkManager/nm-dhcp-helper /usr/lib/connman/scripts/dhclient-script /usr/sbin/ntpd /usr/sbin/tcpdump docker-bash-test docker-default docker-profile-test lxc-container-default lxc-container-default-cgns lxc-container-default-with-mounting lxc-container-default-with-nesting 0 profiles are in complain mode. 1 processes have profiles defined. 1 processes are in enforce mode. /sbin/dhclient (7038) 0 processes are in complain mode. 0 processes are unconfined but have a profile defined.
この新しい docker-profile-test
と言うプロファイルをコンテナに適用するには、 --security-opt
と言うオプションを指定すればよい。
vagrant@localhost:~$ docker run --rm -ti --security-opt apparmor=docker-profile-test centos:7 /bin/bash [root@1f1366f8d216 /]#
このコンテナの中では top
コマンドは発行できない。
[root@1f1366f8d216 /]# top bash: /usr/bin/top: Permission denied
また、 ls
コマンドを発行すると、ホストOSのカーネルのauditログとして記録されることが確認できる。
[root@1f1366f8d216 /]# ls anaconda-post.log bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
vagrant@localhost:~$ sudo tail -f /var/log/syslog ... Nov 24 00:15:10 localhost kernel: [190140.965776] audit: type=1400 audit(1479975310.710:12174): apparmor="AUDIT" operation="exec" info="ix fallback" profile="docker-profile-test" name="/usr/bin/ls" pid=10693 comm="bash" requested_mask="x" fsuid=0 ouid=0 target="docker-profile-test"
これらの連携について、詳細は 公式ドキュメント の通り。
特定のプロセスにプロファイルを適用する
特定のコマンドに限定しないプロファイルについては、コマンド実行時に当該プロセスに付与する形で適用できる。 aa-exec
と言うコマンドがある。
$ aa-exec USAGE: aa-exec [OPTIONS] <prog> <args> Confine <prog> with the specified PROFILE. OPTIONS: -p PROFILE, --profile=PROFILE PROFILE to confine <prog> with -n NAMESPACE, --namespace=NAMESPACE NAMESPACE to confine <prog> in -d, --debug show messages with debugging information -i, --immediate change profile immediately instead of at exec -v, --verbose show messages with stats -h, --help display this help
先ほどの top
の実行を禁止するプロファイルを適用する例。
$ aa-exec --profile=docker-profile-test /bin/bash $ top bash: /usr/bin/top: Permission denied
一方、 libapparmor と言う形で、AppArmorのプロファイル適用などの操作がライブラリに切り出されており、その関数を使ってプロファイルを当てることもできる。
例えば、現在のプロセスのプロファイルを変更する aa_change_profile(2)
と execve
のタイミングで変更する aa_change_onexec(2)
が用意されている(Ubuntuのman)。
それらをmrubyでラップした mruby-apparmor
を作っている。
GitHub - haconiwa/mruby-apparmor: A mruby gem to access libapparmor API
こんな感じでの使い方を想定している。mrubyであるので、他のコンテナ技術と同様haconiwaに組み込むこともできるだろう。
profile_name = ARGV[1] AppArmor.change_onexec(profile_name) exec "/bin/bash"
まとめ
- AppArmorを用いることで、プログラムまたは個別のプロセスに対する強制アクセス制限ができる。
- 今回は、特定のファイル実行を禁止したり、ファイルの実行記録を監査したりする例を試した。
- 個々のプログラムのパスを直接指定するほか、
aa-exec
などのユーティリティコマンドを用いて特定のプロセスをプロファイルを適用した形で実行できる。 - また、 libapparmor と言うC言語のライブラリがあり、libapparmorの一部の機能をmrubyでラップした。