Linux についてそこそこ詳しくなったので、そろそろ SELinux と本気で向き合おうとしている今日この頃です。SELinux 自体は下記のサイトさんが分かりやすくてなんとなく理解できた気がします。
今回はどうやってそれをコンテナに適用するか、調べてみました。
やりたいこととしては、複数のコンテナを立ち上げてコンテナごとにホスト側のファイルのアクセスを制限することです。
以下のようなディレクトリ構成の場合、
/srv/wwwpod/ ├ mariadb/ mariadb のみ許可 ├ nginx/ nginx のみ許可 ├ php/ php のみ許可 └ public/ nginx, php のみ許可
といった設定にしたいのです。元ネタはこの記事です。
コンテナのプロセスのコンテキスト
まずはコンテナのプロセスがどんなコンテキストになるか確認しました。ps コマンドって人によって好みのオプションがあると思いますが、それに Z を追加するとコンテキストが出てきます。
[root@localhost ~]# ps axZ
LABEL PID TTY STAT TIME COMMAND
system_u:system_r:container_t:s0 4000 ? Ss 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
system_u:system_r:container_t:s0 4001 ? Ss 0:00 nginx: master process nginx -g daemon off;
system_u:system_r:container_t:s0 4002 ? S 0:00 php-fpm: pool www
system_u:system_r:container_t:s0 4003 ? S 0:00 php-fpm: pool www
system_u:system_r:container_t:s0 4004 ? S 0:00 nginx: worker process
system_u:system_r:container_t:s0 4005 ? Ssl 0:00 mysqld
コンテナのプロセスのみ抽出しました。デフォルトだと全部 system_u:system_r:container_t:s0
になるみたいですね。
podman / docker のオプションを調べる
ひとまずコンテナ立ち上げ時に SELinux 関連のオプションがないか調べます。どうやら run する時にオプション –security-opt を使用することで設定できるようです。
system_u:system_r:container_t:s0 の場合
--security-opt label=user:system_u
--security-opt label=role:system_r
--security-opt label=type:container_t
--security-opt label=level:s0
動作確認は podman で行っています。 docker で動かなかったら申し訳ありません。
どういうコンテキスト構成にするか
ユーザーとロールはまだ理解していないのでなしとして、タイプかレベルを使うことになるのですが、レベルは用途的にタイプの中での切り分けで使うもののようなので、タイプを変更する方向でまずは考えます。
ただ、タイプを変えると以下のような許可設定を作らないといけないので少し手間がかかります。自作したい場合はこちらが参考になります。
module xxxx 1.0;
require {
type container_t;
}
allow container_t admin_home_t:file { read };
コンテナのデフォルトの許可設定はここみたいですが、正直見てもわかりません。どこかに container_t タイプのプロセスは container_file_t タイプのファイルの読み書きができるっていう設定があるはずなのですが・・・。タイプを変える場合はこの設定を維持しないと何かが動かなくなる気がするので、私がまだ立ち入ってはならない領域のようです。
簡単な方法を探す
ところで、 SELinux には MCS という概念があります。Multi Category Security の略です。
先ほどのコンテキストは system_u:system_r:container_t:s0
でしたが、レベルの s0 の後ろに s0:c20,c85
といった形で , 区切りで複数のカテゴリを指定できる機能です。カテゴリというか感覚的にはタグに近い気がします。
例として、ユーザーのコンテキストを確認すると以下のように出てきます。root で確認したのでほぼ許可するコンテキストになっています。
# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
分解すると以下のような形です。右に意訳を書いてみました。カテゴリの . は範囲指定です。
user: unconfined_u 制限のないユーザー
role: unconfined_r 制限のないロール
type: unconfined_t 制限のないタイプ
level(sensitivity): s0-s0 機密度 0~0 まで見れる
level(category): c0.c1023 カテゴリ 0~1023 まで見れる
機密度は大きくなるほど制限が強いんですが、ファイルの機密度を s1 とかにすると root ユーザーでも見えないってことですよね。相当な機密情報です。最大で s15 まで指定できるみたいですが、そこまで使い切る案件には出会いそうにないです。
カテゴリの動き
試しにディレクトリにカテゴリを設定してみます。
# chcon -Rt container_file_t /srv/wwwpod
# chcon -Rl s0:c1 /srv/wwwpod/mariadb
# chcon -Rl s0:c2,c4 /srv/wwwpod/php
# chcon -Rl s0:c3,c4 /srv/wwwpod/nginx
# chcon -Rl s0:c4 /srv/wwwpod/public
# ls -lZ
drwxr-xr-x. 2 root root system_u:object_r:container_file_t:s0:c1 4096 Aug 12 00:00 mariadb
drwxr-xr-x. 2 root root system_u:object_r:container_file_t:s0:c2,c4 4096 Aug 12 00:00 nginx
drwxr-xr-x. 2 root root system_u:object_r:container_file_t:s0:c3,c4 4096 Aug 12 00:00 php
drwxr-xr-x. 3 root root system_u:object_r:container_file_t:s0:c4 4096 Aug 12 00:00 public
ファイルのコンテキストの変更は chcon コマンドを使用します。
chcon – change file SELinux security context
man chcon より。注:強調は編集者によるもの
名前は chmod, chown と同じように覚えやすそうです。
R オプションは再帰的に反映ですので注意してください。
t オプションでタイプを変更できます。環境によるかもしれませんが、 container_file_t タイプではないファイルには、コンテナ側からアクセスできないです。
l オプションを指定するとコンテキストのレベルを変更できます。
s0 は s0-s0 の略です。機密度は変更しないんですが、機密度とカテゴリは2つ合わせてレベルという概念なのでセットで指定します。
ファイル側のカテゴリが c1 の場合は、c1 を許可されたユーザー・プロセスがアクセス可能です。ファイル側が c3,c4 の場合は、ユーザー・プロセスは c3,c4 両方を許可されていないとアクセスできません。
コンテナ側の設定
上記を踏まえて、コンテナ側のオプションを指定します。それぞれのコンテナの podman run / docker run に以下のような –security-opt オプションを追加します。
mariadb コンテナ: --security-opt label=level:s0:c1
php コンテナ: --security-opt label=level:s0:c2,c4
nginx コンテナ: --security-opt label=level:s0:c3,c4
このように指定すれば、最初に想定した通りの制限がかかります。許可されていない場所にアクセスすると、 Permission denied になります。
ls: can't open '.': Permission denied