コンテナ環境は開発の効率化に貢献する一方で、設定ミスひとつがホストOS全体の侵害につながるリスクを抱えています。Snykの調査では、スキャン対象のDockerイメージの44%に既知の脆弱性が含まれ、より安全なベースイメージへの切り替えで修正可能だったと報告されています。さらに、Docker Hubの人気イメージ上位10件にはそれぞれ少なくとも30件以上の脆弱性が存在していました(出典: Snyk)。

Dockerセキュリティとは、コンテナイメージのビルドからランタイム実行、デーモン設定、ネットワーク制御に至るまで、コンテナライフサイクル全体にわたる防御の仕組みです。Docker公式ドキュメントでは、セキュリティを次の4つの柱で整理しています。

  1. カーネルの名前空間(namespace)とcgroups による隔離
  2. Dockerデーモンの攻撃面 の最小化
  3. コンテナ構成のプロファイル によるリスク軽減
  4. カーネルハードニング機能(AppArmor, SELinux, seccomp)の活用

出典: Docker公式ドキュメント

この記事では、上記の4本柱を軸にしつつ、実際のCVE事例と具体的なDockerfileを交えて対策を整理します。

Dockerセキュリティの4本柱 — 名前空間/cgroups、デーモン攻撃面管理、コンテナ構成プロファイル、カーネルハードニング

実際に報告されたコンテナ攻撃事例

セキュリティ対策を理解するうえで、まず現実の脅威を把握しておくことが重要です。

CVE-2019-5736:runc経由のコンテナ脱出

コンテナランタイム runc のバグを悪用し、コンテナ内部からホストの runc バイナリを上書きできる脆弱性です。runc v1.0-rc6以前が影響対象で、コンテナがroot権限で動作している場合に攻撃が成立します。後述する非rootユーザー実行の重要性を裏付ける代表的な事例です。パッチ適用済みのバージョンへの更新が必須ですが、レガシー環境では依然として注意が必要です。

CVE-2022-0847(Dirty Pipe):カーネル脆弱性によるホスト侵害

Linux 5.8〜5.16.10のカーネルに存在した脆弱性で、非特権コンテナからでもホストの /usr/bin/sudo を上書きしてroot権限を奪取可能でした。コンテナの隔離はカーネル機能に依存しているため、ホストOSのカーネルを常に最新に保つことが必須です。

gh0stEdit:イメージ署名をすり抜けるレイヤー操作攻撃

2025年に発表された研究で示された手法で、Dockerイメージのレイヤーに悪意あるスクリプトを注入しつつ、マニフェストやイメージ署名を壊さないという特徴があります。Docker Content Trust(DCT)をパスしてしまうため、署名だけに依存しない多層防御が求められます。

CVE-2025-23266:GPU利用コンテナからのホスト侵害

NVIDIA Container Toolkit v1.17.7以前に存在した脆弱性です。enable-cuda-compat フックがコンテナの環境変数を継承してしまうため、LD_PRELOAD インジェクションでホストのroot権限を取得可能でした。AI/MLワークロードでGPUコンテナを利用している場合は、Toolkitのバージョンを確認してください(v1.17.8以降で修正済み)。

コンテナ攻撃のレイヤーと対応策 — イメージ・ランタイム・カーネル・デバイスの各層における攻撃例と防御策

Dockerfileで実装するセキュリティ対策

非rootユーザーでの実行

コンテナをroot権限で動作させると、コンテナ脱出時にホストOS上でもroot相当の操作が可能になります。専用ユーザーを作成し、最小権限で動作させることが基本です。

FROM node:22-alpine

# セキュリティ関連パッケージのインストールと不要キャッシュ削除
RUN apk add --no-cache dumb-init \
    && addgroup -S appgroup \
    && adduser -S appuser -G appgroup

WORKDIR /app
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:appgroup . .

USER appuser
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]

ポイントは3つです。

  • adduser -S でシステムユーザーを作成し USER 命令で切り替える
  • COPY --chown でファイル所有権を非rootユーザーに設定する
  • dumb-init をPID 1として使用し、シグナルハンドリングを正しく行う

ベースイメージの選定と最小化

イメージに含まれるパッケージが多いほど攻撃面が広がります。用途に応じた最小イメージを選択してください。

イメージ種別特徴サイズ目安適するケース
Alpine系musl libc + BusyBox。パッケージ数が少ない5〜10 MB汎用的な用途
slim系Debianベースから不要パッケージを除外70〜100 MBglibc依存がある場合
distrolessシェルもパッケージマネージャもない15〜25 MB本番実行専用
Chainguard ImagesゼロCVEを目指した軽量イメージ10〜30 MB高セキュリティ要件

distrolessイメージはシェルが存在しないため、コンテナ内部への侵入後の操作が極めて困難になります。デバッグが必要な場合は、マルチステージビルドで開発用ステージにのみシェルを含めるのが実用的です。

マルチステージビルドによる攻撃面の縮小

ビルドツールやソースコードを最終イメージに含めないことで、攻撃に利用されるツールを排除できます。

# ビルドステージ
FROM golang:1.23 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server .

# 実行ステージ(シェルなし)
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /app/server
USER nonroot:nonroot
ENTRYPOINT ["/app/server"]

ビルドステージにはコンパイラやパッケージマネージャが含まれますが、最終イメージにはバイナリのみがコピーされます。攻撃者がコンテナに侵入しても、curlwgetsh などのツールが存在しないため、追加の攻撃コードをダウンロードする手段がありません。

COPYとADDの使い分け

ADD 命令はアーカイブの自動展開とリモートURLからのダウンロード機能を持ちますが、これがセキュリティリスクになります。

  • Zip Bomb: 圧縮ファイルの自動展開時に、巨大なファイルが生成されてディスクを圧迫する
  • Zip Slip: パストラバーサル(../../etc/passwd)が含まれたアーカイブを展開し、想定外のディレクトリにファイルを配置される
  • リモートURL: TLS検証を明示的に制御できないため、中間者攻撃のリスクがある

原則として COPY を使用し、リモートからファイルを取得する必要がある場合は RUN curlRUN wget で明示的にダウンロードしてください。これにより、TLS証明書の検証やチェックサムの照合を制御できます。

シークレット情報の保護

Dockerfileや .env ファイルにAPIキーやパスワードを直接記述すると、イメージのレイヤー履歴に平文で残ります。docker history コマンドで誰でも閲覧可能になるため、本番環境では絶対に避けてください。

BuildKit のシークレットマウント(ビルド時)

# syntax=docker/dockerfile:1
FROM node:22-alpine
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    npm ci --only=production
docker build --secret id=npmrc,src=.npmrc -t myapp .

この方法では、シークレットはビルド中のみマウントされ、最終イメージのレイヤーには含まれません。

Docker Compose Secretsの活用(実行時)

services:
  web:
    image: myapp:latest
    secrets:
      - db_password
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

コンテナ内では /run/secrets/db_password にファイルとしてマウントされます。環境変数と異なり、docker inspect で値が表示されません。本番環境ではHashiCorp VaultやAWS Secrets Managerとの連携を検討してください。

イメージタグの固定とSHA256ピニング

latest タグはレジストリ上で上書きされるため、同じタグでも異なるイメージが配信される可能性があります。本番用Dockerfileではバージョンタグまたはダイジェスト(SHA256)で固定してください。

# バージョンタグによる固定
FROM node:22.14.0-alpine3.21

# SHA256ダイジェストによる厳密な固定
FROM node@sha256:abc123...(実際のダイジェスト値を使用)

SHA256ピニングはビット単位で同一のイメージを保証しますが、セキュリティパッチ適用時に手動でダイジェストを更新する運用コストが発生します。CI/CDパイプラインでダイジェストの自動更新をチェックする仕組みと併用するのが現実的です。

Dockerデーモンの保護

コンテナの設定を堅牢にしても、Dockerデーモン自体が攻撃者にアクセスされればすべてが無意味になります。デーモンはroot権限で動作し、ホストのファイルシステムやネットワークに完全なアクセス権を持つため、その保護は最優先事項です。

デーモンソケットの露出を防ぐ

/var/run/docker.sock をコンテナにマウントすると、そのコンテナからホスト上の全コンテナを制御できてしまいます。CIツールやモニタリングツールでソケットマウントが必要な場合は、読み取り専用マウント(:ro)やDocker APIプロキシ(Tecnativa/docker-socket-proxy)の利用を検討してください。

TLS認証の有効化

Dockerデーモンをリモートからアクセスする場合、Docker Engine 26でTLS未設定のTCP公開が非推奨となり、Engine 27以降では --tls=false--tlsverify=false を明示した場合にデーモンの起動が失敗するよう変更されています。リモートアクセスにはTLSクライアント証明書認証を設定するか、SSHトンネリングを使用してください。

# SSH経由でリモートDockerに接続(TLS設定不要)
export DOCKER_HOST=ssh://user@remote-host
docker ps

rootlessモードの採用

Docker Engine 19.03で実験的に導入され、20.10で正式サポートとなった rootless モードでは、Dockerデーモン自体が非root権限で動作します。万一デーモンに脆弱性が発見されても、ホストOS上でのroot権限は奪取されません。

# rootlessモードのセットアップ
dockerd-rootless-setuptool.sh install

User Namespace Remapping と併用することで、コンテナ内の UID 0 がホスト上では非特権UID(例: 100000)にマッピングされ、二重の防御層が形成されます。daemon.json に以下を追加します。

{
  "userns-remap": "default"
}

カーネルレベルのセキュリティ機能

Dockerのコンテナ隔離はLinuxカーネルの機能に依存しています。カーネルレベルの設定を活用することで、コンテナからの脱出や権限昇格のリスクを大幅に低減できます。

Linux Capabilities の制御

従来のUNIXではroot権限が全能でしたが、Linux Capabilitiesにより権限を細分化して必要な権限だけを付与できます。

# 全権限を剥奪してから必要な権限のみ追加
docker run --cap-drop=ALL \
           --cap-add=NET_BIND_SERVICE \
           myapp

Webサーバーで1024番未満のポートをバインドするには NET_BIND_SERVICE だけで十分です。SYS_ADMINNET_RAW(パケットスプーフィングに悪用される)は明示的にdropしてください。

実用的な権限監査のワークフロー:

  1. まず --cap-add=ALL で全権限を付与して動作確認する
  2. コンテナ内で capsh --print を実行し、実際に使われる権限を調査する
  3. --cap-drop=ALL に切り替え、必要な権限のみを --cap-add で追加する

seccompプロファイルによるシステムコール制限

seccomp(Secure Computing)は、コンテナが呼び出せるシステムコールをBPFフィルタで制限する仕組みです。Dockerにはデフォルトのseccompプロファイルが同梱されており、rebootclock_settimemount などの危険なシステムコールを約44件ブロックしています。

カスタムプロファイルを作成すれば、アプリケーションに必要なシステムコールだけを許可するホワイトリスト方式が可能です。

docker run --security-opt seccomp=custom-profile.json myapp

–no-new-privilegesフラグ

コンテナ起動後にsetuidバイナリによる権限昇格を防止するオプションです。Docker Composeでは以下のように設定します。

services:
  web:
    image: myapp:latest
    security_opt:
      - no-new-privileges

脆弱性スキャンとサプライチェーン防御

脆弱性スキャンツールの比較

ツール提供元検出対象CI/CD統合特徴
Docker ScoutDocker社OS + アプリ依存GitHub Actions対応Docker Desktop統合
TrivyAqua SecurityOS + アプリ + IaC多数対応OSSで高速
Snyk ContainerSnykOS + アプリ依存多数対応修正提案あり
GrypeAnchoreSBOM消費型CLISyftのSBOMと連携

Docker Scoutの基本的な使い方:

# ローカルイメージのスキャン
docker scout cves myapp:latest

# 推奨ベースイメージの提案
docker scout recommendations myapp:latest

SBOM(ソフトウェア部品表)の生成と検証

SBOMは、イメージに含まれる全パッケージの一覧を機械可読な形式で出力したものです。脆弱性が発見された際に「自社のイメージに影響があるか」を即座に判定するために不可欠です。

# Syftを使ったSBOM生成(CycloneDX形式)
syft myapp:latest -o cyclonedx-json > sbom.json

# 生成したSBOMをGrypeで脆弱性チェック
grype sbom:sbom.json

米国では2021年の大統領令(EO 14028)により連邦政府調達においてSBOM提出が義務化されており、日本でも経済産業省がSBOM導入の手引きを公開するなど、対応の優先度が高まっています。

KubernetesにおけるイメージポリシーのAdmission Controller

Docker Content Trust(DCT)やCosignで署名を付与しても、Kubernetesは標準ではイメージ署名を検証しません。署名付きイメージのみをデプロイさせるには、Admission Controllerの導入が必要です。

  • Connaisseur: DCT(Notary v1)とCosign両方の署名検証に対応するOSSのAdmission Controller
  • Sigstore Policy Controller: Sigstoreエコシステムに統合されたポリシーエンジン

さらに、Open Policy Agent(OPA)とRegoポリシーを組み合わせれば、「latest タグの禁止」「特定レジストリからのイメージのみ許可」といった宣言的なポリシーをアプリケーションコードの修正なしで適用できます。

ネットワークとランタイムの保護

コンテナネットワークの分離

Dockerのデフォルト設定ではすべてのコンテナが同一ブリッジネットワーク上で通信可能です。本番環境では用途ごとにネットワークを分離し、不要な通信経路を遮断してください。

# docker-compose.yml
services:
  web:
    networks:
      - frontend
  api:
    networks:
      - frontend
      - backend
  db:
    networks:
      - backend

networks:
  frontend:
  backend:
    internal: true  # 外部からのアクセスを遮断

データベースコンテナを internal: true のネットワークに配置すれば、インターネットからの直接アクセスが遮断されます。

ファイルシステムの読み取り専用化

コンテナのルートファイルシステムを読み取り専用にすることで、攻撃者がマルウェアをディスクに書き込む手段を排除できます。

docker run --read-only \
           --tmpfs /tmp:rw,noexec,nosuid \
           --tmpfs /var/run:rw,noexec,nosuid \
           myapp

アプリケーションが一時ファイルを必要とする場合は、--tmpfs で特定ディレクトリのみ書き込み可能にしてください。noexec オプションを付けることで、そのディレクトリでの実行ファイル起動も防止できます。

リソース制限の設定

コンテナに対するCPU・メモリの制限がない場合、1つのコンテナが暴走してホスト全体のリソースを枯渇させる(DoS攻撃)リスクがあります。

services:
  web:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
        reservations:
          cpus: '0.25'
          memory: 128M
    pids_limit: 100  # fork bomb対策

pids_limit はコンテナ内のプロセス数を制限する設定で、フォーク爆弾(fork bomb)への対策として有効です。

ランタイム監視とログ収集

コンテナのランタイム動作を監視することで、不正なプロセス実行やファイル改ざんを検知できます。

  • Falco: eBPFベースでシステムコールをリアルタイム監視し、異常を検知するオープンソースツール。「コンテナ内でshが実行された」「/etc配下が書き換えられた」といったイベントをアラートとして発報します
  • Docker logging driver: fluentdjson-file ドライバを設定し、Elasticsearch + Kibanaで可視化する構成が一般的です

高セキュリティ要件が求められるマルチテナント環境では、gVisor(システムコールをカーネル到達前にインターセプトするサンドボックスランタイム)やKata Containers(コンテナごとに軽量VMを起動するハードウェア仮想化ベースの隔離)を検討してください。

Dockerfileの静的解析(Linting)

Dockerfileに含まれるセキュリティ上の問題やベストプラクティス違反を自動的に検出するツールとして、hadolintが広く利用されています。

# hadolintの実行例
docker run --rm -i hadolint/hadolint < Dockerfile

検出される主な問題:

  • DL3002: USERが指定されていない(root実行リスク)
  • DL3003: ADDの使用(COPYへの変更を推奨)
  • DL3008: パッケージバージョンが固定されていない
  • DL3018: Alpine系で --no-cache が指定されていない

CI/CDパイプラインにhadolintを組み込み、問題のあるDockerfileのマージを自動的にブロックすることで、セキュリティ設定を開発プロセスの早期段階(シフトレフト)で検証できます。

セキュリティ対策チェックリスト

本番環境にコンテナをデプロイする前に確認すべき項目を、フェーズ別に整理します。

ビルドフェーズ

  • ベースイメージは公式イメージまたはVerified Publisherから取得しているか
  • イメージタグはバージョン固定またはSHA256ピニングされているか
  • USER 命令で非rootユーザーが指定されているか
  • マルチステージビルドで不要なツール・ソースが除外されているか
  • シークレット情報は --mount=type=secret で渡しているか
  • ADD ではなく COPY を使用しているか
  • hadolintによる静的解析がCIに組み込まれているか

デプロイフェーズ

  • Docker Scout / Trivy / Snykで脆弱性スキャンが実行されているか
  • SBOMが生成・保存されているか
  • イメージ署名(DCTまたはCosign)が付与されているか
  • Kubernetes環境ではAdmission Controllerで署名検証されているか

ランタイムフェーズ

  • --cap-drop=ALL で全権限を剥奪し、必要なものだけ追加しているか
  • --no-new-privileges が設定されているか
  • ルートファイルシステムは --read-only になっているか
  • CPU・メモリ・PIDの制限が設定されているか
  • コンテナネットワークが用途ごとに分離されているか
  • Falcoなどのランタイム監視が有効になっているか

インフラフェーズ

  • Dockerデーモンのソケットはコンテナにマウントされていないか
  • リモートアクセスはTLSまたはSSH経由か
  • ホストOSのカーネルが最新のセキュリティパッチ適用済みか
  • rootlessモードまたはUser Namespace Remappingが有効か

まとめ

Dockerのセキュリティは、イメージ構築時の設定だけで完結するものではありません。デーモンの保護、カーネルレベルのハードニング、ランタイム監視、サプライチェーン全体の検証を組み合わせた多層防御が求められます。

Red Hatが2024年に公開した「State of Kubernetes Security」レポートでは、DevOps・セキュリティ担当者600名のうち42%が「コンテナおよびKubernetesのセキュリティに対応する十分な能力を持っていない」と回答しています(出典: Red Hat)。IBMの「Cost of a Data Breach 2025」レポートでは、認証情報の漏洩を起因とする侵害の平均被害額は467万ドルに達しており(出典: IBM)、コンテナのシークレット管理やアクセス制御の重要性が数字で裏付けられています。

まずは非rootユーザー実行とベースイメージの最小化から着手し、CI/CDパイプラインに脆弱性スキャンとhadolintを組み込んでください。そこから段階的にデーモン保護やseccompプロファイルの導入へ進めていくことで、運用負荷を抑えながらセキュリティレベルを引き上げられます。