BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Dockerのセキュリティは十分ですか?セキュアなコンテナイメージとランタイムを構成するためのアドバイス

Dockerのセキュリティは十分ですか?セキュアなコンテナイメージとランタイムを構成するためのアドバイス

キーポイント

  • The security of Docker is dependent on how you use it: it is overly simplistic to ask ‘Is it Secure?’ when security lies in fine-tuning it for your use-case.
  • You must have a thorough understanding of the difference between Docker image and Docker container runtime and the separate security priorities for each.
  • Working with Docker should involve the principle of least privilege: minimum permissions given whilst still achieving functionality.
  • You can reduce the permissions your Docker images have by: avoiding images running as root user, reducing access to the binaries, and only including binaries that are absolutely necessary at runtime - even going back and removing those used during build.
  • For the Container runtime, ensure your containers are isolated from the host, adjust the default security profile to suit your project and make sure to use newer implementations like containerid and CRI-O to reduce binaries.

原文(投稿日:2022/02/23)へのリンク

Dockerは今や、ほとんどの開発者がよく知っているプラットフォームです。コンテナと呼ばれるパッケージ内で、アプリケーションを簡単に生成し、デプロイし、実行できるようにしてくれます。仮想マシンという形でワークロード毎にオペレーティングシステムを重複するのではなく、必要な依存関係を"パッケージ"にして、ホストオペレーティングシステム上のプロセスとして実行します。こうすることで、マシン間におけるコンフィギュレーションの小さな差異を避けているのです。

Dockerがこのアプローチをポピュラーなものにしたことにより、私たちの多くがDocker ContainerやDocker Imageを話題にするようになりました。実際には、イメージやコンテナには"Docker"は必須ではなく、同じようなフレームワークをベースにすることもできます。

クラウドネイティブなプログラミングの人気が高まるにつれて、DockerやDockerスタイルのアプローチは人気を集めています。"クラウドネイティブ"にはさまざまな意味がありますが、おもにクラウドインフラストラクチャ上でアプリケーション — 大半はマイクロサービスアーキテクチャの — を実行することを指します。クラウドネイティブなアプリケーションでは、クラウドプロバイダの提供する自動化ツールやリソース、機能を利用します。Dockerのようなコンテナ化ツールは多くの場合、このようなプログラミングスタイルに便利です。というのは、コンテナのコンテンツやセットアップによって、基盤となるシステムに縛られない、再利用可能な環境が得られるからです。 

Dockerを使用しているのであれば、Dockerが自分のアプリケーションにとって十分安全なものなのか、ということも知りたいでしょう。多くのシステムがそうであるように、"Dockerはセキュアか?"という質問には、単純にイエスまたはノーで答えることはできません。Dockerを安全な方法で運用することは可能ですが、そのために考慮しなければならない、いくつかのアクションがあります。

このブログでは、Dockerに関する最も重要なセキュリティ考慮事項について説明したいと思います。 

Docker対Dockeイメージ

Dockerのセキュリティについて考えるには、コンテナ内でイメージを実行するDockerと、Dockerイメージ自体の違いについて理解する必要があります。違いは2つあります。

コンテナはDockerイメージから起動します。Dockerイメージは、実行する必要のあるプロセスと、実行に必要なファイルとを定義する層構造になっています。例えばあなたがJakarta EE開発者ならば、これはJakarta EEサーバのインストールと、あなたのアプリケーションになるでしょう。 

Docker Hubは、Dockerイメージの格納と共有を行うためのリポジトリです。これらのイメージからコンテナを直接起動することもできますし、イメージを拡張して、必要なカスタマイズを行った上で利用することも可能です。イメージをカスタマイズする方法や、中に含めるバイナリとそのパーミッションの選択は、アプリケーションのセキュリティに影響します。 

さらに、コンテナを実際に動作させるプログラムがあります。これはイメージやコンテナ、ネットワーク、ストレージボリュームをホストするデーモンプロセス(ユーザの直接的なコントロール下にないバックグラウンドプロセス)で、Docker Engineの場合もあれば、何か他の実装の場合もあります。あなたのプロセスを分離して実行する役割は、このプロセスが担っています。このコンテナの実行方法もまた、セキュリティに影響するのです。

イメージに関するセキュリティの考察

Open Container Initiative(OCI)仕様のルールに準拠して構築したDockerイメージは、最初から包括的なセキュリティを提供するものではありません。このプロセスがコンテナおよびホストシステム内において十分にセキュアである、という確証を持つためには、通過しなければならないステップがいくつかあります。

プロセスをコンテナで運用する時の大きな問題は、そのアプリケーションが誰かに"ハッキング"された場合、基盤にあるホストを通じたアクセスを獲得することにより、多くのシステムに対する大きなリスクとなり得ることです。

コンテナは仮想マシンよりもホストに強く統合されている(前述のように、ホストのオペレーティングシステム上で動作しているため)ので、コンテナを運用する場合には、セキュリティにはより多くの注意を払う必要があります。セキュリティ上の脆弱性は、コンテナ内で発生した場合には、より重大なものになります。アプリケーションが物理的に異なるマシン上で動作していれば、それぞれのアプリケーションはある程度分離されているのですが、コンテナソフトウェア内に脆弱性が存在する場合には、ひとつのアプリケーションないしプロセスが別のコンテナ内のアプリケーションにアクセスすることで、その脆弱性にアクセスしたり、新たな脆弱性を生み出したりする可能性があります。

コンテナ内で運用するアプリケーションないしプロセスに関して取るべき予防措置のひとつとして、決して"root"ユーザとして実行してはならない、というものがあります。rootで実行されるプロセスは強力なパーミッションを持っていて、より低レベルなリソースプロセスにアクセスすることが可能です。コンテナスクリプト内のどこかで、メインプロセスを実行するユーザを常に表示するようにしておくべきです。

USER myuser

理想を言えば、プロセスとアプリケーションのバイナリはすべて所有者をrootにした上で、プロセスを実行するユーザによる読み取りと実行のパーミッションのみを持つようにしておくとよいでしょう。こうしておけば、そのプロセスからコンテナ内のアプリケーションを構成するバイナリやスクリプトを変更することはできなくなるので、漏洩が発生した場合の被害を限定的にすることが可能になります。

このシナリオは、コードを可能な限り低いレベルのパーミッションで実行するという、最小特権の原則(principle of the least privilege)を実現するものです。プロセスがパーミッションを持っていなければ、悪用することはできません。もうひとつの原則は、潜在的な攻撃対象領域を削減することです。必ずしも必要ではないバイナリがイメージに含まれている場合、それらがセキュリティ的脆弱性の原因になる可能性があります。

ですからイメージには、絶対に必要なバイナリのみを含むようにしてください。可能ならば"スクラッチ"イメージを出発点にして、必要なバイナリのみを加えていくとよいでしょう。あるいはAlpineイメージのような、小さなイメージからスタートするようにします。存在するバイナリや実行可能ファイルが少なければ、潜在的なセキュリティ脆弱性に寄与することも少なくなります。

イメージの不要な部分を取り除く第3の方法は、マルチステージビルドを採用することです。特に"イメージ"そのものを使用して、コンテナ内で実行する必要のある最終的なアプリケーションを構築する場合には、その他のステップはすべて別のステージで実行することができます。こうすれば、レイヤ内でイメージを適切に構造化できると同時に、実行時に不要なものはすべて削除することが可能になります。

#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package

#
# Package stage
#
FROM payara/micro:5.2021.10-jdk11
COPY --from=build /home/app/target/hello.war ${DEPLOY_DIR}

上記のマルチステージビルドは、必要なファイルとプロセスのみを最終的なイメージに保持する例を示しています。ソースコードやmavenツールは最終的なイメージには不要で、必要なのはWebアプリケーションのWARファイルだけです。2つのステージに分割することによって、不要なものは実行時に利用できないようにしています。プロセスやアプリケーションが標準的なイメージの一部であったとしても、同じ方法論を用いるべきでしょう。可能であれば、ベースイメージ(fromscratch)からスタートして、本当に必要なものだけを加えるようにするべきです。 

コンテナランタイムに関するセキュリティの考察

イメージの実行方法や、イメージの実行に使用するソフトウェアもまた、セキュリティ上の脆弱性につながる可能性があります。

コンテナ内でのプロセス実行にrootを用いるべきではない、ということはすでに述べましたが、特権実行はコンテナの起動時にも見られます。このフラグを設定すると、コンテナにすべての機能を与えると同時に、デバイスのcgroupコントローラが適用するすべての制限を解除することになります。このため、セキュリティ上の問題が発生した場合には、他よりも大きな影響があるのです。

コンテナは"サンドボックス"内で実行して、ホストや実行中の他のコンテナから分離する必要があります。この特権フラグはこのサンドボックスを削除するので、一切使用するべきではありません。また、オプション"--net=host"もサンドボックスに影響するため、セットすることは避けてください。このオプションはコンテナに対して、rootプロセスと同じように低い番号のポートをオープンできるようにするため、サンドボックスの分離に影響する可能性があります。

コンテナ実行時にホストネットワークオプションを使用すると、ポートマッピングが無効になり、ホストネットワークからの分離も効果がなくなります。コンテナがホストネットワークと同じネットワークリソースを使うことになるのです。この低い領域のポートは"ウェルノウンポート(well-known port)"と呼ばれるものです。一般的にはスーパーユーザのプロセスからのみ接続されるものなので、ユーザが意識することはあまりないのですが、コンテナプロセスがネットワークスタック全体にアクセスできるようになれば、他のウェルノウンポートのスキャンを行う可能性があります。外部からのアクセスはおそらく不可能ですが、ホストのネットワークを使っているので、コンテナプロセスの内部からポーリングすることは可能です。

コンテナの起動にDockerイメージを使用可能なプログラムは、Dockerランタイムだけではありません。すでに述べたように、Dockerはコンテナをポピュラーなものにしたツールですが、最初の実装であったため、こうしたコンテナランタイムの運用方法については、長年にわたって多くのことが学ばれてきました。KubernetesのConrainer Runtime Interface(CRI)が定義されたことを受けて、このCRIに従う形でより優れた、よりセキュアな実装も登場しています。

現在では、コンテナ型ランタイムやCRI-Oランタイムを使用して、Dockerイメージベースのコンテナを運用することも可能です。これらの実装では一部のバイナリやプロセスが省略されているため、よりスリムで高速、かつ安全なものになっています。例えば、dockerd(Docker Runtimeプロセスの名称)とは異なり、実行中のコンテナにSSHアクセスすることはできません。攻撃領域が縮小することで、問題が発生する確率も小さくなっています。

しかし、これら新しいバイナリであっても、セキュリティ上のリスクはゼロではないので、プロセスに合わせたセキュリティの調整が求められます。コンテナを対象としたセキュリティプロファイルも存在しますが、AppRrmor Linux Security Moduleを使えば、セキュリティプロファイルをもっと詳細に調整することができます。フォルダへのアクセス、ネットワークアクセス、読み込みや書き込み、実行の許可(あるいは不許可)を定義することが可能です。AppArmorファイルの以下のエントリは、/etcおよび/homeディレクトリへの書き込みとリストアクセスを定義するものです。

deny /etc/** wl,
deny /home/** wl,

コンテナ内のプロセスに何が必要かという知識に基づいて、アプリケーションが適切に動作する上で必要なパーミッションのみを開放することが必要です。このプロファイルは、コンテナ実行時に指定することが可能です。

docker run <other options> --security-opt apparmor=my_profile <container-image>

結論:最大のセキュリティを実現するためのファインチューニング 

Dockerイメージやコンテナは、さまざまなシナリオでの使用が想定されているので、自身のユースケースに合わせて調整する必要があります。その場合においても、セキュリティの一般原則が、必要なものを判断するガイドラインになります。 

最小特権の原則とは、セキュリティ違反を回避するために、機能を達成する上で最小限のパーミッションのみを与えるべきだ、というものです。コンテナ化においては、コンテナ内のメインプロセスをrootユーザで実行すべきではない、という意味になります。ファイルにも適切なパーミッションを使用した上で、AppArmorプロファイルを使ってアクセスを制限するべきです。

攻撃領域を小さくするためには、自分たちのユースケースにおいて本当に必要なものだけを含めるようにすると同時に、例えばconteinerdやCRI-Oのような新しい実装を使うことや、できる限り少ないバイナリやプロセスでコンテナを運用することも必要です。

作者について

この記事に星をつける

おすすめ度
スタイル

BT