🎄

GitHub Actions CI での trivy config 検証設定例

2024/12/08に公開

本記事は以下の8日目のエントリです。

シフトレフト、大事ですよね。
terraform での IaC における misconfiguration チェックを CI にやらせたい、と考え、以前から個人的に採用していた tfsec でのチェックを、今年はついに trivy へ移行しました。

https://trivy.dev/latest/docs/coverage/iac/terraform/

昨年から 管理単位を小さくして運用する terraform の考え方をベースに、変更された箇所のみをチェックする設定を使っています。
実際の設定と、自身の工夫ポイントを紹介します。

変更箇所のみをチェックする設定

以下のような設定ファイル群を .github 配下に置いています

trivy conf CI の設定 yaml
.github/workflows/trivy-pjx_acr.yml

# refs https://zenn.dev/yokoo_an209/articles/trivy-for-terraform

name: trivy-scan (pjx_acr)

on:
  # push:
  #   paths:
  #     - 'tg/terragrunt.hcl'
  #     - 'tg/aws/envs/acr/**'
  pull_request:
    paths:
      - 'tg/terragrunt.hcl'
      - 'tg/aws/envs/acr/**'

env:
  ENVIRONMENTS_DIR: 'tg/aws/envs'

jobs:
  determine-workdir:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: read
      contents: read
    outputs:
      workdirs: ${{ steps.filter.outputs.workdirs }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - uses: dorny/paths-filter@v3
        id: changes
        with:
          filters: .github/pjx_acr.yml

      - name: filter
        id: filter
        run: |
          WORKDIRS=$(echo '${{ toJSON(steps.changes.outputs) }}' | jq '. | to_entries[] | select(.value == "true") | .key')
          echo "workdirs=$(echo $WORKDIRS | jq -sc '.')" >> $GITHUB_OUTPUT

  # trivy config scan
  trivy-scan:
    needs: determine-workdir
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: write
      pull-requests: write
    if: needs.determine-workdir.outputs.workdirs != '[]'
    strategy:
      matrix:
        workdir: ${{ fromJSON(needs.determine-workdir.outputs.workdirs) }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Trivy Scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: "config"
          #severity: "HIGH,CRITICAL"
          #exit-code: '1'
          scan-ref: '${{ github.workspace }}/${{ env.ENVIRONMENTS_DIR }}/${{ matrix.workdir }}'
          #trivyignores: ${{ github.workspace }}/.configs/.trivyignore
          output: trivy-scan-result.txt

      - name: Format Trivy Scan Result
        run: |
          if [ -s trivy-scan-result.txt ]; then
            # ファイルに内容がある場合
            echo -e "## :warning: trivy config 結果 :warning: (${{ env.ENVIRONMENTS_DIR }}/${{ matrix.workdir }})\n<details><summary>詳細</summary>\n\n\`\`\`\n$(cat trivy-scan-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
          else
            # ファイルが空の場合
            echo -e "## trivy config 結果 (${{ env.ENVIRONMENTS_DIR }}/${{ matrix.workdir }})\n脆弱性が検知されませんでした。" > formatted-trivy-result.md
          fi

      - name: Comment PR with Trivy scan results
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          recreate: true
          GITHUB_TOKEN: ${{ secrets.github_token }}
          path: formatted-trivy-result.md

dorny/paths-filter が見ている、変更箇所だけ CI を走らせる設定はこんな感じです。

.github/pjx_acr.yml
acr/iam/oidc-role:
  - 'tg/terragrunt.hcl'
  - 'tg/aws/envs/acr/iam/oidc-role/**'
  - 'tg/tf-modules/tgp-oidc/**'

acr/res/sops-key:
  - 'tg/terragrunt.hcl'
  - 'tg/aws/envs/acr/res/sops-key/**'

acr/res/ecr:
  - 'tg/terragrunt.hcl'
  - 'tg/aws/envs/acr/res/ecr/**'

acr/mng/secrets:
  - 'tg/terragrunt.hcl'
  - 'tg/aws/envs/acr/mng/secrets/**'

なお、ディレクトリ構成は以下のようにしています。

ディレクトリ構成
tg
├── aws
│   └── envs
│       └── acr
│           ├── iam
│           │   └── oidc-role
│           ├── mng
│           │   └── secrets
│           └── res
│               ├── ecr
│               └── sops-key
└── tf-modules
    └── tgp-oidc

GitHub Actions には label 設定 API に文字数制限(50)あることを8月に知った[3]ので、以来、収まるように独自定義の略称を用いています。
across を acr、manage を mng、resource を res とかです。

独自に工夫しているポイント

(抜粋)
      - name: Trivy Scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: "config"
          #severity: "HIGH,CRITICAL"
          #exit-code: '1'
          scan-ref: '${{ github.workspace }}/${{ env.ENVIRONMENTS_DIR }}/${{ matrix.workdir }}'
          #trivyignores: ${{ github.workspace }}/.configs/.trivyignore
          output: trivy-scan-result.txt

      - name: Format Trivy Scan Result
        run: |
          if [ -s trivy-scan-result.txt ]; then
            # ファイルに内容がある場合
            echo -e "## :warning: trivy config 結果 :warning: (${{ env.ENVIRONMENTS_DIR }}/${{ matrix.workdir }})\n<details><summary>詳細</summary>\n\n\`\`\`\n$(cat trivy-scan-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
          else
            # ファイルが空の場合
            echo -e "## trivy config 結果 (${{ env.ENVIRONMENTS_DIR }}/${{ matrix.workdir }})\n脆弱性が検知されませんでした。" > formatted-trivy-result.md
          fi

      - name: Comment PR with Trivy scan results
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          recreate: true
          GITHUB_TOKEN: ${{ secrets.github_token }}
          path: formatted-trivy-result.md

個人的な方針で、以下2つを意図的にそうしてます。

1. exit-code: '1' をコメントアウトしている

これ、有効にしておくと、misconfig を検出したときに CI が止まっちゃって、どこにどういう問題があるかわからなくなっちゃうんですよね。。。
ということで、コメントアウトして、Format Trivy Scan Result のところで「ファイルに内容がある場合」に来るので、 :warning: で目立つようにするというやり方をしてます。

2. trivyignores をまとめない

理由あって ignore する、は各現場であり得るものだと思っていて、何を ignore しているのかを方針的に明確化するために ${{ github.workspace }}/.configs/.trivyignore に集約するのも1つの考え方、と思います。
が、自分は集約ファイルを使わず、問題箇所に1つ1つ
#trivy:ignore:AVD-AWS-0098 # (LOW): Secret explicitly uses the default key.
のように ignore 指定を入れるようにしています。「ここにはこういう misconfiguration がある」がわかるようにしておく意図です。

これには賛否あると思いますが、自分はこうしています。

こんな感じで検出

(ひとまず記事公開したいので、あとでスクショ貼ります。)

最後まで読んでいただきありがとうございました。

明日9日目の予定は以下になっています。お楽しみに。

脚注
  1. 7日目は fumiken さんの Fargate接続ツール(MP社製)のご紹介 でした。 ↩︎

  2. 7日目は @roulgonzaress さんの GHAをKEDAで動かす でした。 ↩︎

  3. https://zenn.dev/link/comments/41b88dcf6b4f00 の真ん中あたり参照 ↩︎

Discussion