前回はDockerのrootlessを試しましたので、今回はPodmanで同じようなことを試してみようと思います。
Podmanとは
PodmanとはRed Hat社が開発したコンテナ管理ツールです。
Red Hat Enterprise Linux 8以降ではDockerの利用は非推奨となり、その代替としてPodmanが提供されています。PodmanはDockerと互換性があり、コマンドライン操作についてはほぼ互換になっています。
PodmanにはDockerのようにdockerデーモンに該当するデーモンプロセスがないため、デーモンがダウンして全コンテナに影響が出るといったことがないのも特徴の一つです。
最近のDockerであればrootlessについては同様に可能ですが、インストールするだけですぐroot権限が無くても一般ユーザで利用できる点がDockerよりも優れています。(Dockerのrootlessモードはちょっとだけ設定変更が必要です)。
Red Hat Enterprise Linux(以下RHEL)や様々なRHELクローン、CentOS StreamやFedoraではパッケージが標準で提供されており、簡単に導入可能です。そのほかのLinux ディストリビューションでもパッケージが提供されているようです。
Podmanの設定の話
PodmanはデフォルトでSELinuxサポートが適用されています。
Podmanなどのデフォルトの設定は /usr/share/containers/ に置かれていて、デフォルト設定でもある程度セキュアな状態を保っています。
$ ls /usr/share/containers/ containers.conf mounts.conf seccomp.json selinux
PodmanとSELinux
このディレクトリーのselinuxディレクトリーにはSELinuxがホスト上でenforcingモードの場合、それぞれのプロセスに設定するコンテキストが設定されています。
# tree selinux/ selinux/ └── contexts # cat selinux/contexts process = "system_u:system_r:container_t:s0" file = "system_u:object_r:container_file_t:s0" ro_file="system_u:object_r:container_ro_file_t:s0" kvm_process = "system_u:system_r:container_kvm_t:s0" init_process = "system_u:system_r:container_init_t:s0" engine_process = "system_u:system_r:container_engine_t:s0"
Podmanとセキュアコンピューティングモード
seccomp.jsonはセキュアコンピューティングモード(secure computing mode; seccomp)の設定がされています。この設定によってコンテナー内で利用できる処理(システムコール)を制限できます。
適切な設定が行われていないと、コンテナランタイムの脆弱性や仕様を悪用するなどして不正利用や情報の漏洩につながる可能性があるので、コンテナーに対してシステムコールの許可を適切に設定することは重要です。ちなみにDockerでも標準で300 以上のシステムコールのうち、約 44 件のシステムコールを無効にしているようです。
標準のseccomp.jsonをコピーもしくは編集してカスタムポリシーの定義も可能ですが、設定ファイルはJSON形式で書かれていますし、かつ設定の記述方法もあまり情報がなく難解なので、設定するのは至難の業です。
Podmanの場合はcontainers.conf
にケーパビリティを設定するところがあります。ここを適切に設定するだけで必要もしくは不要なシステムコールの定義ができるので、こちらで設定するほうがわかりやすくておすすめです。
Linuxではプロセスは一般ユーザ権限か特権(root権限)で動きます。一般ユーザー権限では動かないプロセスの場合は特権を付与して実行するわけですが、すべての特権を渡してしまうとそのプロセスに脆弱性があった場合に、すべての特権を取られてしまう可能性があります。それを防ぐために、現在のLinuxでは特権を細分化して「ケーパビリティ」として定義されています。
適切なケーパビリティを付与することによって、すべての特権を付与するのと比べてプロセスの脆弱性を狙った攻撃を食らったとしても被害を最小化することができる可能性があります。
ケーパビリティとはつまり、必要最小限の権利を与えるために設定するもののことです。Linux kernel 2.4から実装されています。詳細については次をご覧ください。
- https://gihyo.jp/admin/serial/01/linux_containers/0042
- https://manpages.ubuntu.com/manpages/noble/ja/man7/capabilities.7.html
- https://security.sios.jp/security/capability-info-20200701/
Podmanには次のようなケーパビリティがデフォルトで設定されていました。
# List of default capabilities for containers. If it is empty or commented out, # the default capabilities defined in the container engine will be added. # default_capabilities = [ "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "NET_BIND_SERVICE", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_CHROOT"
最近のLinuxディストリビューションではDockerとPodmanのパッケージが提供されており、あえてPodmanを使う理由がわからないと思われる方もいるかも知れませんが、こういった設定が簡単にできる点がPodmanのメリットであると私は考えます。
ちなみに /usr/share/containers/containers.conf
の冒頭にも書かれていますが、/usr/share/containers/
にデフォルトの設定が、/etc/containers/
にrootユーザーでPodmanを使うときの設定があり、ユーザーごとの設定は$HOME/.config/containers/
に置くと良いそうです。
Podmanの設定を変えてみる
先程のケーパビリティのデフォルト設定を、ユーザーでPodmanを使ってコンテナーを実行した場合にchrootができないように設定変更してみましょう。基本的にはデフォルトのcontainers.conf
を持ってきて、書き換えるだけで設定は完了します。
$ mkdir -p $HOME/.config/containers/ $ cp /usr/share/containers/containers.conf $HOME/.config/containers/ $ cp /usr/share/containers/storage.conf /etc/containers/ $ vi $HOME/.config/containers/containers.conf ... default_capabilities = [ "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "NET_BIND_SERVICE", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_CHROOT" ]
「SYS_CHROOT」の行を取り除いてみてください。忘れずに最後の項目のカンマも取り除きます。
storage.conf
の方もrootless_storage_path
のコメント部分を外します。
... # Storage path for rootless users # # rootless_storage_path = "$HOME/.local/share/containers/storage" rootless_storage_path = "$HOME/.local/share/containers/storage"
設定したディレクトリーを作成しておきましょう。
$ mkdir -p $HOME/.local/share/containers/storage
Podmanで前回Dockerでやったことを試す
では、前回試したことをPodmanで実行してみましょう。 Podmanの場合、dockerのコマンドをpodmanに置き換えるだけで、後はサブコマンド、オプションを指定するだけで同じように使えます。
まずはコンテナーを起動し、
$ podman container run -it -v /:/rootfs fedora:36 bash [root@49400a4458e6 /]# cat /etc/fedora-release Fedora release 36 (Thirty Six)
chrootを実行すると、次のようにパーミッションがないと言われて失敗します。
[root@49400a4458e6 /]# chroot /rootfs chroot: cannot change root directory to '/rootfs': Operation not permitted
もとの設定にあった「SYS_CHROOT」を追加してみましょう。
$ vi $HOME/.config/containers/containers.conf ... default_capabilities = [ "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "NET_BIND_SERVICE", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_CHROOT" ]
もう一度同じように実行してみると、コンテナーのシェルから抜けてしまうことが可能でした。
$ podman container run -it -v /:/rootfs fedora:36 bash [root@499e0eae894f /]# chroot /rootfs sh-5.1#
とはいえ、システムでSELinuxが有効になってさえいれば、抜けたシェルの先で任意のコマンドを実行するのはなかなか至難の業です。前の方法のようにchrootは実行できないようにしておいたほうが、多くのLinuxディストリビューションで有効な設定ですし、安全です。
sh-5.1# podman ps Error: error creating runtime static files directory: mkdir /var/lib/containers/storage/libpod: permission denied sh-5.1# dnf update Config error: [Errno 13] Permission denied: None
というわけで、Podmanはデフォルトの設定でSELinuxが有効であり、デフォルトの設定のままでも一定の保護は有効であることがわかりました。また、ケーパビリティを適切に設定することで最小特権をコンテナーに適用することができることも確認できました。