ytooyamaのブログ

サーバ構築とか、仕事で発見したこととか、趣味のこととかを書いています。

コンテナーとセキュリティーについて調べたのをまとめる

コンテナーとセキュリティーについて、現在調査をしています。 Linuxにおけるセキュリティーというと、まず思い浮かべるのがSELinuxでしたが、他にも色々存在する事がわかりました。

調べたら次のような情報を見つけました。また石川さんがごめんなさいされていますね。

blog.1q77.com

一つ目の問題

Dockerには-vオプションでDockerホストのディレクトリーパスをマウントする機能があります。 手軽にコンテナーに対して永続データの置き場所を提供できるので便利である反面、注意して使わないとセキュリティー的にもインフラの継続性的にも問題があります。

例えば先のサイトにも書かれていましたが、次のようなコマンドを実行しても拒否されることなく結果が出力されてしまいます。

[root@localhost ~]# docker run -it -v /:/rootfs fedora bash

[root@dab3405e670b /]# cat /rootfs/etc/shadow
root:!::0:99999:7:::
bin:*:18178:0:99999:7:::
daemon:*:18178:0:99999:7:::
...
[root@dab3405e670b /]# echo test >> /rootfs/etc/passwd
[root@dab3405e670b /]# 

これは、Dockerホストの / をコンテナーの/rootfsにマウントして、コンテナー内部からDockerホストのファイルを参照したり、書き換えるということを認証もなく実行できてしまう...という問題です。コンテナーはVMと違ってカーネルを共用していますし、コンテナーはrootユーザーで動いているからできる芸当ですね。

RHEL DockerやPodmanでは

SELinuxが有効な状態でRHEL DockerもしくはPodman、CRI-Oなどを使っていると、上記のような問題を制限できます。試しに同じコマンドをFedoraとPodmanで実行してみましょう。次のようにPermission deniedと表示され、コマンドの実行を拒否されます。

[root@localhost ~]# podman run -it -v /:/rootfs fedora bash

[root@1365e5cc2291 /]# cat /rootfs/etc/shadow
cat: /rootfs/etc/shadow: Permission denied
[root@1365e5cc2291 /]# echo test >> /rootfs/etc/passwd
bash: /rootfs/etc/passwd: Permission denied

RHEL DockerとはRHEL 7、CentOS 7、過去のバージョンのFedoraで提供されていたDocker 1.13.1までのバージョンのことです。その名の通りRed Hatがメンテナンスをしていたバージョンです。ちなみにDocker CEではSELinuxによる保護は限定的です。

それではDocker CEからRHEL Dockerに乗り換えると良いかというと、そういうわけにもいきません。RHEL DockerはRHEL7やCentOS 7では使えますが、それ以降のバージョンでは使えませんし、そもそも現時点では最新リリース版のFedora 31ではもはや提供されていません。Podmanをおすすめします。 ちなみにpodman-dockerというパッケージをインストールするとdockerコマンドを叩いてもよきにはからってpodmanに置き換えて実行してくれます。

長くDockerに対して言われていた「dockerdみたいなデーモンがコンテナーが実行されているか否かに関わらず実行され続ける」問題もPodmanにはありません。

もう一つの問題

SELinuxを使うとある程度、コンテナーのセキュリティインシデントに対して防御できることがわかりましたが、今日はここで終わりではありません。SELinuxを有効にした状態でも防げない問題も存在します。次をご覧ください。

[root@localhost ~]# docker run -v /:/rootfs -it fedora bash

[root@0336f1e252ac /]# chroot /rootfs
sh-5.0# do
do            docker-init   dockerd       done          dosfsck       
docker        docker-proxy  domainname    dos2unix      dosfslabel    
sh-5.0# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
0336f1e252ac        fedora              "bash"              About a minute ago   Up About a minute                       modest_lalande

これは何をやっているかというと、コンテナーでchrootしてコンテナーから抜けるというチャレンジをしてみました。これもとくに認証されずに実行できてしまいます。

コマンドの3行目でコンテナーから抜けています。そのあと、ホスト上でdockerコマンドを実行してみました。抜けてきたコンテナーが実行されていることがわかります。ちなみに「exit」を実行するとコンテナーに戻る事が可能です。悪意のあるユーザーやソフトウェアが含まれたコンテナーにより、ホスト上でコード実行される危険性があるということです。これに対する対策が必要であることがわかりました。

seccomp(secure computing mode)を使おう

Dockerはseccomp(secure computing mode)という、Linuxカーネル上でアプリケーションのサンドボックスメカニズムを提供するためのセキュアコンピューティングの機能をサポートしています。次が公式のドキュメントです。

docs.docker.com

seccompは多くの他のコンテナーランタイムでもサポートしています。ある程度のSyscallはコンテナー内で実行された場合にブロックしてくれます。しかし、デフォルトの設定ではchrootは許可されているようです。Dockerのデフォルトの定義は先のドキュメントにも書かれていますがGitHubに公開されています。

github.com

Dockerではdocker run実行時に--cap-add、--cap-dropを使って特定の機能を許可する、拒否することが可能です。 以下は実行例です。dockerコマンドをpodmanに差し替えても同じ結果になります。

[root@localhost ~]# docker run -v /:/rootfs --cap-drop SYS_CHROOT -it fedora /bin/bash

[root@7f27b76ebb21 /]# chroot /rootfs
chroot: cannot change root directory to '/rootfs': Operation not permitted

これは次のようにjson定義ファイルを指定して実行することもできます。同様にchrootを拒否できました。dockerコマンドをpodmanに差し替えても同じ結果になります。

[root@localhost ~]# docker run --security-opt seccomp=/root/custom-seccomp.json -v /:/rootfs -it fedora bash

[root@6b37638706d7 /]# chroot /rootfs
chroot: cannot change root directory to '/rootfs': Operation not permitted

上記の実行例は660行目から673行目を取り除いたファイルを使っています。

rootlessの試み

SELinuxとseccompを組み合わせて使えば、オペミスや問題のあるコンテナーイメージがあったとしても、一定の問題を回避できることがわかりました。しかし、LinuxのすべてのディストリビューションでSELinuxが使えるわけではありませんから、その場合は別の対策が必要になります。

べつの観点からコンテナーのセキュリティーを観測してみると、roorlessというキーワードが見つかります。これはその名が示すとおり、rootユーザーを使わないでコンテナーと関連するサービスを実行するという試みです。

例えばDockerはrootlessで動くDockerを開発、提供していますし、Red Hat Enterprise Linux 8.1に含まれるPodmanは制限はあるものの、rootlessでの実行をサポートしています。Kubernetesもusernetesという、root権限を必要せずに動くKubernetesを使えるようにしようという試みが行われています。

最後に

SELinuxが有効な環境で正規な理由でホストパスをマウントしたい場合は次のように実行します。 次の例はPodmanで実行した例ですが、同様にDockerでも可能です。パスの最後にzを付加しています。このパスには読み書きが可能です。

# podman run -v /root/data:/mydata:z -it fedora /bin/bash

ここらへんについては次の情報を参考にしてください。

qiita.com

参考情報

Red Hat Enterprise Linux Atomic Host 7 コンテナーセキュリティガイド

Seccomp security profiles for Docker

その他の情報

このブログサイトはJavaScriptを使っていますが、読み込んでいるJavaScriptは全てはてなが提供しているものであり、筆者が設置しているものではありません。