実践的な「Trivy」利用方法〜「VSCode」によるスキャンからCI/CDパイプライン、「Trivy Operator」による継続的なスキャン〜Cloud Nativeチートシート(18)

Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する連載。今回は、Trivyの代表的な利用シーンを取り上げながら、実践的に利用するための検討ポイントを解説する(最新のv0.56.2含め2024年の情報に合うように更新)。

» 2024年12月05日 05時00分 公開
[西澤勇紀, 岡本隆史, 正野勇嗣株式会社NTTデータ]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

 Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する本連載「Cloud Nativeチートシート」。前回はTrivyの単体機能を中心に紹介しました。

 Trivyを組織やプロジェクトで取り入れるには、開発のライフサイクルから運用まで幅広く検討する必要があります。本稿では、Trivyの代表的な利用シーンを取り上げながら、実践的に利用するための検討ポイントを解説します。

Trivyの利用シーン

 アプリケーション開発はソースコードを書いて、ビルドやテストを行い、デプロイするといった流れを繰り返します。このような開発のライフサイクルの中、どこでTrivyを利用するのがよいのでしょうか? どのようなことを検討しなくてはならないのでしょうか?

 本稿では下記3つの利用シーンを取り上げて解説します。

Trivy利用シーン 狙い
【1】 ローカル開発環境 開発の初期段階から脆弱性の混入を防ぐ
【2】 CI/CDパイプライン 脆弱性を含むアプリケーションのデプロイを防ぐ
【3】 実行中のアプリケーション リリース後に発見された新規脆弱(ぜいじゃく)性を継続的に検知する
開発ライフサイクルとTrivy利用シーン

【1】ローカル開発環境

 ローカル開発環境は、開発者個人の作業場所です。「Docker」「Minikube」などを利用してコンテナを動かせるようにしたり、試行錯誤しながらコードを実装してテストしたりといった作業を繰り返します(上図の「個人作業のインナーループ」に該当)。

 Trivyは、次のような開発ツールと連携することで、ローカル開発環境上でも効率的なスキャンを可能にします。

 ローカル開発環境上でスキャンを有効にすると、開発の初期段階から脆弱な設定を検知できます。つまり、シフトレフト(後工程で行っていたセキュリティ対策を、開発工程の早い段階に組み込む考え方)なセキュリティ対策を実現できるといえるでしょう。

 では、ローカル開発環境上のスキャンを試してみましょう。今回はVisual Studio Code(以後、VSCode)を利用したTrivyのスキャンを紹介します。

VSCodeでTrivyによるスキャン

 VSCodeには「Trivy Vulnerability Scanner」という拡張機能があります。この拡張機能を利用すると、VSCode上でTrivyのスキャンが可能になります。

 今回は、Windows環境でVSCodeを利用したTrivyのスキャンについて紹介します。以前は、Windows版のTrivyがリリースされていなかったのでWSL Remoteを利用したセットアップが必要でしたが、最近はWindows版がリリースされているのでWindows環境でも簡単に利用できます。

 セットアップからTrivyスキャンまでの大まかな流れは次の通りです。

 順に進めていきます。

・1.VSCodeをWindowsにインストール

 VSCodeのインストールについては、@IT記事「Visual Studio Codeから『Hello Python』してみよう」などをご覧ください。

 なお本稿では、VSCodeの日本語パック「Japanese Language Pack for Visual Studio Code」をインストールしない状態で解説しています。日本語パックをインストールしている場合は、各メッセージを適宜読み替えてください。

・2.Trivyをインストール

 Trivyのリリースサイトから、Windows版のバイナリファイル「trivy_0.XX.x_windows-64bit.zip」をダウンロード、解凍し、アーカイブに含まれる「trivy.exe」を適当なディレクトリにインストールします。

 ここでは、「C:\bin」ディレクトリにコピーします。コピーしたら、command shellから動作を確認します。

C:\> C:\bin\trivy.exe version
Version: 0.56.2
....

・3.VSCode拡張機能「Trivy Vulnerability Scanner」をインストール

 VSCodeからTrivyのスキャンができるように、拡張機能の「Trivy Vulnerability Scanner」をインストールします。

VSCode拡張機能「Trivy Vulnerability Scanner」のインストール

 Trivy Vulnerability Scannerの設定画面の「Trivy:Binary Path」にTrivyをインストールしたパスを設定します。

VSCode拡張機能「Trivy Vulnerability Scanner」のインストール

・4.VSCodeでTrivyスキャン

 サンプルとして、よくありそうなDockerfileを用意します。

# ベースイメージを指定
FROM alpine:latest
 
# nginxのインストール
RUN apk update && apk add --no-cache nginx
 
# 設定ファイル配布
ADD default.conf /etc/nginx/http.d/default.conf
 
# nginx実行
CMD nginx -g "daemon off;"
VSCodeでDockerfileを用意

 「Trivy Vulnerability Scanner」をインストールすると、サイドバーにTrivyのアイコンが出現するので、これをクリックします。「Run Trivy now」を押下してDockerfileをスキャンしてみます。

VSCodeでDockerfileのスキャン

 エラーが表示されてスキャンできない場合は、Trivyや「Trivy Vulnerability Scanner」のバージョンを更新してみてください。スキャン結果は下図のようになりました。

VSCode上のスキャン結果

 「FINDINGS EXPLORER」に指摘項目(Regoのポリシーで定義されているID)の一覧が表示され、上記のように「DS005」を開くとDockerfileの8行目を修正すべきだと分かります。指摘内容の詳細は「FINDINGS HELP」から確認できます。

 以上がWindows環境でVSCodeを利用したTrivyのスキャンの流れです。macOSでも同様に簡単にセットアップできます。

 今回はDockerfileを対象にスキャンしましたが、Kubernetesマニフェストや「Terraform」などの他の設定ファイルもスキャンできます。また、アプリケーションの依存ライブラリ情報が記載されるファイルを探してパッケージやライブラリの脆弱性もスキャンできます。

 このように、ローカル開発環境上のスキャンを有効にすることで、初期開発のフェーズでも実装しながらCLI(コマンドラインインタフェース)不要で、インタラクティブに脆弱性の検知、対処が可能になります。

ローカル開発環境上のスキャン検討で意識しておくこと

 チームで開発する場合は、次のようなことを意識しておくとよいでしょう。

  • ローカル開発環境の要件を確認する
    チームで採用するOSやIDE(統合開発環境)などの要件からTrivyの利用可否や効率的なスキャン方法を検討する
  • 利用者間のバージョン差異に注意する
    Trivyは頻繁にバージョンアップするので、バージョンの差異に注意しながら、定期的にアップデートするなどの方針を決めておく
  • 開発のアジリティと脆弱性対応のトレードオフを気にする
    開発の初期段階でどこまで対処すべきかを意識合わせしておく。指摘を抑止したい場合は「.trivyignore」ファイルをチームで共有しながらスキャンすることで作業を効率化することも可能

【2】CI/CDパイプライン

 アプリケーションの品質を保ちながら継続的に新機能を素早く提供するために、CI/CD(継続的インテグレーション/継続的デリバリー)パイプラインを検討している方が増えてきたと思います。

 IPA(情報処理推進機構)が提供している「SP800-190:アプリケーションコンテナセキュリティガイド:4.1.1 イメージの脆弱性」には、「ビルドプロセスにおいて脆弱性が検知されたコンテナイメージの進行を防止するルールを設定できることが望ましい」と記載されています。そのため、CI/CDパイプラインのビルドフェーズで脆弱性のスキャンを検討することが望ましいでしょう。

 コンテナ開発のビルドパイプラインの流れは、パイプライン内でビルドやテストを行い、ビルド成果物、コンテナイメージをコンテナレジストリに格納するといった感じです。このビルドパイプラインにTrivyのスキャンを組み込むことで、脆弱性を検知したコンテナイメージをコンテナレジストリに格納せず、脆弱なアプリケーションのデプロイを防ぐことができます。

 CI/CDパイプライン構成は多岐にわたりますが、本稿では「GitLab CI」を利用したコンテナイメージのスキャンを紹介します。

GitLab CIとTrivy

 GitLab CIでTrivyを動かします。ここでは本連載第6回「GitLabによるCI実践入門――Kubernetesで利用するコンテナイメージをビルドする」の「ci-sample」リポジトリのコードを利用するので、記事を参考にリポジトリのフォークを済ませておいてください。

 まずはGitLab CIの動作確認も兼ねて、次のように「.gitlab-ci.yml」を変更します。

# 使用するイメージの指定
image: docker:stable
 
# 使用するサービスの指定
services:
   - docker:dind
 
# 環境変数
variables:
   CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
   REPO_NAME: gitlab.com/cloudnativetips/ci-sample
   TRIVY_VERSION: 0.56.2
 
before_script:
   - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
 
# ステージ
stages:
   - stage-build
   - stage-scan
   - stage-update-latest-image
 
# stage-buildステージ
build:
   # ステージ
   stage: stage-build
   # 処理内容
   script:
      - docker build --target build_image -t $CONTAINER_IMAGE:$CI_COMMIT_SHA .
      - docker push $CONTAINER_IMAGE:$CI_COMMIT_SHA
   only:
      - merge_requests
      - main
 
# stage-scanステージ
scan:
   stage: stage-scan
   script:
      - docker pull $CONTAINER_IMAGE:$CI_COMMIT_SHA
      # 1.Trivyインストール
      - wget --no-verbose https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz -O - | tar -zxvf -
      # 2. スキャン結果のファイル出力と見える化
      - ./trivy --cache-dir .trivycache/ image --exit-code 0 --no-progress --format template --template "@contrib/gitlab.tpl" -o gl-container-scanning-report.json $CONTAINER_IMAGE:$CI_COMMIT_SHA
      # 3. スキャン結果をログに出力
      - ./trivy --cache-dir .trivycache/ image --exit-code 0 --severity CRITICAL,HIGH --no-progress $CONTAINER_IMAGE:$CI_COMMIT_SHA
      # 4. 脆弱性を検知したらパイプラインを停止
      - ./trivy --cache-dir .trivycache/ image --exit-code 1 --severity CRITICAL --no-progress $CONTAINER_IMAGE:$CI_COMMIT_SHA
   artifacts:
      reports:
         container_scanning: gl-container-scanning-report.json
   only:
      - merge_requests
      - main
 
# stage-update-latest-imageステージ
update-latest-image:
   stage: stage-update-latest-image
   script:
      - docker pull $CONTAINER_IMAGE:$CI_COMMIT_SHA
      - docker tag $CONTAINER_IMAGE:$CI_COMMIT_SHA $CONTAINER_IMAGE:latest
      - docker push $CONTAINER_IMAGE:latest
   only:
      - main
.gitlab-ci.yml

 マージリクエスト(MR)を作成して、mainブランチにマージします。パイプラインが動くので、下図のような実行結果を確認します。

GitLab CIパイプラインの正常終了を確認

 「stage-scan」ステージでTrivyのスキャンが次の流れで実行されています。

 この流れを順に見ていきます。

・1.Trivyインストール

 実行結果は次のようになります。

$ wget --no-verbose https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz -O - | tar -zxvf -
Connecting to github.com (140.82.113.3:443)
Connecting to objects.githubusercontent.com (185.199.108.133:443)
writing to stdout
LICENSE
-                      0% |                                | 97673  0:05:00 ETA
-                     50% |****************                | 14.2M  0:00:01 ETA
-                    100% |********************************| 28.0M  0:00:00 ETA
written to stdout
README.md
contrib/asff.tpl
contrib/gitlab-codequality.tpl
contrib/gitlab.tpl
contrib/html.tpl
contrib/junit.tpl
trivy

 Trivyのインストールは、tarファイルによるインストール、rpmによるインストール、バイナリのみのインストールなど、複数の方法が提供されています。

 tarファイルやrpmでインストールすると、Trivyの出力形式を変換するテンプレートが用意されておいます。GitLabを利用した場合、gitlab.tplテンプレートを利用することで、セキュリティスキャン結果をGitLabに登録できるフォーマットに変換してくれます。前回記事で紹介したバイナリのみのインストールでは、テンプレートファイルが配置されないので、ご注意ください。

・2.スキャン結果のファイル出力と見える化

 実行結果は次のようになります。

$ ./trivy --cache-dir .trivycache/ image --exit-code 0 --no-progress --format template --template "@contrib/gitlab.tpl" -o gl-container-scanning-report.json $CONTAINER_IMAGE:$CI_COMMIT_SHA
2022-07-02T08:57:30.911Z	INFO	Need to update DB
2022-07-02T08:57:30.912Z	INFO	DB Repository: ghcr.io/aquasecurity/trivy-db
2022-07-02T08:57:30.912Z	INFO	Downloading DB...
2022-07-02T08:57:33.494Z	INFO	Vulnerability scanning is enabled
2022-07-02T08:57:33.494Z	INFO	Secret scanning is enabled
2022-07-02T08:57:33.495Z	INFO	If your scanning is slow, please try '--security-checks vuln' to disable secret scanning
2022-07-02T08:57:33.495Z	INFO	Please see also https://aquasecurity.github.io/trivy/v0.29.2/docs/secret/scanning/#recommendation for faster secret detection
2022-07-02T08:57:33.933Z	INFO	Number of language-specific files: 1
2022-07-02T08:57:33.933Z	INFO	Detecting gobinary vulnerabilities...

 次のようなオプションを利用しています。

  • --cache-dir
    • スキャン時に使用するローカルのキャッシュディレクトリを指定する
    • グローバルオプションなので「Trivy [Global Option] image [Option]」形式で「image」サブコマンドの前に指定する
  • --exit-code
    • 脆弱性を検知したときのパイプラインの動きを制御する
      • 「--exit-code 0」:脆弱性を検知してもパイプラインは停止しない
      • 「--exit-code 1」・脆弱性を検知するとパイプラインが停止する
  • --no-progress
    • 次のような脆弱性情報のDBのダウンロード状況を示すプログレスバーを表示しない。「32.60 MiB / 32.60 MiB [------------------------------------------------------------------------------------------------------------] 100.00% 18.34 MiB p/s 2.0」
  • --format template --template "@contrib/gitlab.tpl"
    • スキャン結果をテンプレートファイル(gitlab.tpl)の形式で出力する

 Trivyのスキャン結果をファイル(gl-container-scanning-report.json)に出力しました。ファイル出力の主な理由は、スキャン結果を見える化するためです。

 GitLabにはコンテナのスキャン結果をGitLab上で確認できる仕組みがあり、「@contrib/gitlab.tpl」を利用すると、GitLabで利用できるフォーマットで出力してくれます。「.gitlab-ci.yml」の下記設定によって、レポートとして登録できます。

   artifacts:
      reports:
         container_scanning: gl-container-scanning-report.json

 「Pipelines」実行結果のタブに「Security」タブが追加され、Trivyのスキャン結果をGitLab上で確認できます。

GitLabの「Project」→「Pipelines」の実行結果タブからTrivyスキャン結果を表示

 ただし、こちらの画面を確認できるのはGitLabの Ultimateプランのみです。Ultimateプランで表示可能なセキュリティレポートの詳細は公式ドキュメントを確認してください。Freeプランでは確認できませんが、下図のようにスキャン結果のファイルはダウンロードできます。

GitLabからスキャン結果ファイルをダウンロード

 今回はGitLab CIの例を紹介しましたが、環境次第でスキャン結果の見える化の検討方法は異なります。例えば次のようなことを考えます。

  • GitHub Action」でスキャンし、SARIF形式でファイルに出力してGitHubの「Code Scanning」を利用する
  • 「AWS CodeBuild」でスキャンし、ASFF形式でファイルに出力して「SecurityHub」と連携させる
  • スクリプトを利用してスキャン結果をサードパーティー製品の「FutureVuls」と連携させる
  • スキャン結果をJSON形式のファイルに出力してから、カスタマイズした上でSlackと連携させる

 脆弱性スキャン結果の見える化は運用プロセスとも密接に関連します。「誰がスキャン結果を確認するのか?」「どのように脆弱性を対処していくのか?」を考えながら、ファイルの出力方法や見える化について検討します。

・3.スキャン結果をログに出力

 実行結果は次のようになります。

$ ./trivy --cache-dir .trivycache/ image --exit-code 0 --severity CRITICAL,HIGH --no-progress $CONTAINER_IMAGE:$CI_COMMIT_SHA
2022-07-02T08:57:34.211Z	INFO	Vulnerability scanning is enabled
2022-07-02T08:57:34.211Z	INFO	Secret scanning is enabled
2022-07-02T08:57:34.212Z	INFO	If your scanning is slow, please try '--security-checks vuln' to disable secret scanning
2022-07-02T08:57:34.212Z	INFO	Please see also https://aquasecurity.github.io/trivy/v0.29.2/docs/secret/scanning/#recommendation for faster secret detection
2022-07-02T08:57:34.216Z	INFO	Number of language-specific files: 1
2022-07-02T08:57:34.216Z	INFO	Detecting gobinary vulnerabilities...
app (gobinary)
==============
Total: 1 (HIGH: 1, CRITICAL: 0)
┌───────────────────┬────────────────┬──────────┬───────────────────┬───────────────┬──────────────────────────────────────────────────────────┐
│      Library      │ Vulnerability  │ Severity │ Installed Version │ Fixed Version │                          Title                           │
├───────────────────┼────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────┤
│ golang.org/x/text │ CVE-2021-38561 │ HIGH     │ v0.3.3            │ 0.3.7         │ golang: out-of-bounds read in golang.org/x/text/language │
│                   │                │          │                   │               │ leads to DoS                                             │
│                   │                │          │                   │               │ https://avd.aquasec.com/nvd/cve-2021-38561               │
└───────────────────┴────────────────┴──────────┴───────────────────┴───────────────┴──────────────────────────────────────────────────────────┘

 スキャン結果をログに出力しました。ログの出力は必須ではありませんが、ログに出力することでデバッグが容易になります。例えば、スキャン結果の見える化が何らかの理由でうまく機能していなくても、ログから直ちに結果を確認できるので、何かと便利です。

 スキャン対象次第では大量のログが出力されます。今回は重大度の大きな「CTIRICAL」「HIGH」に絞ってログに出力しています。必要に応じて対象を絞りながらログの出力も有効利用しましょう。

・4.脆弱性を検知したらパイプラインを停止

 実行結果は次のようになります。

$ ./trivy --cache-dir .trivycache/ image --exit-code 1 --severity CRITICAL --no-progress $CONTAINER_IMAGE:$CI_COMMIT_SHA
2022-07-02T08:57:34.481Z	INFO	Vulnerability scanning is enabled
2022-07-02T08:57:34.481Z	INFO	Secret scanning is enabled
2022-07-02T08:57:34.481Z	INFO	If your scanning is slow, please try '--security-checks vuln' to disable secret scanning
2022-07-02T08:57:34.482Z	INFO	Please see also https://aquasecurity.github.io/trivy/v0.29.2/docs/secret/scanning/#recommendation for faster secret detection
2022-07-02T08:57:34.485Z	INFO	Number of language-specific files: 1
2022-07-02T08:57:34.485Z	INFO	Detecting gobinary vulnerabilities...
app (gobinary)
==============
Total: 0 (CRITICAL: 0)

 ここでは、Trivyのオプションに「--exit-code 1」を指定しているので、「CRITICAL」の脆弱性を検知すると、パイプラインが停止します。つまり、脆弱なコンテナイメージをコンテナレジストリに格納させないようにして、その結果として脆弱なコンテナイメージのデプロイを防ぐことができます。なお、今回は「CRITICAL」の脆弱性が検知されなかったので、パイプラインは正常終了しました。

 「CRITICAL」の脆弱性を検知してパイプラインが停止したら、脆弱性の影響を確認して問題の有無を確認します。脆弱性を対処できるのが理想ですが、「影響がない」と判断して前に進める場合は、「.trivyignore」ファイルで「検知対象外」とすることも検討します。

 ここで悩むポイントは「どのような条件でパイプラインを停止させるか?」です。簡易に設定するなら今回のように運用してみてよいと思います。ただし、「CRITICAL」「HIGH」はあくまで一般的な指標であり、同じCVE-IDでもセキュリティアドバイザリが示す脆弱性のスコアはベンダーごとで異なります。また、ネットワーク到達度によっても影響度は変わります。

 答えがあるようなものではありませんが、運用プロセスにも関わるので、組織やプロジェクトのセキュリティ方針を考慮しながら、脆弱性の検知/対処方針を検討します。「CRITICAL」「HIGH」だけではなく、独自の条件でパイプラインの停止を検討する場合は、JSON形式のスキャン結果で得られる詳細な情報が役立つかもしれません。

 頻繁にパイプラインが停止し、開発に影響がある場合は、試験環境へのデプロイ時は脆弱性によってパイプラインを停止せず、リリース前に脆弱性を評価して対処するように運用するなど、プロジェクトに適したやり方を工夫してみてください。

コラム Trivyのローカルキャッシュとスキャン高速化の裏側

 Trivyは脆弱性情報のDBとスキャンしたパッケージやライブラリの情報をローカルのキャッシュディレクトリに保存します。このキャッシュを利用することでスキャンを高速化しています。コンテナイメージをスキャンする流れからこの仕組みを見ていきます。

Trivyのローカルキャッシュ

1.脆弱性情報のDBをローカルにダウンロード

 初めに、脆弱性情報をまとめたDBファイルをGHCR(GitHub Container Registry)からダウンロードして、キャッシュディレクトリに保存します。このDBは約30MBと軽量なので、パイプライン内で毎回ダウンロードしても影響は軽微です。

 また6時間ごとに最新のDBにアップデートされます。更新予定時刻は以下の設定ファイルから確認できます。

$ cat ~/.cache/trivy/db/metadata.json
{"Version":2,"NextUpdate":"2022-07-02T12:08:37.186726308Z","UpdatedAt":"2022-07-02T06:08:37.186726708Z","DownloadedAt":"2022-07-02T09:58:31.871719916Z"}

 設定ファイルを検査するルール(Regoのポリシー)はTrivyのバイナリに埋め込まれているので、DBファイルのようなダウンロードは必要なく設定ファイルをスキャンできます。

※なお、v0.27.1までは、設定ファイルのスキャンにおいても、ポリシーをローカルにダウンロードしていました。提供されるポリシーは自動的にダウンロードされるので、ユーザーが意識することはありませんが、バージョンが古いと仕組みが異なる点に注意してください。

2.OSパッケージ/ライブラリ情報をキャッシュ

 次に、コンテナイメージのスキャンでは、コンテナイメージを丸ごとローカルにダウンロードするのではなく、コンテナイメージの各イメージレイヤーからOSパッケージとアプリケーションの依存ライブラリ情報を抽出してキャッシュとして保持します。


CI/CDパイプラインのスキャンを中長期的に利用するには?

 CI/CDパイプラインにTrivyのスキャンを組み込むことで、簡単にスキャンを継続できます。しかし、運用していくと困りごとは出てくるものです。先を見据えて次のようなことも考慮しておくとよいでしょう。

  • パイプラインが止まり過ぎて開発のアジリティに影響を及ぼしていないか?
    • 求めるセキュリティレベル次第だが、開発のアジリティとのトレードオフを考慮して、必要に応じて緩和することも検討する
    • 例えば、「検証用のビルドのみであり、デプロイしないので脆弱性を検知してもパイプラインを停止せずに通知のみとする」といった判断もある
  • 本番環境にアプリケーションをリリースした後もCI/CDパイプラインのスキャンのみを採用していないか?
    • CI/CDパイプラインのスキャンでは実行中のアプリケーションの脆弱性を検知できない
    • 新規脆弱性に気付けるよう、実行中のアプリケーションを対象にした定期スキャンも検討する
  • 開発のパイプラインとセキュリティスキャンを密結合としたときの運用負荷は大きくないか?
    • Trivyに限った話ではないが、パイプラインの増加や複雑化に伴いバージョンアップなどの変更コストも増加する
    • 開発チームやセキュリティチームといった別々のロールのメンバーが同じパイプラインを更新する場合、役割分担やパイプラインの管理/更新方法も整理しておく
    • 大規模システムや高度なスケーラビリティが求められる場合は、スキャンの分離や自動化などを検討しつつ、変更容易性の観点も含めてパイプラインの構成を検討する


【3】実行中のアプリケーション

 新しい脆弱性は日々発見されています。既にデプロイして稼働しているイメージの脆弱性が運用中に発見されることなど日常茶飯事でしょう。しかし、CI/CDパイプラインのスキャンは実行中のアプリケーションの脆弱性を確認できません。本番環境にアプリケーションをリリースした後は、実行中のアプリケーションを対象に定期的にスキャンする必要があります。

 TrivyはKubernetesクラスタを対象に、実行中のリソースをスキャンできます。v0.28.0から登場したKubernetesクラスタスキャン機能も実行中のリソースを対象にスキャンできますが、定期的にスキャンするにはどうしたらよいのでしょうか?

 ここからはKubernetesネイティブな方法で実行中のアプリケーションを定期スキャンできる「Trivy Operator」を紹介します。

Trivy Operatorとは

 Trivy OperatorはAqua Securityが提供するオープンソースソフトウェア(OSS)のKubernetes Operatorの一つです。Kubernetesクラスタ内のリソースを対象としたスキャナーであり、スキャン結果をKubernetesネイティブなカスタムリソースとして保持します。

 リソース作成のタイミングでスキャンしたり、定期的なスキャンもサポートしたりしています。例えば、新たなPodが作成されると、Pod内のコンテナイメージが自動的にスキャンされ、結果をカスタムリソースとして保持します。

 もともとはAqua SecurityのOSSプロジェクト「Starboard」で、Trivyや「polaris」「kube-benchといったツールを組み合わせて包括的なKubernetesクラスタのスキャナーを開発していましたが、Trivyの機能が拡大するにつれて、Kubernetes上でTrivyをネイティブに実行することを目指してTrivy Operatorの開発がスタートしました。

 Trivyの機能追加に伴い今後もアップデートしていくことが予想されますが、本稿では現在公開されているTrivy Operatorを利用した定期スキャンを紹介します。

Trivy Operatorのデプロイと定期スキャン

 Trivy OperatorをHelmチャートでデプロイします。

# リポジトリ追加
$ helm repo add aqua https://aquasecurity.github.io/helm-charts/
"aqua" has been added to your repositories
 
# リポジトリアップデート
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "stable" chart repository
...Successfully got an update from the "aqua" chart repository
Update Complete. Happy Helming!
 
$ helm install trivy-operator aqua/trivy-operator\
   --namespace trivy-system \
   --create-namespace \
   --set="trivy.ignoreUnfixed=true" \
   --version v0.24.1
NAME: trivy-operator
LAST DEPLOYED: Sun Oct 20 11:48:04 2024
NAMESPACE: trivy-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
You have installed Trivy Operator in the trivy-system namespace.
It is configured to discover Kubernetes workloads and resources in
all namespace(s).
Inspect created VulnerabilityReports by:
    kubectl get vulnerabilityreports --all-namespaces -o wide
Inspect created ConfigAuditReports by:
    kubectl get configauditreports --all-namespaces -o wide
Inspect the work log of trivy-operator by:
    kubectl logs -n trivy-system deployment/trivy-operator

 Trivy Operatorがデプロイされました。

$ kubectl get pod -n trivy-system
NAME                             READY   STATUS      RESTARTS   AGE
trivy-operator-bfbf9d557-8pdw7   1/1     Running     0          4h32m

 次の「deployment.yaml」を用意します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-alpine
spec:
  selector:
    matchLabels:
      app: nginx-alpine
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx-alpine
    spec:
      containers:
      - name: nginx-alpine
        image: nginx:alpine
        ports:
        - containerPort: 80
deployment.yaml

 Kubernetesクラスタにdeployment.yamlをデプロイします。

$ kubectl apply -f deployment.yaml
deployment.apps/nginx-alpine created

 Podが作成されると、TrivyのスキャナーJobが起動して脆弱性のスキャンを開始します。

$ kubectl get job -A -w
NAMESPACE      NAME                                  COMPLETIONS   DURATION   AGE
trivy-system   scan-vulnerabilityreport-65b95454df   0/1           5s         5s
trivy-system   scan-vulnerabilityreport-65b95454df   1/1           19s        19s
trivy-system   scan-vulnerabilityreport-65b95454df   1/1           19s        19s

 スキャンの結果は、次の4つのカスタムリソースに格納されています。

  • VulnerabilityReport:コンテナイメージの脆弱性スキャン結果
  • ConfigAuditReport:マニフェストのスキャン結果
  • ExposedSecretReport:シークレットのスキャン結果
  • SbomReport:SBOMの結果

 スキャン結果を確認します。

# vulnerabilityreports:コンテナイメージの脆弱性スキャン結果
$  kubectl get vulnerabilityreports -o wide
NAME                                             REPOSITORY      TAG      SCANNER   AGE     CRITICAL   HIGH   MEDIUM   LOW   UNKNOWN
replicaset-nginx-alpine-9484f4b94-nginx-alpine   library/nginx   alpine  Trivy   2m20s   0          0      8        0     0
 
# configauditreports:Kubernetesマニフェストのスキャン結果
$ kubectl get configauditreports -o wide
NAME                                SCANNER   AGE     CRITICAL   HIGH   MEDIUM   LOW
replicaset-nginx-alpine-9484f4b94  Trivy   2m55s   0          0      6        7

 コンテナイメージの脆弱性スキャン結果の詳細は次のように確認できます。

$ kubectl get  vulnerabilityreports replicaset-nginx-alpine-9484f4b94-nginx-alpine -oyaml
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:ount: 0
  annotations:0
    trivy-operator.aquasecurity.github.io/report-ttl: 24h0m0s
  creationTimestamp: "2024-10-20T11:50:36Z"
  generation: 1t: 0
  labels:imestamp: "2024-10-20T11:50:36Z"
    resource-spec-hash: 94458d54d
    trivy-operator.container.name: nginx-alpinenerabilityreports replicaset-nginx-alpine-9484f4b94-nginx-alpine -oyaml  |less
    trivy-operator.resource.kind: ReplicaSet
    trivy-operator.resource.name: nginx-alpine-9484f4b94
    trivy-operator.resource.namespace: default
  name: replicaset-nginx-alpine-9484f4b94-nginx-alpine
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: false
    controller: true
    kind: ReplicaSet
    name: nginx-alpine-9484f4b94
    uid: 96e89e92-0bc3-403e-95e4-96443dde4abe
  resourceVersion: "9634611"
  uid: d9f9ad81-8df2-49be-9fbe-ea80c873ae84
report:
  artifact:
    digest: sha256:cb8f91112b6b50ead202f48bbf81cec4b34c254417254efd94c803f7dd718045
    repository: library/nginx
    tag: alpine
  os:
    family: alpine
    name: 3.20.3
  registry:
    server: index.docker.io
  scanner:
    name: Trivy
    vendor: Aqua Security
    version: 0.53.0
  summary:     # 検出された脆弱性の概要
    criticalCount: 0
    highCount: 0
    lowCount: 0
    mediumCount: 8
    noneCount: 0
    unknownCount: 0
  updateTimestamp: "2024-10-20T11:50:36Z"
  vulnerabilities:    # 検出された脆弱性の個々の詳細情報
  - fixedVersion: 7.83.1-r2
    installedVersion: 7.83.1-r1
    links: []
    primaryLink: https://avd.aquasec.com/nvd/cve-2022-32205
    resource: curl
    score: 5.9
    severity: MEDIUM
    title: 'curl: Set-Cookie denial of service'
    vulnerabilityID: CVE-2022-32205
...
  - fixedVersion: 7.83.1-r2
    installedVersion: 7.83.1-r1
    links: []
    primaryLink: https://avd.aquasec.com/nvd/cve-2022-32208
    resource: libcurl
    score: 5.3
    severity: MEDIUM
    title: 'curl: FTP-KRB bad message verification'
    vulnerabilityID: CVE-2022-32208

 スキャン結果を保持するカスタムリソースはPodと同じ「metadata.ownerReferences」です。つまり、ここではReplicaSetが親リソースとなります。

 kubectlコマンドのプラグイン「kubectl tree」を利用して、リソースのツリーを確認します。手順は省略しますが、「krew」を利用して「kubectl tree」コマンドを実行できるようにしておきます。

$ kubectl tree deploy nginx-alpine
NAMESPACE  NAME                                                                    READY  REASON  AGE
default    Deployment/nginx-alpine                                                   -              8m19s
default    └─ReplicaSet/nginx-alpine-9484f4b94                                     -              8m19s
default      ├─ConfigAuditReport/replicaset-nginx-alpine-9484f4b94                 -              8m19s
default      ├─Pod/nginx-alpine-9484f4b94-cmr4x                                    True           8m19s
default      ├─Pod/nginx-alpine-9484f4b94-wx52v                                    True           8m19s
default      └─VulnerabilityReport/replicaset-nginx-alpine-9484f4b94-nginx-alpine  -              8m

 Podと同様、スキャン結果のカスタムリソースもReplicaSetが親リソースとなっています。これによってKubernetesのガベージコレクションの仕組みが適用されるので、Deployment(ReplicaSet)の再作成に合わせて古いPodのスキャン結果が自動的に削除されるようになります。

 次に定期スキャンの仕組みを確認します。

 Trivy Operatorはスキャン結果を保持する期間(TTL)を設定するパラメーター「OPERATOR_SCANNER_REPORT_TTL」が用意されています。このパラメーターでTTLを指定すると、スキャン結果のカスタムリソースの「annotations」にTTLが設定され、TTLを経過したスキャン結果は自動的に削除されるようになります。

 今回はHelm Chart でインストールする際にTTLを1日(8万6400秒)に設定したので、スキャン結果のリソースを確認すると「metadata.annotations.trivy-operator.aquasecurity.github.io/report-ttl: 24h0m0s」が付与されています。つまり、1日経過するとこのスキャン結果は自動的に削除されます。

$ kubectl get vulnerabilityreports replicaset-nginx-alpine-9484f4b94-nginx-alpine -o yaml | grep metadata -2
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:
  annotations:
    trivy-operator.aquasecurity.github.io/report-ttl: 24h0m0s

 Trivy Operatorはカスタムリソースのスキャン結果の状態を確認しており、スキャン結果が削除されると再度スキャンします。例えば次のようにスキャン結果を手動で削除しても、再度スキャナーのJobが実行され、新たなスキャン結果が生成されます。

# スキャン結果の確認
$ kubectl get vulnerabilityreports
NAME                                             REPOSITORY      TAG      SCANNER   AGE
replicaset-nginx-alpine-9484f4b94-nginx-alpine   library/nginx   alpine  Trivy   13m
 
# スキャン結果の削除
$ kubectl delete vulnerabilityreports replicaset-nginx-alpine-9484f4b94-nginx-alpine
vulnerabilityreport.aquasecurity.github.io "replicaset-nginx-alpine-9484f4b94-nginx-alpine" deleted
 
# スキャン Job の起動確認
$ kubectl get job -A -w
NAMESPACE      NAME                                  COMPLETIONS   DURATION   AGE
trivy-system   scan-vulnerabilityreport-65b95454df   0/1           4s         4s
trivy-system   scan-vulnerabilityreport-65b95454df   1/1           18s        18s
trivy-system   scan-vulnerabilityreport-65b95454df   1/1           18s        18s
 
# スキャン結果が再作成されていることを確認
$ kubectl get vulnerabilityreports
NAME                                             REPOSITORY      TAG      SCANNER   AGE
replicaset-nginx-alpine-9484f4b94-nginx-alpine   library/nginx   alpine  Trivy   50s

 このような仕組みで、Trivy Operatorは定期的なスキャンをサポートしています。

コラム Trivyのネットワーク要件とクライアントサーバ構成について

 オンプレミス環境などでTrivyを利用する場合のTIPSとしてTrivyのネットワーク要件とクライアントサーバ構成について少し補足します。

ネットワーク要件

 本稿はインターネットに接続できる前提で進めてきましたが、脆弱性情報のDBファイルのダウンロードには次のような通信要件があります。

  • ghcr.io
  • pkg-containers.githubusercontent.com

 ファイアウォールなどでアウトバウンド通信が制御されている環境下ではDBファイルをダウンロードできないかもしれません。 もし直接DBファイルをダウンロードできない場合は、「Air-Gapped Environment」を参考にしながら、オフライン環境でTrivyを動かす仕組みを検討しましょう。

クライアントサーバ構成

 多くのケースではスタンドアロン構成によるTrivyのスキャンで問題ないと思いますが、Trivyはクライアントサーバ構成もサポートしています。

 クライアントサーバ構成では、Trivyのローカルキャッシュを複数のメンバーに共有して効率的にスキャンしたり、キャッシュのバックエンドに「Redis」を採用できます。大規模環境でのスキャンやプロダクト用途では有利に働くケースもあるので、必要に応じてこちらの構成も検討してみるのもよいでしょう。


まとめ

 2回にわたってTrivyを紹介しました。当初はコンテナイメージのスキャンニングツールとして提供されていましたが、現在では、さまざまな便利な機能が強化されたのを確認できると思います。Trivyを活用してコンテナのセキュリティを強化してみてはいかがでしょうか。

■更新履歴

【2024/12/5】最新のv0.56.2含め2024年の情報に合うように更新しました。


Copyright © ITmedia, Inc. All Rights Reserved.

スポンサーからのお知らせPR

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。