📚

Trivy: セキュリティテスト&コンプライアンステストツールの導入手順

2024/02/03に公開

インフラの運用ではデータ破損、ネットワークダウン、セキュリティインシデントといったサービスの稼働に重大な影響を与える問題が発生する可能性があるため、可能な限りそのような事態が起きないよう常に厚くテストしておきたいところである。

そのような問題への対応策として、Trivyは有効なツールである。

Trivyの具体的な利用方法をざっと列挙すると

  • Misconfiguration Scanning: Terraform、CloudFormation、ARM templates、Kubernetes、Helm Charts、 Dockerfileなどの定義ファイルに脆弱性につながる設定が含まれていないかスキャン

    (またDBの削除保護設定が行われているかのチェックなど、Regoでカスタムポリシーを定義することで追加でスキャン可能)

  • Secret Scanning: プロジェクト内にAWSやGCPのクレデンシャル情報が含まれていないかスキャン

  • Vulnerability Scanning: OSパッケージ、各プログラム言語、Kubernetes等の公開脆弱性データベースを用いて、ファイルシステム等に対する脆弱性スキャン

  • License Scanning: システムで使用しているOSパッケージやアプリケーションライブラリにライセンス上の問題があるものが含まれていないかスキャン

などがある。

これらのスキャンを必要に応じて、ローカルやクラウドのインスタンス上のファイルシステム、DockerコンテナイメージやAMIなどのマシンイメージ、GitHubなどのリポジトリ、さらには稼働中のKubernetesクラスタやAWS上に構築したインフラに対して行うことができる。

以下ではソースコードプロジェクトにTrivyを導入する手順を説明する。

インストール

Macのhomebrewを使用している場合、Trivyは以下のコマンドでインストールできる。

$ brew install trivy

シェルでインストールする場合は、以下のコマンド。

$ curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.48.3 # <-インストールバージョンを指定

その他のインストール方法はこちら

CIでTrivyを実行する手順についても、主要なCIやBuildツールのものが一通り揃っている。

インストールが完了したら、実際にTrivyを使用してみる。

Trivyにはスキャンにあたって、様々な設定項目コマンドラインオプションがあるが、ソースコードプロジェクト内の脆弱性スキャンを行うだけなら、以下のコマンドだけで、上で挙げたすべてのスキャンを実施するに事足りる。

# 以降に書かれているスキャンの検証では、このコマンドしか使わないようにしている
$ trivy fs --scanners vuln,secret,misconfig,license --exit-code=1 --ignorefile ./.trivyignore.yaml ./

スキャンはカレントディレクトリからサブディレクトリ内へと再起的に行われる。

--exit-code=1は1つでもチェックに引っかかった場合のexit codeを指定する。

デフォルトは--exit-code=0で、CIなどで使う場合には1つでもチェックに引っかかったらエラー終了したいので、この設定が必要になる。

--ignorefile ...については後述。

検証

Misconfiguration Scanning

GCSバケットを作成するTerraformの設定を持つインフラプロジェクトを、trivyコマンド(上記)でスキャンしてみる。

プロジェクトは以下のように構成する。

$ cd /tmp/sample-project
$ tree -a
.
├── .trivyignore.yaml
└── main.tf

1 directory, 2 files

各ファイルの内容は以下のようになる。

main.tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "5.25.0"
    }
  }
  required_version = "1.8.1"
}

provider "google" {
  project = "your-gcp-project"
  region  = "asia-northeast1"
}

variable "env" {
  type = string
}

resource "google_storage_bucket" "example" {
  name          = "${var.env}-example-bucket"
  location      = "ASIA"
  storage_class = "MULTI_REGIONAL"
  versioning {
    enabled = true
  }
  uniform_bucket_level_access = true
  public_access_prevention = "enforced"
}
.trivyignore.yaml
# 設定の定義方法は後述
misconfigurations:

secrets:

vulnerabilities:

licenses:

以上のファイルの作成が完了したらスキャンコマンドを実行する。

実行結果は以下のようになる。

$ trivy fs --scanners vuln,secret,misconfig,license --exit-code=1 --ignorefile ./.trivyignore.yaml ./
2024-02-02T22:16:40.927+0900	INFO	Need to update DB
2024-02-02T22:16:40.927+0900	INFO	DB Repository: ghcr.io/aquasecurity/trivy-db
2024-02-02T22:16:40.927+0900	INFO	Downloading DB...
...

main.tf (terraform)

Tests: 2 (SUCCESSES: 1, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

LOW: Storage bucket encryption does not use a customer-managed key.
════════════════════════════════════════════════════════════════════════════════════════
Using unmanaged keys makes rotation and general management difficult.

See https://avd.aquasec.com/misconfig/avd-gcp-0066
────────────────────────────────────────────────────────────────────────────────────────
 main.tf:20-29
────────────────────────────────────────────────────────────────────────────────────────
  20 ┌ resource "google_storage_bucket" "example" {
  21 │   name          = "${var.env}-example-bucket"
  22 │   location      = "ASIA"
  23 │   storage_class = "MULTI_REGIONAL"
  24 │   versioning {
  25 │     enabled = true
  26}
  27 │   uniform_bucket_level_access = true
  28 │   public_access_prevention = "enforced"
  29}
────────────────────────────────────────────────────────────────────────────────────────

スキャン結果のメッセージを見ると、GCSストレージの暗号化用の鍵について、自前で管理したものを使うべきとの警告が出る。

これに対して、自前での鍵の管理は鍵の消失などのオペレーションミスのリスクが、クラウド側で管理してもらうのに比べてリスクが高いと考えた場合、このスキャンは行われないようにしたい。

その場合、このチェックのIDであるavd-gcp-0066を以下のように.trivyignore.yamlに設定することで、スキャンをスキップすることができる。

.trivyignore.yaml
misconfigurations:
  # 現v0.51.1バージョンでは、IDは大文字で指定しないと認識しないので注意
  - id: AVD-GCP-0066

secrets:

vulnerabilities:

licenses:

再度、同じスキャンコマンドを実行すると、今度はavd-gcp-0066のエラーが発生しなくなり、正常終了する。

Secret Scanning

secretsの挙動を試したい場合、以下のコマンドを実行することで確認できる。

$ echo "SLACK_TOKEN=xoxb-000000000000-000000000000-000000000000000000000000" > .env
$ trivy fs --scanners vuln,secret,misconfig,license --ignorefile ./.trivyignore.yaml ./
2024-02-02T23:44:19.076+0900	INFO	Vulnerability scanning is enabled
2024-02-02T23:44:19.076+0900	INFO	Misconfiguration scanning is enabled
2024-02-02T23:44:19.078+0900	INFO	Secret scanning is enabled
...

.env (secrets)

Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)

HIGH: Slack (slack-access-token)
════════════════════════════════════════════════════════════════════════════════════════
Slack token
────────────────────────────────────────────────────────────────────────────────────────
 .env:1
────────────────────────────────────────────────────────────────────────────────────────
   1 [ SLACK_TOKEN=*****************-000000000000-000000000000000000000000
   2
────────────────────────────────────────────────────────────────────────────────────────

なお、ドキュメントにはAWS、GCP、GitHub、GitLab、Slack等の認証情報をチェックすると書いている。

Slackの他にGCPのクレデンシャルJSONは正しく検知できたが、AWSの実験用AWS_ACCESS_KEY_IDには反応しなかったり、実利用可能なGITHUB_TOKENを含めてもエラーにならなかったりと、多少怪しい挙動を感じる。

secretsのコードの正規表現を調べてみれば原因がわかるかもしれないが、しばらくすれば直るような気もしているので、いったん調査は後回しにしている。

他にもXXX_PASSWORDのような自作のパスワード引き渡し用環境変数などについてもチェックしたい場合は、カスタムの設定を書いて運用する必要があるなど、導入には多少時間がかかるかもしれない。

あとMarkDown(.md)ファイルはデフォルトでsecretsのスキャン対象とならない点に注意が必要となる。

他にも、名前にtestやexampleといった文字列が含まれているファイルやディレクトリについても対象外となるため、この挙動を無効化したい場合はルートディレクトリ直下にtrivy-secret.yamlという名前の設定ファイルを作成して以下のように定義を行う必要がある。

(除外する際に指定するIDはこちらを参照)

disable-allow-rules:
  - markdown
  - tests
  - examples

Vulnerability Scanning

パッケージ脆弱性のスキャンは、以下のコマンドなどで試すことができる。

$ go mod init sample-project
# 自分が最近遭遇した脆弱性のあるパッケージ&バージョン
$ go get golang.org/x/crypto@v0.13.0
$ trivy fs --scanners vuln,secret,misconfig,license --ignorefile ./.trivyignore.yaml ./

...
go.mod (gomod)

Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)

┌─────────────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────┐
│       Library       │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │                          Title                          │
├─────────────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────┤
│ golang.org/x/crypto │ CVE-2023-48795 │ MEDIUM   │ fixed  │ 0.13.0            │ 0.17.0        │ ssh: Prefix truncation attack on Binary Packet Protocol │
│                     │                │          │        │                   │               │ (BPP)                                                   │
│                     │                │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2023-48795              │
└─────────────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────────────┘

対応方法として、Fixed Versionに書かれているバージョン以上にバージョンアップすることでエラーは出なくなる。

$ go get golang.org/x/crypto@v0.17.0
go: upgraded golang.org/x/crypto v0.13.0 => v0.17.0

License Scanning

同様にライセンスのスキャンは、以下のコマンドなどで試す。

$ go get github.com/hashicorp/terraform-exec/tfexec@v0.20.0
$ trivy fs --scanners vuln,secret,misconfig,license --ignorefile ./.trivyignore.yaml ./

...
┌──────────────────────────────────────────┬──────────────────┬────────────────┬──────────┐
│                 Package                  │     License      │ Classification │ Severity │
├──────────────────────────────────────────┼──────────────────┼────────────────┼──────────┤
│ github.com/apparentlymart/go-textseg/v15 │ Apache-2.0       │ Notice         │ LOW      │
│                                          ├──────────────────┤                │          │
│                                          │ MIT              │                │          │
│                                          ├──────────────────┤                │          │
│                                          │ Unicode-DFS-2016 │                │          │
├──────────────────────────────────────────┼──────────────────┼────────────────┼──────────┤
│ github.com/hashicorp/go-version          │ MPL-2.0          │ Reciprocal     │ MEDIUM   │
├──────────────────────────────────────────┤                  │                │          │
│ github.com/hashicorp/terraform-exec      │                  │                │          │
├──────────────────────────────────────────┤                  │                │          │
│ github.com/hashicorp/terraform-json      │                  │                │          │
├──────────────────────────────────────────┼──────────────────┼────────────────┼──────────┤
│ github.com/zclconf/go-cty                │ MIT              │ Notice         │ LOW      │
├──────────────────────────────────────────┼──────────────────┤                │          │
│ golang.org/x/text                        │ BSD-3-Clause     │                │          │
└──────────────────────────────────────────┴──────────────────┴────────────────┴──────────┘

おわり

Trivyの基本的な使用方法の説明は以上となる。

割と重要な機能であるRegoによるカスタムポリシーの実行には振れていないが、現在インフラプロジェクトにおけるRego/ポリシーチェックの発射台として、Trivyの他にも、TFLint、OPA、Conftestなどの候補が挙げられている状態で、どのツールに実行を任せるべきかまだ決めていない。

仮にTrivyに一本化するとなった場合には、検証方法を追記する。

追記

カスタムポリシーの実行環境の検証完了。

https://zenn.dev/erueru_tech/articles/44b35947044f91

ただしRegoの発射台はConftestに一本化された。

https://zenn.dev/erueru_tech/articles/6a9d502efc8d7b

Discussion