Dock Stay
CI でイメージを安全性チェック
CI/CD

CI でイメージを安全性チェック

Trivy / Grype による脆弱性スキャンと hadolint による Dockerfile lint を CI に組み込み、リスクの高い変更を自動で止めます。

CI でイメージを安全性チェック diagram

スキャンの 3 レイヤー

コンテナのセキュリティチェックは目的が違う 3 レイヤーに分かれます。

レイヤー ツール 何を見つけるか
Dockerfile lint hadolint apt-get--no-install-recommends 抜け、ADD の誤用、USER 指定なし、:latest タグ使用、など 書き方の問題
脆弱性スキャン Trivy / Grype ベースイメージやアプリ依存に含まれる既知 CVE。OS パッケージ(apk / apt / yum)と言語エコシステム(npm / pip / gem 等)両方
構成スキャン Trivy config, Checkov Dockerfile や Compose ファイル自体のセキュリティ問題(root で動く、secret ハードコード等)

最低限 hadolint + Trivy を CI に入れれば、Docker に慣れていないチームでも致命的な問題の大半を事前に検出できます。

hadolint で Dockerfile を lint する

hadolint は Dockerfile 専用の静的解析ツール。

デフォルトのルールが実用的で、.hadolint.yaml でプロジェクトに合わせた例外設定もできます。

bash
# .github/workflows/lint.yml
- uses: hadolint/hadolint-action@v3.1.0
  with:
    dockerfile: Dockerfile
    failure-threshold: warning   # error/warning/info/style
    format: sarif                # GitHub Advanced Security に取り込める
    output-file: hadolint.sarif

- if: always()
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: hadolint.sarif

Trivy でイメージを脆弱性スキャン

Trivy は OS パッケージと言語エコシステムを横断してスキャンでき、SARIF 出力で GitHub Security タブにも取り込めます。

次の例はビルド済みイメージをスキャンし、CRITICAL / HIGH があれば CI を落とす構成です。

bash
- uses: aquasecurity/trivy-action@0.28.0
  with:
    image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
    format: sarif
    output: trivy.sarif
    severity: CRITICAL,HIGH
    ignore-unfixed: true        # 修正 PR が出ていない脆弱性は除外(運用で判断)
    exit-code: 1                 # 発見時は CI を落とす
    vuln-type: os,library

- if: always()
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy.sarif

Grype との使い分け

Grype(Anchore)は Trivy のライバルで、同じ領域をカバーします。

どちらを選ぶかは好みの部分もありますが、以下で使い分けると良いです。

  • Trivy: OS パッケージと言語ランタイムの両方を厚く見る。設定ファイルスキャン(IaC)も含む総合パック。最初の選択肢
  • Grype: SBOM を syft で作ってから grype sbom:./sbom.json と食わせる運用が綺麗。SBOM 中心の運用にフィットする

両方を並行して動かすのも悪くありません。

検出漏れのクロスチェックになります。

運用上のコツ

  • ignore-unfixed: true を使うかは状況次第: CVE データベース上で未修正(アップストリームにもパッチがない)の脆弱性を CI で止めても、開発者は対応不能。通常は除外するのが現実的。ただし高リスク脆弱性は別途把握する
  • .trivyignore / .grype.yaml で抑制管理: 抑制する場合は 理由と期限 をコメントで書く。「なぜ無視して OK か」の文脈がないと監査で詰む
  • PR コメントに脆弱性サマリを出す: aquasecurity/trivy-actionformat: table + github-script で PR に貼れば、レビュアーが気づきやすい
  • mode: imagemode: fs を使い分け: 開発初期は fs でソース時点の依存をスキャン(ビルドなしで高速)、本番向けは image で実イメージをスキャン
  • スキャンのタイミング: PR で先にブロック、main へのマージ時にもう一度、本番デプロイ前にも最新 DB で再スキャン。3 回やるのが鉄則(依存ツリーは日々変化する)
  • Dockerfile の改善が最大の対策: -slim / -alpine / distroless に変えるだけで脆弱性件数が 1/5〜1/10 になることも。スキャン結果ばかり追うより、まずベースイメージを小さくする