CI/CD
CI でイメージを安全性チェック
Trivy / Grype による脆弱性スキャンと hadolint による Dockerfile lint を CI に組み込み、リスクの高い変更を自動で止めます。
スキャンの 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.sarifTrivy でイメージを脆弱性スキャン
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.sarifGrype との使い分け
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-actionのformat: table+github-scriptで PR に貼れば、レビュアーが気づきやすい mode: imageとmode: fsを使い分け: 開発初期はfsでソース時点の依存をスキャン(ビルドなしで高速)、本番向けはimageで実イメージをスキャン- スキャンのタイミング: PR で先にブロック、main へのマージ時にもう一度、本番デプロイ前にも最新 DB で再スキャン。3 回やるのが鉄則(依存ツリーは日々変化する)
- Dockerfile の改善が最大の対策:
-slim/-alpine/distrolessに変えるだけで脆弱性件数が 1/5〜1/10 になることも。スキャン結果ばかり追うより、まずベースイメージを小さくする
