コンテナの「信頼」は幻想だ:レジストリ汚染からサプライチェーンを守るための実戦的防衛術
「Docker Hubから有名なイメージを引っ張ってきたから大丈夫」——そう思っているなら、今すぐその認識を改めたほうがいい。
現場のペネトレーションテストで見ていると、攻撃者は今、アプリケーションのバグを突くよりも、もっと効率的で静かなルートを好む。それがサプライチェーン攻撃だ。CI/CDパイプラインという「聖域」に、毒入りのイメージを忍び込ませる。一度汚染されたイメージがデプロイされれば、Webアプリのコードがどれほど堅牢でも、インフラレベルでバックドアを設置される。
今日は、攻撃者がどのようにしてレジストリを汚染し、我々はどうやってそれを「技術的強制力」で封じ込めるべきか、その泥臭い現実を解説する。
—
1. 攻撃者が狙う盲点:タグの「流動性」と信頼の欠如
攻撃者の常套手段は、latestタグのような「流動的なタグ」の付け替えだ。多くの開発環境では、docker pull node:latest のようにイメージを取得する。もし攻撃者がレジストリの認証情報を奪取したり、中間者攻撃(MITM)を仕掛けられたりすれば、latestタグを悪意あるイメージにすり替えるのは造作もない。
また、Dockerfileの冒頭にある FROM python:3.9-slim も油断ならない。公式イメージになりすましたタイポスクワッティング(例: pyth0n-official)や、GitHubのプロジェクトが乗っ取られて悪意あるレイヤーが追加されるケースも枚挙に暇がない。
2. 対策の核:Sigstore/Cosignによるデジタル署名
「信頼できないものは実行しない」を自動化する最強の手段が、Cosignによる署名検証だ。イメージが「誰によって作られ、改ざんされていないか」を暗号学的に証明する。
実践:Cosignによる署名と検証のフロー
まずは、コンテナをビルドするCIパイプラインで署名を行う必要がある。
# 1. 鍵ペアの生成(秘密鍵はGitHub Secrets等に厳重保管せよ)
cosign generate-key-pair
# 2. イメージに署名を付与してレジストリへプッシュ
cosign sign --key cosign.key my-registry.com/my-app:v1.0.0
ここで重要なのは、「署名のないイメージは実行させない」というポリシーをKubernetes側で強制することだ。これには Kyverno や Policy Controller を使用する。
Kyvernoによる防御設定(YAML)
Kubernetesクラスターに以下のポリシーを適用すれば、署名が確認できないイメージはPodの生成段階で弾かれるようになる。
# cluster-policy.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce # 検証失敗時はデプロイを拒否する
rules:
- name: check-image-signature
match:
resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "my-registry.com/my-app:*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
(ここに公開鍵を貼り付ける)
-----END PUBLIC KEY-----
—
3. なぜ「静的解析」だけでは不十分なのか
多くの現場で「イメージスキャンツール(Trivyなど)をCIに入れているから安心」という声を聞く。確かにTrivyは素晴らしいが、それは「既知の脆弱性(CVE)」を探すものであり、「意図的に仕込まれたバックドア」は検知できない。
攻撃者は、Goのランタイムやライブラリに難読化されたバックドアを埋め込み、特定の環境変数やリクエストヘッダが来た時だけ起動するように細工する。このレベルの脅威に対抗するには、以下の運用ルールを徹底してほしい。
現場で守るべき「コンテナ運用3箇条」
1. タグの固定 (Digest指定):
FROM node:18 ではなく FROM node:18@sha256:abcdef... を使うこと。タグは名前であり、ダイジェスト(ハッシュ値)こそが実体だ。
2. マルチステージビルドの厳格化:
実行環境(ランタイム)には、ビルドツールや不要なシェルを含めない。distroless イメージを採用し、攻撃の足場を最小限に削る。
3. 最小権限のPod Security:
コンテナを root で動かすな。Dockerfile内で USER 1000:1000 を指定し、読み取り専用のファイルシステム (readOnlyRootFilesystem: true) を強制せよ。
4. 最後に:セキュリティは「性悪説」で設計せよ
サプライチェーン攻撃の恐ろしさは、それが「信頼の連鎖」を逆手に取る点にある。君たちが信頼しているGitHubのAction、信頼している公式イメージ、それらすべてが汚染される可能性があるという前提で設計を組んでほしい。
何か新しいライブラリを入れる時、あるいはCIを構築する時、常に自問自答してほしい。「もし、このツール自体が攻撃者の手に落ちていたら、自分のシステムを守り抜けるか?」と。
堅牢なシステムは、コードの美しさではなく、「裏切られた時にどれだけ被害を小さくできるか」という疑念の積み重ねから生まれる。今日紹介したCosignによる署名検証は、そのための第一歩だ。まずは開発環境の小さなパイプラインから導入してみることを強く勧める。
さあ、コードを書こう。ただし、疑い深さを忘れずに。
コメント