コンテナの「中身」は腐っているかもしれない:CI/CDで脆弱性を即死させるガバナンス術
現場でコードを書いていると、「動けば正義」という誘惑に駆られる瞬間があるだろう。しかし、セキュリティの現場に長くいる人間から言わせれば、その「動いているコンテナ」こそが、攻撃者にとっての格好の入り口だ。
OSのハーデニングをどれだけ完璧に施しても、アプリが依存しているライブラリの中に数年前のCVE(脆弱性)が眠っていれば、脆弱なコンテナを動かしているのは火薬庫の上で焚き火をしているのと同じだ。今日は、Trivyを使ってビルドパイプラインを「検問所」に変える、泥臭くも現実的な実装方法を伝授する。
1. なぜ「スキャン」だけでは不十分なのか
多くの現場が陥る罠がこれだ。「スキャンツールを導入した」だけで満足し、検知された脆弱性を放置すること。脆弱性スキャンは、ただの「通知機能」ではない。脆弱なイメージを本番環境に到達させないための物理的なブレーキであるべきだ。
例えば、npmやpipで適当な古いパッケージをインストールしたコンテナイメージ。攻撃者はこれに対し、Metasploitやカスタムスクリプトを使い、既知のRCE(リモートコード実行)を仕掛けてくる。一度シェルを奪われれば、ホストOSへのエスケープや、IAMロールを悪用したクラウド環境の全掌握は時間の問題だ。
2. CI/CDパイプラインへの強制統合(GitHub Actions編)
「脆弱性があったらビルドを失敗させる」。これが鉄則だ。以下は、Trivyを使って高リスク(CRITICAL/HIGH)な脆弱性が見つかった場合に、デプロイプロセスを強制停止させるGitHub Actionsの構成例だ。
# .github/workflows/docker-build.yml
name: Build and Secure Scan
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker Image
run: docker build -t my-app:${{ github.sha }} .
# ここでTrivyを動かす。exit-code 1を設定するのが最大のポイント
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
format: 'table'
# CRITICALとHIGHの脆弱性があればビルドを強制終了(exit 1)
exit-code: '1'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
# スキャンをパスした場合のみPushする
- name: Push to Registry
if: success()
run: docker push my-app:${{ github.sha }}
この設定の肝は exit-code: '1' にある。CIツールが「失敗」と認識することで、脆弱なイメージがレジストリにプッシュされるのを物理的に遮断する。現場では「開発が止まるから」という文句が出るかもしれない。だが、インシデント対応で徹夜することに比べれば、ビルド失敗は安すぎるコストだ。
3. ハーデニングの基本:ベースイメージを「削ぎ落とせ」
そもそも、スキャンで引っかかる脆弱性の多くは「使っていないOSのツール」に由来する。curlやwget、netcatなどが不要なら、イメージから消し去るべきだ。
Dockerfileの書き方を工夫するだけで、攻撃の足がかり(Attack Surface)を劇的に減らせる。
# 悪い例: ubuntuのようなフルOSイメージをベースにしている
# FROM ubuntu:latest
# 良い例: distrolessを使うことで、シェルさえ存在しない最小構成にする
# これにより、攻撃者が侵入してもコマンドが打てない(lsすら存在しない)
FROM gcr.io/distroless/nodejs-debian12:latest
WORKDIR /app
COPY ./dist /app
# 実行ユーザーをroot以外にする(必須)
USER 1000
CMD ["index.js"]
distrolessを採用すれば、OS層の脆弱性は極小化される。シェルがなければ、攻撃者がシェルコードを送り込んでも実行する環境がないのだ。これは最強のハーデニングと言える。
4. 最後に:現場を動かすための「説得力」
技術的な実装以上に重要なのが、チーム内での「合意形成」だ。セキュリティは足枷ではない。あなたのコードが、あなたの作ったサービスが、攻撃者によって改竄され、顧客のデータを流出させる事態を防ぐための「保険」だ。
もし、開発チームから「修正が面倒だ」と言われたら、こう言い返してほしい。
「脆弱なライブラリを使い続けるのは、鍵の壊れた玄関に住み続けるのと同じだ。今は運良く泥棒が入っていないだけで、次は君の作ったロジックが踏み台にされる番かもしれないぞ」と。
我々の仕事は、ただ動くものを作ることではない。「いつ攻撃されても安全な状態を維持し続けること」だ。今日紹介したTrivyによるCI/CDガバナンスと、distrolessの導入、これだけで君たちのシステムは上位1%の堅牢なインフラへと生まれ変わるはずだ。
次は、この先にある「実行時のランタイム監視(Falco等)」について話そう。まずはこの「入り口」の門番を徹底することから始めてくれ。期待している。
コメント