こんにちは。 CI/CD を含むクラウドインフラの構築や技術支援を担当している、クラウドエンジニアの佐藤です。
突然ですが、Apache Log4j の脆弱性、皆さまの記憶に新しいのではないでしょうか?
自社の開発しているアプリ、または自社が使っているアプリに Apache Log4j ライブラリは使われているのか?使われているとして脆弱性のあるバージョンなのか?を急いで調べ、暫定対応や恒久対応を実施された事と思います。
私は以前、アプリ開発もしていました。バグや脆弱性は下流工程で問題が発生してから修正する工数より、上流工程で対応した方が工数が少なくて済みます。
そこで、上流工程にあたる Azure Pipelines の CI パイプラインに脆弱性スキャナーを組み込みます。コードがプッシュされる度にその時点の脆弱性情報データベースから対象のライブラリやパッケージを使っていないか自動的に検査する仕組みにします。
組み込む脆弱性スキャナーは、Trivy を使ってみたいと思います。私自身セキュリティーは専門外なので、他にもたくさんの脆弱性スキャナーはあると思います。
ちなみに、私の認識として Trivy は、オープンソースでコンテナーイメージの脆弱性をスキャンするお手軽なツールだと思っていました。ところが、Trivy のドキュメントを読むと、プログラミング言語のライブラリやパッケージの脆弱性もスキャンできるそうです。
という事で、インフラ構築目線ではありますが、Azure Pipelines で脆弱性スキャナー Trivy の使い方を検証してみました。
dotnet を使って、簡単な検証用アプリを作成します。
私の開発環境は .NET 7 がインストール済みです。
「 trivydemoapp 」という名前でアプリを作成し、ビルドが通る事を確認します。
$ dotnet --version
7.0.100
$ dotnet new mvc --name trivydemoapp
$ cd trivydemoapp
$ dotnet build
Installing Trivy
のドキュメントを参考に Trivy をインストールします。
私の開発環境は「 Install from GitHub Release (Official) 」の方法でインストールしました。
インストールするバージョンは、現時点で最新のものを使用しました。
※
Releases · aquasecurity/trivy
$ curl -LO https://github.com/aquasecurity/trivy/releases/download/v0.37.2/trivy_0.37.2_Linux-64bit.tar.gz
$ tar -xzf trivy_0.37.2_Linux-64bit.tar.gz
$ sudo mv trivy /usr/local/bin/
$ rm trivy_0.37.2_Linux-64bit.tar.gz
必要最小限のコマンドラインオプションで Trivy を実行してみます。
「 Downloading DB... 」と表示され DB をダウンロードしているのが確認できます。
$ trivy fs .
2023-02-14T01:32:28.344Z INFO Need to update DB
2023-02-14T01:32:28.344Z INFO DB Repository: ghcr.io/aquasecurity/trivy-db
2023-02-14T01:32:28.344Z INFO Downloading DB...
35.62 MiB / 35.62 MiB [-------------------------------------------------------------------------] 100.00% 22.60 MiB p/s 1.8s
2023-02-14T01:32:31.550Z INFO Vulnerability scanning is enabled
2023-02-14T01:32:31.550Z INFO Secret scanning is enabled
2023-02-14T01:32:31.550Z INFO If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-02-14T01:32:31.550Z INFO Please see also https://aquasecurity.github.io/trivy/v0.37/docs/secret/scanning/#recommendation for faster secret detection
2023-02-14T01:32:39.617Z INFO Number of language-specific files: 0
あえて脆弱性のあるパッケージのバージョンを使ってみます。
使うパッケージは「 Newtonsoft.Json 」のバージョン「 12.0.1 」です。
$ dotnet add package Newtonsoft.Json -v 12.0.1
$ dotnet list package
Project 'trivydemoapp' has the following package references
[net7.0]:
Top-level Package Requested Resolved
> Newtonsoft.Json 12.0.1 12.0.1
$ dotnet build
再度 Trivy を実行します。
Newtonsoft.Json ライブラリは、Severity が「 HIGH 」、Fixed Version が「 13.0.1 」という脆弱性スキャン結果が表示されています。
$ trivy fs .
2023-02-14T01:34:05.019Z INFO Vulnerability scanning is enabled
2023-02-14T01:34:05.019Z INFO Secret scanning is enabled
2023-02-14T01:34:05.019Z INFO If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-02-14T01:34:05.019Z INFO Please see also https://aquasecurity.github.io/trivy/v0.37/docs/secret/scanning/#recommendation for faster secret detection
2023-02-14T01:34:12.936Z INFO Number of language-specific files: 1
2023-02-14T01:34:12.936Z INFO Detecting dotnet-core vulnerabilities...
bin/Debug/net7.0/trivydemoapp.deps.json (dotnet-core)
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)
┌─────────────────┬─────────────────────┬──────────┬───────────────────┬───────────────┬───────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │
├─────────────────┼─────────────────────┼──────────┼───────────────────┼───────────────┼───────────────────────────────────────────────────┤
│ Newtonsoft.Json │ GHSA-5crp-9r3c-p9vr │ HIGH │ 12.0.1 │ 13.0.1 │ Improper Handling of Exceptional Conditions in │
│ │ │ │ │ │ Newtonsoft.Json │
│ │ │ │ │ │ https://github.com/advisories/GHSA-5crp-9r3c-p9vr │
└─────────────────┴─────────────────────┴──────────┴───────────────────┴───────────────┴───────────────────────────────────────────────────┘
Fixed Version である「 13.0.1 」に変更して、脆弱性スキャンの結果を確認します。
脆弱性の解消が確認できました。
$ dotnet add package Newtonsoft.Json -v 13.0.1
$ dotnet list package
Project 'trivydemoapp' has the following package references
[net7.0]:
Top-level Package Requested Resolved
> Newtonsoft.Json 13.0.1 13.0.1
$ dotnet build
$ trivy fs .
2023-02-14T01:35:31.850Z INFO Vulnerability scanning is enabled
2023-02-14T01:35:31.850Z INFO Secret scanning is enabled
2023-02-14T01:35:31.850Z INFO If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-02-14T01:35:31.850Z INFO Please see also https://aquasecurity.github.io/trivy/v0.37/docs/secret/scanning/#recommendation for faster secret detection
2023-02-14T01:35:39.652Z INFO Number of language-specific files: 1
2023-02-14T01:35:39.652Z INFO Detecting dotnet-core vulnerabilities...
後ほど、Azure Pipelines で Trivy の脆弱性スキャン結果を確認するため、あえて脆弱性のあるバージョンに戻しておきます。
$ dotnet add package Newtonsoft.Json -v 12.0.1
Azure DevOps の「 Organization Settings 」から「 Extensions 」を開きます。
「 Browse marketplace 」をクリックします。
検索ボックスに「 trivy 」を入力して検索します。
Aqua Security の「 trivy 」をクリックします。
「 Get it free 」をクリックします。
「 Install 」をクリックします。
「 Proceed to organization 」をクリックします。
Azure DevOps の「 Organization Settings 」から「 Extensions 」を開きます。
「 trivy by Aqua Security 」が表示されているのを確認します。
ローカルにある検証用アプリを Azure Repos の Git リポジトリにプッシュします。
$ dotnet new gitignore
$ git init
$ git add -A
$ git commit -m "first commit"
$ git branch -m main
$ az repos create \
--org https://dev.azure.com/ceblog/ \
--project ceblog \
--name trivydemoapp
$ git remote add origin https://ceblog@dev.azure.com/ceblog/ceblog/_git/trivydemoapp
$ git push -u origin --all
Git リポジトリにプッシュできました。
Azure DevOps Pipelines Task for Trivy
のドキュメントを参考に Azure Pipelines の YAML を作成します。
ファイル名は「 azure-pipelines.yml 」です。
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
inputs:
version: 7.x
- task: DotNetCoreCLI@2
displayName: DotNetBuild
inputs:
command: build
- task: trivy@1
inputs:
path: .
azure-pipelines.yml を Git リポジトリにプッシュします。
$ git add -A
$ git commit -m "add azure-pipelines.yml"
$ git push
Azure Pipelines を作成すると自動的に CI パイプラインが動き出します。
$ az pipelines create \
--name trivydemoapp-ci \
--repository trivydemoapp \
--repository-type tfsgit \
--yml-path azure-pipelines.yml
CI パイプラインの詳細を確認すると「 trivy 」タスクが失敗しています。
ログを確認すると「 docker run 」や「 aquasec/trivy:latest 」と表示されているので、Docker コンテナーで Trivy が実行されている事がわかります。
「 20230214.1 」をクリックします。
「 #20230214.1 」の左側に赤のバツアイコンが表示されています。
「 Trivy 」タブをクリックします。
Newtonsoft.Json ライブラリは、Severity が「 HIGH 」である事がわかります。
ID 列の「 GHSA... 」をクリックします。
Patched versions で「 13.0.1 」が確認できます。
CVSS が 10 段階で「 7.5 」である事が確認できます。
ちなみに Apache Log4j の時は 10 段階で 10 でした。
※
Remote code injection in Log4j · CVE-2021-44228
例えば、ある組織やチームでは、脆弱性スキャンは情報として扱い、CI パイプラインは止めたくない場合があったりします。
Trivy のオプションに「 exitCode 」があるので、値を「 0 」にして再度 CI パイプラインを動かしてみます。
azure-pipelines.yml の一番最後に「 exitCode: 0 」を追加します。
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
inputs:
version: 7.x
- task: DotNetCoreCLI@2
displayName: DotNetBuild
inputs:
command: build
- task: trivy@1
inputs:
path: .
exitCode: 0
azure-pipelines.yml を Git リポジトリにプッシュします。
$ git add -A
$ git commit -m update
$ git push
CI パイプラインの詳細を確認すると「 trivy 」タスクが成功しています。
「 20230214.2 」をクリックします。
「 #20230214.2 」の左側に緑のチェックアイコンが表示されています。
「 Trivy 」タブをクリックします。
最初に実行した CI パイプラインと同様に Newtonsoft.Json の脆弱性スキャンの結果が表示されています。
Azure Pipelines の Trivy エクステンションを追加する GUI を見ると、trivy コマンドラインのオプションが少ない事がわかります。
azure-pipelines.yml の trivy タスクを Bash タスクに変更して、ローカルで trivy をインストールしたのと同じように組み込みます。
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
inputs:
version: 7.x
- task: DotNetCoreCLI@2
displayName: DotNetBuild
inputs:
command: build
- task: Bash@3
inputs:
targetType: inline
script: |
curl -LO https://github.com/aquasecurity/trivy/releases/download/v0.37.2/trivy_0.37.2_Linux-64bit.tar.gz
tar -xzf trivy_0.37.2_Linux-64bit.tar.gz -C /tmp/
/tmp/trivy fs .
azure-pipelines.yml を Git リポジトリにプッシュします。
$ git add -A
$ git commit -m update
$ git push
CI パイプラインの詳細を確認すると「 Bash 」タスクが成功しています。
ローカルで Trivy を実行した時のように、脆弱性スキャンの結果が表示されています。
「 20230214.3 」をクリックします。
Trivy をコマンドラインで実行したので、赤枠のところに「 Trivy 」タブがありません。
Trivy コマンドラインは、実行結果を SARIF (Static Analysis Results Interchange Format) 形式で出力する事ができます。
SARIF を「 Scans 」タブとして表示できるエクステンションを Azure DevOps に追加します。
Marketplace から「 sarif 」を検索し、「 SARIF SAST Scans Tab 」をクリックします。
「 Get it free 」をクリックします。
「 Install 」をクリックします。
Azure DevOps の「 Organization Settings 」から「 Extensions 」を開きます。
「 SARIF SAST Scans Tab 」が表示されているのを確認します。
azure-pipelines.yml の Bash タスクで SARIF ファイル出力「 --format sarif --output trivy-results.sarif 」を追加します。
PublishBuildArtifacts タスクを追加します。
「 CodeAnalysisLogs 」という名前の ArtifactName に SARIF ファイルを登録します。
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
inputs:
version: 7.x
- task: DotNetCoreCLI@2
displayName: DotNetBuild
inputs:
command: build
- task: Bash@3
inputs:
targetType: inline
script: |
curl -LO https://github.com/aquasecurity/trivy/releases/download/v0.37.2/trivy_0.37.2_Linux-64bit.tar.gz
tar -xzf trivy_0.37.2_Linux-64bit.tar.gz -C /tmp/
/tmp/trivy fs --format sarif --output trivy-results.sarif .
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: trivy-results.sarif
ArtifactName: CodeAnalysisLogs
azure-pipelines.yml を Git リポジトリにプッシュします。
$ git add -A
$ git commit -m update
$ git push
CI パイプラインの詳細を確認すると「 Bash 」タスクが成功しています。
SARIF ファイルを出力したため、Trivy コマンドライン実行時に表示された脆弱性スキャンの結果が表示されなくなりました。
「 20230214.4 」をクリックします。
「 Scans 」タブをクリックします。
Newtonsoft.Json の脆弱性スキャンの結果が表示されています。
CI パイプラインでコードの静的解析や E2E テストを組み込む例は多いと思います。今回検証したライブラリやパッケージの脆弱性スキャンも CI パイプラインに組み込んでみてはいかがでしょうか?
このブログ記事が誰かの何かの参考になればうれしいです。 最後まで読んで頂き、ありがとうございます。