コンテナの「中身」を信頼するな。TrivyとSBOMで守るサプライチェーンの防壁
現場で手を動かしているエンジニア諸君、お疲れ様。
「Dockerイメージを作ってCI/CDに流せば終わり」と思っているなら、今すぐ考えを改めたほうがいい。現代のインフラにおいて、コンテナイメージは「ブラックボックス」だ。OSレベルの脆弱性はもちろん、アプリケーションが依存しているライブラリの裏で、誰がどんなコードを混入させたか、君は本当に把握しているか?
最近の攻撃者は、アプリケーションそのものを直接狙うよりも、信頼されているサプライチェーン(OSSライブラリやベースイメージ)に潜り込む手法を好む。今日は、TrivyによるスキャンとSBOM(ソフトウェア部品表)を活用して、この泥沼から抜け出すための具体的な実装術を伝授する。
—
なぜ「脆弱性スキャン」だけでは不十分なのか
多くの現場では trivy image <image_name> をCIに組み込んで満足している。だが、それは「今、何が入っているか」をスキャンしているに過ぎない。
本当の恐怖は、「ある日突然、過去のビルドに含まれていたライブラリに致命的な脆弱性(CVE)が見つかったとき」に訪れる。ビルド済みイメージを全て引き剥がして再構築できる準備はできているか? そのための地図がSBOMだ。
実践:SBOMを活用した可視化のフロー
SBOMは、コンテナ内に何が含まれているかの「成分表示」だ。これを生成・保存しておくことで、CVEが公開された瞬間、どのイメージを修正すべきかが一瞬で特定できる。
以下は、CI/CDパイプライン(GitHub Actions例)に組み込むべき設定の断片だ。
# .github/workflows/security.yml
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Trivyを使ってSBOMを生成し、スキャンを実行
- name: Run Trivy with SBOM generation
run: |
# CycloneDX形式でSBOMを出力し、ついでに脆弱性もチェック
trivy image --format cyclonedx --output sbom.json my-app:latest
trivy image --exit-code 1 --severity CRITICAL my-app:latest
env:
TRIVY_USERNAME: ${{ secrets.DOCKER_USER }}
TRIVY_PASSWORD: ${{ secrets.DOCKER_PASS }}
—
攻撃者の視点:脆弱なライブラリが招く「PoC」の現実
例えば、古い python の requests ライブラリや、Node.js の lodash 等に脆弱性があるとしよう。攻撃者はここを突いて、コンテナ内で任意のコマンドを実行させる。
もし君のアプリケーションでユーザーからの入力をそのまま eval() に渡したり、動的なライブラリ読み込みを行っている場合、コンテナ内での RCE(リモートコード実行)は容易だ。
防御の鉄則:不要な機能は削ぎ落とせ
ハーデニングの基本は「攻撃対象領域(Attack Surface)を最小化する」ことだ。特に distroless イメージを採用し、シェルやパッケージマネージャすら含めないのが現代の定石だ。
# セキュアなマルチステージビルドの例
# ビルド環境
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o myapp .
# 実行環境(Distroless: シェルも入っていない最小構成)
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/myapp /myapp
# ここには /bin/sh すら存在しないため、攻撃者は侵入後も何もできない
USER nonroot:nonroot
ENTRYPOINT ["/myapp"]
—
実務で役立つTips:WAFと連携した検知の自動化
SBOMを生成するだけではインシデントは防げない。運用中のコンテナが攻撃されているかどうかを判断するためには、NginxやWAFのログと紐付ける必要がある。
もし君が Nginx をリバースプロキシとして使っているなら、以下のようにヘッダーを付与して、バックエンドのコンテナIDを追跡できるようにしておくと、調査時の泥臭い作業が劇的に減る。
# nginx.conf
server {
listen 80;
location / {
# どのコンテナが処理したかを識別(デバッグ・フォレンジック用)
add_header X-Container-ID $hostname;
proxy_pass http://backend-app:8080;
}
}
—
最後に:エンジニアとしてのマインドセット
セキュリティを「足枷」と捉えるか、「プロダクトの信頼性という付加価値」と捉えるかで、君のエンジニアとしての価値は変わる。
1. SBOMを資産にせよ: ビルドごとにSBOMをS3やGitHub Releaseに保存しろ。脆弱性通知が来た時、それを検索するだけで君の仕事は終わる。
2. 「動けばいい」を捨てる: Dockerfile に apt-get upgrade を書くような構成ではなく、ベースイメージを定期的に更新し、スキャンに引っかかるイメージはデプロイを遮断する強い意志を持て。
3. 継続的な学習: 脆弱性情報は日々変わる。CVE-2024-xxxx といったIDを追うだけでなく、なぜその脆弱性が生まれたのかという「根本原因」を理解する癖をつけろ。
完璧な防御は存在しない。しかし、「攻撃者が侵入したあとに苦労する環境」を作ることこそが、我々エンジニアの腕の見せ所だ。
さあ、次は君たちのコードをよりセキュアなものへ書き換える番だ。準備はいいか?
コメント