ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

AppArmorとDockerとその他コンテナ的プロセスについて調べた

ここ数日のAppArmorに関する調査をまとめておく。アドベントカレンダーの季節だけど、特に該当しそうなカレンダーがなかったのでただの記事を書きます...

突っ込みどころはあると思うので、修正指摘等お気軽に。

AppArmor とは

AppArmorはMAC(Mandatory Access Control - 強制アクセス制御)を実現するためのミドルウェアの一つ。同じようにMACを実現するものにはSELinuxなどがあるが、AppArmorはシステム全体というよりはプログラム単位で、ファイル別またはプロセスごとにセキュリティプロファイルを紐づけることができる。

そのプロファイルでは:

  • どのファイルにどのようにアクセスできるか/できないか(読み書き実行など)
  • ネットワークやRaw Socketなどへのアクセスができるか

以上についてUNIXのファイル権限やKernel Capabilityに関係なく強制的な制限をかけることができる。

UbuntuCanonical社により開発がされており、Ubuntuではデフォルトで統合されている。その他サポートしているディストロはDebian、Arch LinuxOpenSUSEなど(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でラップした。