Trivy・Grypeでコンテナイメージ脆弱性スキャンを自動化:SBOM生成・CI/CD・Kubernetes実践ガイド

Trivy・Grypeでコンテナイメージの脆弱性スキャンを自動化する実践ガイド。SBOM生成、CI/CD統合、Kubernetes admission、Cosign署名まで、2026年CRA対応の運用パターンを動くコマンドで解説します。

Trivy・Grype脆弱性スキャン実践ガイド 2026

更新日: 2026年6月30日

Trivy・Grypeは、コンテナイメージに含まれるOSパッケージや言語ライブラリの既知脆弱性(CVE)を高速にスキャンするオープンソースツールで、CI/CDパイプラインに組み込むことで本番環境へ脆弱なイメージを到達させずに済みます。本ガイドでは、2026年時点でEU Cyber Resilience Act(CRA)対応として要求されるSBOM(Software Bill of Materials)生成、GitHub Actions/GitLab CIへの統合、Kubernetes Admission Controllerによる強制ポリシーまでを、実際に動くコマンドと設定例で解説します。正直なところ、最初は「スキャナを動かすだけ」と思っていたのですが、実運用ではVEXやadmissionまで含めないと形骸化してしまうのが現実でした。

  • Trivy 0.59以降とGrype v0.85以降は、OS層・言語層・IaCの脆弱性に加えてmalwareスキャンとSecrets検出に対応している。
  • SBOMはCycloneDX 1.6またはSPDX 2.3形式で生成し、CRA・米国EO 14028・NTIA最小要素を満たす必要がある。
  • CI/CDではビルド時に--exit-code 1 --severity HIGH,CRITICALを指定し、脆弱性が見つかればパイプラインを停止させるのが基本パターン。
  • Kubernetes側ではKyverno・OPA Gatekeeperで「scanされていないイメージ」「Critical脆弱性を含むイメージ」のadmissionを拒否する。
  • Cosign + Rekorによる署名検証と組み合わせ、SBOM・スキャン結果をattestationとしてOCIレジストリに保管する運用が2026年標準。
  • SBOM単体では防御になりません。トリアージ運用(VEX、SLSA Provenance)と組み合わせて初めて意味を持ちます。

なぜコンテナイメージの脆弱性スキャンが必要か

2024年のSonatype State of the Software Supply Chain Reportによれば、本番で稼働するコンテナイメージのおよそ80%が「CriticalもしくはHighの既知脆弱性」を1件以上含んでおり、その大半はベースイメージのglibc・openssl・zlibといった共有ライブラリに由来します。これらは開発者が書いたコードではなく、FROM debian:12FROM python:3.13-slimのように選んだベースイメージから自動的に持ち込まれるため、Dockerfileを書く段階では気付きにくいのが特徴です。

2026年に強制適用が始まったEU Cyber Resilience Act(CRA)と、米国大統領令EO 14028の延長施策により、コンテナイメージに対しても「製造者は既知の脆弱性を追跡し、SBOMを利用者に提供する」ことが事実上の義務となりました。これに対応するもっとも軽量かつ実装しやすい選択肢が、ローカルでもCIでも数秒で動くTrivyとGrypeです。スキャンをeBPFランタイムセキュリティ(FalcoやTetragon)と組み合わせることで、ビルド時(shift-left)と実行時(runtime)の両面からカバーできます。

TrivyとGrypeの違いは何か

TrivyとGrypeはどちらも代表的なオープンソース脆弱性スキャナですが、設計思想と得意領域に明確な違いがあります。Trivy(Aqua Security製)はオールインワン型で、コンテナ・ファイルシステム・Gitリポジトリ・Kubernetesクラスタ・IaC(Terraform/CloudFormation/Helm)・Secrets・ライセンスまで単一バイナリでカバーします。一方Grype(Anchore製)はSyftが生成したSBOMを基盤に脆弱性マッチングだけを行う設計で、SBOMファーストの運用と相性が良く、CycloneDX/SPDXへの忠実性が高いのが強みです。

項目Trivy 0.59+Grype v0.85+
開発元Aqua SecurityAnchore
スキャン対象OS・言語・IaC・K8s・Secrets・ライセンスOS・言語(SBOM経由)
SBOM生成内蔵(CycloneDX, SPDX, Trivy JSON)Syftと連携(標準分業)
脆弱性DBTrivy DB(OCI artifactとして配布)Grype DB(毎日更新)
VEX対応OpenVEX, CSAF VEXOpenVEX
初回スキャン速度中規模イメージで約3〜8秒SBOMがあれば1〜2秒
K8sクラスタスキャンtrivy k8sで標準対応外部ツール(Anchore Enterprise)必要
Air-gapped運用DBをoras pullで取得可能grype db updateのオフラインモード

実務上は「Trivyを主に使い、CycloneDX SBOMの厳密性が必要な箇所(CRA提出やSPDX署名)でSyft+Grypeを使い分ける」のが2026年のベストプラクティスです。両者を併用してもライセンスはApache-2.0なので商用利用に問題はありません。

TrivyとGrypeのインストールと基本スキャン

RHEL系(Rocky Linux 9 / AlmaLinux 9 / RHEL 9)でのインストールは、いずれも公式のyumリポジトリ追加で完了します。Debian/Ubuntuの場合はaptリポジトリを追加してください。

# Trivy のインストール(Rocky Linux 9 / RHEL 9)
sudo tee /etc/yum.repos.d/trivy.repo <<'EOF'
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/9/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://aquasecurity.github.io/trivy-repo/rpm/public.key
EOF
sudo dnf install -y trivy

# Grype と Syft のインストール(公式インストールスクリプト)
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | \
  sudo sh -s -- -b /usr/local/bin
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | \
  sudo sh -s -- -b /usr/local/bin

# バージョン確認
trivy --version
grype version
syft version

インストール後、まずは公式イメージに対して基本スキャンを実行し、出力形式を体感しておくと後の運用設計が早くなります。

# Trivy:HIGH と CRITICAL のみ出力し、検出されたら exit code 1 を返す
trivy image \
  --severity HIGH,CRITICAL \
  --ignore-unfixed \
  --exit-code 1 \
  --format table \
  python:3.13-slim

# Grype:同じイメージを Syft 経由でスキャン
syft python:3.13-slim -o cyclonedx-json=sbom.cdx.json
grype sbom:sbom.cdx.json --fail-on high

--ignore-unfixedはupstreamで未修正の脆弱性(ベンダーが「Will Not Fix」と判定したものを含む)をノイズから除外する重要なフラグです。これを付けないとDebian系イメージで数百件の表示になり、本当に対応が必要な脆弱性が埋もれます。

SBOM生成:CycloneDXとSPDXの実践

SBOMはコンテナイメージに含まれる全コンポーネント(OSパッケージ、言語ライブラリ、ファイル)を機械可読形式で記述した「部品表」です。CRAやNTIAが要求する最小要素には、サプライヤー名・コンポーネント名・バージョン・依存関係・ハッシュ・タイムスタンプが含まれます。2026年現在の業界標準はCycloneDX 1.6(OWASP管理)とSPDX 2.3(Linux Foundation管理)の2つで、用途によって使い分けます。

# Trivy で CycloneDX 1.6 形式の SBOM を生成
trivy image \
  --format cyclonedx \
  --output app.cdx.json \
  myregistry.example.com/app:1.4.2

# Syft で SPDX 2.3 形式の SBOM を生成(CRA 提出向け)
syft myregistry.example.com/app:1.4.2 \
  -o spdx-json=app.spdx.json

# 生成済み SBOM を後から再スキャン(脆弱性 DB は更新される)
grype sbom:app.cdx.json -o sarif > app.sarif.json
trivy sbom app.spdx.json --severity CRITICAL

SBOMはOCIレジストリにartifactとして添付するのが標準的です。Docker Hub・GHCR・Harbor v2.10以降はOCI 1.1のreferrers APIに対応しており、cosign attach sbomまたはoras attachでイメージタグに紐付けられます。詳細はCycloneDX公式仕様SPDX 2.3仕様書を参照してください。

CI/CDパイプラインへの組み込み

脆弱性スキャンはローカルではなく、CI上で毎ビルド自動実行することで初めて防壁として機能します。GitHub ActionsとGitLab CIの最小構成を以下に示します。どちらもSARIF形式で結果を出力し、GitHubのSecurityタブまたはGitLabのDependency Scanning画面に統合されます。

GitHub Actions(Trivy + SARIF アップロード)

# .github/workflows/container-scan.yml
name: container-scan
on:
  push:
    branches: [main]
  pull_request:

jobs:
  scan:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      security-events: write   # SARIF アップロードに必須
      id-token: write          # OIDC で cosign 署名する場合
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t local/app:${{ github.sha }} .

      - name: Trivy scan (fail on HIGH/CRITICAL)
        uses: aquasecurity/[email protected]
        with:
          image-ref: local/app:${{ github.sha }}
          format: sarif
          output: trivy.sarif
          severity: HIGH,CRITICAL
          ignore-unfixed: true
          exit-code: 1

      - name: Upload SARIF to GitHub Security
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy.sarif

GitLab CI(Grype + SARIF レポート)

# .gitlab-ci.yml
container_scan:
  stage: test
  image:
    name: anchore/grype:v0.85.0
    entrypoint: [""]
  variables:
    IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  script:
    - grype "$IMAGE" --fail-on high -o sarif > grype.sarif
    - grype "$IMAGE" -o json | jq '.' > grype.json
  artifacts:
    reports:
      sast: grype.sarif
    paths:
      - grype.sarif
      - grype.json
    when: always

運用上の重要なポイントは、mainブランチへのマージ前(PR時)にのみ厳しいゲートを設定することです。featureブランチでもCriticalで毎回落とすと開発者がスキャンを無視する文化が定着してしまい、本末転倒になります。LynisやOpenSCAPによるCIS準拠監査と同様、ゲートと検知は段階的に強めるのが鉄則です。

Kubernetes Admission Controllerでの強制

CIで止めるだけでは、CIを経由しない手動デプロイや古いイメージタグの再デプロイを防げません。Kubernetes側でも「スキャン済み・許容Severityのイメージ以外をデプロイさせない」admission policyを設定するのが2026年のベストプラクティスです。Kyverno 1.13とOPA Gatekeeper v3.18が代表的な実装で、いずれもCNCF Graduatedプロジェクトです。

# Kyverno ClusterPolicy: イメージは内部レジストリ由来かつ署名済み必須
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce
  background: false
  rules:
    - name: verify-cosign-signature
      match:
        any:
          - resources:
              kinds: [Pod]
      verifyImages:
        - imageReferences:
            - "registry.internal.example.com/*"
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/example-org/*"
                    issuer: "https://token.actions.githubusercontent.com"
          mutateDigest: true
          required: true

さらに、Trivyのtrivy-operatorをクラスタにデプロイしておくと、稼働中の全イメージを定期的に再スキャンし、新たに公表されたCVE(例:2026年に公表されたopenssl 3.4のCRITICAL)をVulnerabilityReport CRDとして自動生成します。Prometheus exporterと組み合わせれば、Grafanaダッシュボードで「Critical脆弱性を含むPod数」のSLOを可視化できます。

検出された脆弱性をどう優先順位付けするか

CVSSスコアだけで対応順序を決めるのは2026年では非効率とされており、Critical(CVSS 9.0以上)の脆弱性であっても実際には全く悪用されていないものが多数存在します。CISA KEV(Known Exploited Vulnerabilities)カタログとEPSS(Exploit Prediction Scoring System)を組み合わせ、実際に攻撃に使われている、または攻撃される確率が高い脆弱性から対応するのが現代的なアプローチです。

  • CISA KEV登録:最優先(米CISAが「実際に悪用が確認された」と認定したもの)。
  • EPSS > 0.7:30日以内に悪用される確率70%以上。1週間以内の対応推奨。
  • CVSS 9.0+ かつ Network attack vector:パッチ提供から30日以内。
  • CVSS 7.0-8.9:四半期パッチサイクルに含める。
  • CVSS < 7.0 かつ EPSS < 0.05:VEXで「not_affected」を宣言してノイズ削減。
# Trivy で EPSS と KEV をスコア表示(v0.55 以降)
trivy image --vex repo \
  --pkg-types os,library \
  --scanners vuln \
  --format json \
  --output report.json \
  myregistry.example.com/app:1.4.2

# jq で EPSS > 0.5 のみ抽出
jq '[.Results[].Vulnerabilities[]?
     | select(.EPSS != null and .EPSS.Score > 0.5)
     | {ID:.VulnerabilityID, EPSS:.EPSS.Score, Sev:.Severity}]' report.json

Cosignによる署名とSBOM attestation

SBOMとスキャン結果は、生成しただけでは「誰が」「いつ」作ったものか保証できません。Sigstore Cosign 2.5以降を使い、SBOMをin-toto attestation(DSSE形式)として署名し、OCIレジストリのreferrers APIで紐付けるのが標準です。これによりCRA要件の「真正性」と「完全性」を同時に満たせます。

# Keyless 署名(GitHub Actions OIDC を利用)
export COSIGN_EXPERIMENTAL=1
IMAGE=ghcr.io/example/app@sha256:abc123...

# イメージ本体に署名
cosign sign "$IMAGE"

# SBOM を attestation として添付(CycloneDX)
cosign attest \
  --predicate app.cdx.json \
  --type cyclonedx \
  "$IMAGE"

# Trivy のスキャン結果を attestation として添付
cosign attest \
  --predicate trivy-result.json \
  --type vuln \
  "$IMAGE"

# 検証側:Rekor 透過ログで署名と attestation を検証
cosign verify-attestation \
  --type cyclonedx \
  --certificate-identity-regexp "https://github.com/example/.*" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  "$IMAGE"

cosign keylessはRekor透過ログにすべての署名イベントを記録するため、後から「このイメージは2026-06-30のworkflow runで署名された」と監査可能です。詳細はSigstore Cosign公式ドキュメントを参照してください。

運用上の落とし穴とアンチパターン

これまで複数のチームでTrivy/Grype導入を支援してきましたが、技術的にスキャンを動かすこと自体は容易です。問題は、「スキャン疲労」で形骸化させてしまう事例が圧倒的に多いこと。実際、私が前職で関わったプロジェクトでも、最初の3ヶ月で誰もアラートを見なくなった、という典型例を体験しました。よくあるアンチパターンを挙げておきます。

  1. 全Severityでpipelineを落とす:LOWやMEDIUMで落とすと、開発者が「とりあえず無視する」文化を作ってしまいます。最初はHIGH以上、慣れたらMEDIUMという段階的導入が現実的です。
  2. SBOMを生成して終わり:SBOMは保管・更新・追跡されて初めて価値が出ます。最低でも週次でSBOMを再スキャンする運用を組んでください。
  3. VEXを書かない:偽陽性(攻撃面に該当しないコードパス)にVEXを書かないと、毎週同じ脆弱性で議論が起きます。OpenVEX形式でnot_affectedを宣言するワークフローを早期に整備してください。
  4. baseイメージをpinしないFROM python:3.13-slimのようなタグ参照はビルドごとに中身が変わります。digestでFROM python:3.13-slim@sha256:...とpinし、Renovate/Dependabotで更新する運用が必須です。
  5. K8s admissionだけに頼る:admissionは「デプロイ時」のチェックのみで、稼働中のPodは対象外です。trivy-operatorまたはnftablesによるネットワーク分離と組み合わせ、多層防御を組んでください。

2026年のサプライチェーンセキュリティは、「単一ツールで全部解決」ではなく「Trivy/Grype + Cosign + Kyverno + trivy-operator + VEX運用」というパイプライン全体での組み合わせが鍵になります。最初から完璧を目指さず、まずはCIでHIGH/CRITICALを止めるところから始め、3〜6ヶ月かけてSBOM・署名・admissionへと段階的に拡張していくのが現実的なロードマップです。

よくある質問(FAQ)

TrivyとGrype、結局どちらを選ぶべきですか?

多くの組織にとってTrivyが第一選択です。単一バイナリでコンテナ・IaC・Kubernetes・Secretsまでカバーでき、運用負荷が低いためです。SBOM標準への厳密な準拠(特にSPDX署名やNTIA要件)が必要な場合は、Syft+Grypeを併用してください。両者はApache-2.0で併用にライセンス上の問題はありません。

SBOMはなぜ2026年に必要とされているのですか?

EU Cyber Resilience Act(CRA)が2026年末から本格適用され、製造者にSBOM提供義務が課されたことが直接の契機です。米国でもEO 14028の延長施策で連邦調達品にSBOMが要求されており、グローバルにビジネス継続要件となりました。Log4Shellのような事案で「自社で何を使っているか」を即座に答えられる必要があります。

CI/CDで脆弱性スキャンが遅い場合、どう高速化すればよいですか?

Trivy DBをDockerレイヤーキャッシュではなく、CIランナーのpersistent cacheまたは社内OCIレジストリ(trivy --db-repositoryで指定)に格納するのが最も効果的です。さらに、ビルド時にSBOMを1回生成しておき、後続のスキャンはtrivy sbomまたはgrype sbom:でSBOM経由にすると、レジストリpullが不要になり数倍高速化します。

distrolessやChainguard Imagesを使えばスキャン不要になりますか?

不要にはなりませんが、検出件数が劇的に減り運用が楽になります。Chainguard Imagesは「Known 0 CVE」を売りにしていますが、アプリケーション層の依存ライブラリ(npm, pip, Maven等)に脆弱性が含まれることは依然としてあり、スキャンは必要です。distroless化はノイズ削減として有効であり、スキャン廃止の理由にはなりません。

VEX(Vulnerability Exploitability eXchange)とは何ですか?

VEXは「あるSBOM内の脆弱性が、実際にそのソフトウェアでexploit可能かどうか」を機械可読で表明する文書形式です。OpenVEXとCSAF VEXの2系統があり、TrivyとGrypeはOpenVEXに対応しています。例えば「openssl 3.0.7のCVE-2023-0286は、自製品では該当APIを呼ばないため not_affected」と宣言することで、CI/CDでの誤検知ノイズを大幅に削減できます。

著者について Editorial Team

Our team of expert writers and editors.