ブログ

コンテナエスケープからの防御:マルチテナントホスティングのためのDocker分離実践ガイド

マルチテナント環境におけるコンテナエスケープ脆弱性や分離障害からDockerコンテナを保護する方法を、具体的な手順と例で学びましょう。

要約

Dockerコンテナはホストカーネルを共有するため、特にマルチテナントホスティングでは、1つのコンテナエスケープがすべてのテナントを侵害する可能性があるため、分離が極めて重要です。多くの開発者は、コンテナが完全に分離された仮想マシンであると想定していますが、現実は異なります。この記事では、Docker分離の基盤となるLinuxカーネル機能(名前空間、cgroup)と、それらを脅かす攻撃ベクトルについて説明します。特権の制限、セキュアなランタイムの使用、イメージのスキャン、ネットワークセグメンテーションの実装など、Dockerセットアップを強化するための実践的な手順を学びます。マルチテナントWordPressホスティングプロバイダーの実際の例を通して、これらの防御策を適用する方法を理解できます。パフォーマンスのトレードオフやseccomp/AppArmorの使用に関する注意点もカバーします。目標は、コンテナエスケープを防ぎ、テナントを安全に保つための堅牢な分離戦略を提供することです。

はじめに

共有WordPressホスティング、SaaSアプリケーション、開発環境サービスなど、マルチテナントホスティングプラットフォームを実行している場合、コンテナエスケープは悪夢のようなシナリオです。カーネルの脆弱性や設定ミスにより、あるテナントがコンテナから脱出し、他のテナントのデータやホスト自体にアクセスできるようになる可能性があります。Dockerの分離は、名前空間やcgroupのようなLinuxカーネル機能に依存していますが、デフォルトの設定では堅牢なセキュリティには不十分なことがよくあります。この記事では、攻撃ベクトルを解説し、マルチテナントWordPressの実際の例を交えながら、Dockerコンテナをロックダウンするための実行可能な手順を提供します。本番環境のオーケストレーションに関するより広範な情報については、本番対応コンテナ化アプリケーションのオーケストレーションガイドをご覧ください。

Docker分離の理解

Dockerコンテナは、プロセスレベルの分離を提供するためにLinux名前空間を使用します。PID名前空間はプロセスツリーを分離し、ネットワーク名前空間はネットワークインターフェイスを分離し、マウント名前空間はファイルシステムのマウントを分離し、ユーザー名前空間はコンテナのrootを特権のないホストユーザーにマッピングできるようにします。コントロールグループ(cgroup)は、CPU、メモリ、ディスクI/Oなどのリソース使用量を制限します。これらの機能が組み合わさって、各コンテナの周りに「サンドボックス」が作成されます。しかし、個別のカーネルを実行する仮想マシンとは異なり、コンテナはホストカーネルを共有します。これは、カーネルの脆弱性(例:CVE-2022-0492)が悪用されると、コンテナの名前空間分離から脱出できる可能性があることを意味します。さらに、コンテナ内でrootとしてコンテナを実行する、コンテナにすべてのケーパビリティを与える、または不要なLinuxケーパビリティをドロップしないといった設定ミスは、攻撃対象領域を広げる可能性があります。

攻撃ベクトル

一般的な攻撃ベクトルには以下が含まれます。

  • カーネルエクスプロイト: ホストカーネルのバグを悪用してホストアクセスを取得する。
  • 特権コンテナ: --privilegedオプションで実行すると、すべてのケーパビリティが付与され、ほとんどの分離が無効になる。
  • ケーパビリティの乱用: 完全な特権モードなしでも、CAP_SYS_ADMINCAP_NET_ADMINのような危険なケーパビリティを持つコンテナは、ファイルシステムをマウントしたりネットワーク設定を操作したりできる。
  • 安全でないイメージの実践: 既知の脆弱性を持つベースイメージの使用や、コンパイラやシェルインタープリタなどの不要なツールの含める。
  • 共有マウント名前空間: ホストディレクトリをコンテナにマウントすると、読み取り専用でない場合にエスケープを許す可能性がある。

実践的なセキュリティ対策

1. 非rootユーザーとしてコンテナを実行する

デフォルトでは、Dockerはコンテナ内でrootとしてコンテナを実行します。攻撃者がコンテナ内でroot権限を取得した場合、より多くの影響力を持つことになります。Dockerfileでユーザーを作成し、USERディレクティブを使用します。また、可能な限り、任意のホストユーザーにマッピングするためにDocker Composeで--userフラグを使用することは避けてください。

2. すべてのケーパビリティをドロップし、必要なものだけを追加する

Linuxケーパビリティは、スーパーユーザー権限を小さな単位に分割します。Docker Composeでは、cap_drop: ALLを使用し、その後、必要なもの(例:NET_BIND_SERVICE)のみをcap_addします。SYS_ADMINNET_ADMINSYS_PTRACEのような危険なケーパビリティは避けてください。

3. 読み取り専用ルートファイルシステムを使用する

コンテナ定義でread_only: trueを設定します。これにより、攻撃者がコンテナのファイルシステムに書き込むのを防ぎます。アプリケーションが一時ファイルを書き込む必要がある場合は、その場所にtmpfsボリュームをマウントします。

4. ユーザー名前空間のリマッピングを有効にする

ユーザー名前空間のリマッピングは、コンテナのrootユーザーを非rootホストユーザーにマッピングします。これにより、コンテナのrootがエスケープした場合でも、リマップされたユーザーの権限しか持たないため、分離のレイヤーが追加されます。/etc/docker/daemon.json"userns-remap": "default"を設定して有効にします。これにより、ボリュームの権限が複雑になる可能性があることに注意してください。詳細については、安全で効率的なWebホスティングのためのDocker分離マスターガイドを参照してください。

5. SeccompおよびAppArmor/AppArmorプロファイルの適用

Seccompは、コンテナが実行できるシステムコールを制限します。Dockerは、危険なシステムコールをブロックするデフォルトのseccompプロファイルを提供します。カスタムプロファイルを作成することもできます。同様に、AppArmor(またはSELinux)は強制アクセス制御を提供します。AppArmorを使用して、コンテナを許可された操作の最小セットに制限します。セキュリティプロファイルは、Docker Composeのsecurity_optを介して設定できます。

6. 最小限のベースイメージを使用し、脆弱性をスキャンする

AlpineやDistrolessのような、攻撃対象領域が小さいイメージを選択します。Docker Scout、Trivy、Clairなどのツールで定期的にイメージをスキャンします。脆弱なイメージがデプロイされるのを防ぐために、CI/CDパイプラインにスキャンを統合します。

7. カスタムブリッジネットワークによるネットワークセグメンテーション

テナントまたはアプリケーションティアごとに個別のブリッジネットワークを作成します。これにより、東西トラフィックが制限されます。Docker Composeでは、ネットワークを定義し、サービスを分離します。サービスがインターネットへのアウトバウンドアクセスを必要としない場合は、internal: trueを使用します。ホスト上のファイアウォールルールは、コンテナ間のトラフィックをさらに制限します。

8. Cgroupによるリソースの制限

Docker Composeでdeploy.resources.limitsを使用してCPUとメモリの制限を設定します。これにより、侵害されたコンテナがリソース枯渇攻撃を開始するのを防ぎます。さらに、より細かい制御のためにkernel_memorymemory_reservationを設定します。

実例:Docker ComposeによるマルチテナントWordPressホスティング

複数のWordPressサイトを異なるクライアントのために、それぞれ独自のDockerコンテナでホストするシナリオを考えてみましょう。安全でないセットアップは次のようになります。

version: '3'
services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - ./wp-content:/var/www/html/wp-content
  db:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

このセットアップは脆弱です。WordPressコンテナは内部でrootとして実行され、すべてのケーパビリティを持ち(何もドロップされていないため)、書き込みアクセス権を持つホストディレクトリをマウントし、無制限のネットワークアクセスを持っています。

次に、これを強化しましょう。

version: '3'
services:
  wordpress:
    image: wordpress:latest
    user: www-data
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /var/www/html/wp-content/plugins
    security_opt:
      - seccomp=seccomp-profile.json
      - apparmor=wordpress-profile
    networks:
      - frontend
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wp-uploads:/var/www/html/wp-content/uploads
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
  db:
    image: mysql:5.7
    user: mysql
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    networks:
      - backend
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
    deploy:
      resources:
        limits:
          cpus: '0.25'
          memory: 128M
networks:
  frontend:
    driver: bridge
    internal: false
  backend:
    driver: bridge
    internal: true
volumes:
  wp-uploads:
  db_data:

主な改善点:

  • 両方のコンテナが非rootユーザー(www-datamysql)で実行されます。 -すべてのケーパビリティがドロップされ、NET_BIND_SERVICEのみが追加されます。 -WordPressのファイルシステムは、tmpfsマウントとアップロードボリュームを除き、読み取り専用です。 -SeccompおよびAppArmorプロファイルが適用されます(カスタムプロファイルを提供する必要があります)。 -別々のネットワークがWebとデータベースを分離し、データベースネットワークは内部です。 -リソース制限により、リソース枯渇を防ぎます。

WordPress固有のDocker強化の詳細については、Docker for WordPress: 隔離されたコンテナがすべてを変える理由をご覧ください。

注意点

  • ユーザー名前空間のリマッピング: 強力ですが、リマップされたホストUIDがコンテナUIDと同じではないため、ボリュームマウントが壊れます。正しい権限でディレクトリを事前に作成するか、リマッピングをサポートするDockerボリュームを使用する必要がある場合があります。
  • Seccomp/AppArmorプロファイル: カスタムプロファイルは、アプリケーションのシステムコールとファイルアクセスパターンを理解する必要があります。過度に制限的なプロファイルは機能を損なう可能性があります。十分にテストしてください。
  • パフォーマンス: SeccompやAppArmorのような追加のセキュリティレイヤーは最小限のオーバーヘッドですが、リソース制限や読み取り専用ファイルシステムは、書き込みが多いアプリケーションに影響を与える可能性があります。
  • オーケストレーションの複雑さ: マルチテナント環境では、テナントごとのDocker Composeファイルの管理が煩雑になる可能性があります。Kubernetesのような高レベルのオーケストレーションツールを検討してください。ただし、それ自体にもセキュリティ上の考慮事項があります。

結論

コンテナエスケープは、マルチテナントDockerホスティングにおける現実的な脅威ですが、予防可能です。分離メカニズムを理解し、ケーパビリティのドロップ、非rootユーザーでの実行、ユーザー名前空間の有効化、seccomp、AppArmor、ネットワークセグメンテーション、定期的なイメージスキャンなどの多層防御を適用することで、リスクを大幅に軽減できます。Dockerのデフォルト設定は、マルチテナントワークロードには本番環境レベルではないことを忘れないでください。これらの手順を今日から実装して、テナントとインフラストラクチャを保護してください。Dockerセキュリティのベストプラクティスに関する包括的な概要については、DockerによるWebアプリケーションの保護:分離とベストプラクティス実践ガイドを参照してください。

Sources (5)