コネヒト開発者ブログ

コネヒト開発者ブログ

trivyとGithub Actionsを使用しTerraform設定ファイルのセキュリティスキャンを実行する仕組みを作りました

この記事はコネヒトアドベントカレンダー21日目の記事です。

コネヒト Advent Calendar 2023って?
コネヒトのエンジニアやデザイナーやPdMがお送りするアドベント カレンダーです。
コネヒトは「家族像」というテーマを取りまく様々な課題の解決を 目指す会社で、
ママの一歩を支えるアプリ「ママリ」などを 運営しています。

adventar.org

はじめに

コネヒトのプラットフォームグループでインフラ関連を担当している@yosshiです。 今年の7月に入社してから早いもので半年が経ちました。時が経つのは本当に早いですね。

今回のブログでは、セキュリティスキャンツールであるtrivyを使って、自動的にIaC (Infrastructure as Code)スキャンを実行する仕組みを構築した話をしたいと思います。

弊社ではインフラ構成をTerraform利用して管理するようにしており、それをモノレポの構成で運用しています。

インフラリソースの作成・変更・削除をする際には必ず相互レビューを必須としているものの、人的なチェックのみに依存しているためファイルの設定ミスやセキュリティ上の見落としが潜在的なリスクとなっていました。

これらを事前に検知するツールを調べていたところtrivyの存在を知り、今回導入に至りました。

trivyとは

  • コンテナイメージやアプリケーションの依存ライブラリ・OSのパッケージなどを迅速にスキャンし、セキュリティリスクを効率的に検出するセキュリティツールです。
  • 当初はコンテナのセキュリティ問題に焦点を当てたツールとして開発されたようですが、後にTerraformやKubernetesなどの設定ファイルのチェック機能も追加されました。
  • 設定ファイルのチェックでは、設定ミスやセキュリティのベストプラクティスに沿っていない構成などを検知することができます。

参考:https://github.com/aquasecurity/trivy

(参考)スキャン可能な対象 2023/12時点

  • コンテナイメージ
  • ファイルシステム
  • リモートGitリポジトリ
  • 仮想マシンイメージ
  • Kubernetes
  • AWS

(参考)検出可能な内容 2023/12時点

  • OSパッケージとソフトウェア依存関係(SBOM)
  • 既知の脆弱性(CVE)
  • IaCの問題と設定ミス
  • 機密情報と秘密
  • ソフトウェアライセンス

上記の通りtrivyでは、Terraformのコードだけでなくさまざまなセキュリティスキャン行うことができます。

trivy自体の詳しい説明はここでは割愛するので、詳しくは公式サイトや他の方の記事などをご確認いただけると幸いです。

Github Actionsでの実装

では早速ですが実装内容の説明に移りたいと思います。 今回はTerraformコードのスキャンをCIに組み込んでいます。 弊社では CIツールとしてGithub Actionsを利用しているため、今回もこちらを利用します。

スキャン実行は、trivy公式で用意しているGithub Actions用のツール(tricy-action)があるので、こちらをそのまま利用しています。

背景

まず、前提条件となる弊社のディレクトリ構造を説明します。

弊社では、サービスで共通利用するリソース(base_system)と各サービスで利用するリソース(product_system)とでディレクトリを分けており、 product_sytem以下にはサービスごと関連するリソースが紐づいています。以下のようなイメージです。

.
├── base_system
│   ├── common
│   │   └── terraform
│   ├── privilege
│   │   └── terraform
│   .
│   .
└── product_system
    ├── (サービス1)
    │   └── terraform
    ├── (サービス2)
    │   └── terraform
    .
    .
    .

実装方針と内容

実装したGithub Actionsのコードは以下の通りです。

主に以下のことをやっています。

  1. シェルスクリプト(sync-updated-dirs-to-work-dir.sh)の実行
    • mainブランチとプルリクエスト中のブランチのコードの差分を検知
    • 差分となっているディレクトリを作業用のディレクトリに同期
  2. 作業ディレクトリに対してスキャン実行
  3. 検出されたスキャン結果をPRのコメントに残す。

Github Actionsのコードは以下の通りです。

name: trivy-scan

on:
  pull_request:
    types: [opened, reopened, synchronize]

permissions:
  id-token: write
  contents: read
  pull-requests: write

jobs:
  trivy_scan:
    name: Run Trivy Scan
    runs-on: ubuntu-latest
    steps:
      - name: Clone repo
        uses: actions/checkout@v4

      - name: fetch origin/main for getting diff
        run: git fetch --depth 1 origin $GITHUB_BASE_REF

      - name: Sync Updated Dir to Work Dir
        env:
          GITHUB_TOKEN: ${{ secrets.github_token }}
        run: |
          bash utils/scripts/sync-updated-dirs-to-work-dir.sh

      - name: Trivy Scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          severity: 'HIGH,CRITICAL'
          scan-ref: scan_work_dir
          output: trivy-scan-result.txt

      - name: Format Trivy Scan Result
        run: |
          if [ -s trivy-scan-result.txt ]; then
            # ファイルに内容がある場合
            echo -e "## 脆弱性スキャン結果\n<details><summary>詳細</summary>\n\n\`\`\`\n$(cat trivy-scan-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
          else
            # ファイルが空の場合
            echo -e "## 脆弱性スキャン結果\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

詳しく見ていきます。

まずは変更差分となったディレクトリを調べるため、Github Actionsの中でシェルスクリプトを実行しています。

ここでは一時的なディレクトリを用意し、プルリクエスト上で変更が発生したディレクトリの内容を作業用のディレクトリに同期するという作業を行なっています。

Github Actionsの関連部分は以下です。

- name: Sync Updated Dir to Work Dir
   run: |
     bash utils/scripts/sync-updated-dirs-to-work-dir.sh

sync-updated-dirs-to-work-dir.shはrsyncをwrapしたもので、前述の通りプルリクエスト上で変更が発生したterraformディレクトリを、スキャン実行する一時的な作業用ディレクトリに同期する処理をしています。1

細かな実装は内部事情に特化したものとなっているため割愛しますが、例えば、base_system/common/terraform, base_system/privilege/terraform, product_system/(サービス1)/terraformのディレクトリで変更が発生している場合、このスクリプトを実行することで作業用ディレクトリ(scan_work_dir)が作成され、以下のようなディレクトリ構造となります。

.
├── base_system
│   ├── common
│   │   └── terraform
│   └── privilege
│   │   └── terraform
│   .
│   .
├── product_system
│   ├── (サービス1)
│   │   └── terraform
│   ├── (サービス2)
│   │   └── terraform
│   .
│   .
└── scan_work_dir (ここのディレクトリにtrivyによるスキャンを実行する)
    ├── base_system
    │   ├── common
    │   │   └── terraform
    │   └── privilege
    │   │   └── terraform
    └── product_sytem
        └── (サービス1)
            └── terraform

次に同期してきた作業用ディレクトリに対してスキャンを実行します。
ここでは前述の通り、公式が用意しているtricy-actionを利用しています。

- name: Trivy Scan
  uses: aquasecurity/trivy-action@master
  with:
   scan-type: 'config'
   severity: 'HIGH,CRITICAL'
   scan-ref: scan_work_dir
   output: trivy-scan-result.txt

オプションの内容は以下の通りです。

  • scan-type: 'config'
    • configはTerraformなどの設定ファイルをスキャンする際に使用する。
  • scan-ref: scan_work_dir
    • 作業用ディレクトリ scan_work_dir を指定している。
  • output: trivy-scan-result.txt
    • スキャンした結果をテキストファイルtrivy-scan-result.txtに出力する。
    • このテキストファイルは次のアクションでフォーマットを整形して出力するために使用している。
  • serverity
    • 脆弱性の深刻度で'CLITICAL'と'HIGH'を指定しています。
    • ここのレベルは、CVSS2によって定量化された脆弱性の深刻度をもとに設定されています。

他のオプションなどについてはtricy-actionのページをご確認ください。

次に、前のアクションで出力したテキストファイルを見やすく整形した上で、marocchino/sticky-pull-request-commentを使用しプルリクエストのコメント欄に出力しています。

プルリクエスト更新時には、既存の脆弱性に関するコメントを削除した上で新規コメントを残して欲しかったので、その点でmarocchino/sticky-pull-request-comment(recreate: trueのオプション指定)を利用することで楽に実装することができました。

# スキャンした結果を整える
- name: Format Trivy Scan Result
  run: |
    if [ -s trivy-scan-result.txt ]; then
      # ファイルに内容がある場合
      echo -e "## 脆弱性スキャン結果\n<details><summary>詳細</summary>\n\n\`\`\`\n$(cat trivy-scan-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
    else
      # ファイルが空の場合
      echo -e "## 脆弱性スキャン結果\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

スキャン結果

  • 脆弱性が検知されなかった場合

  • 脆弱性が検知された場合

「詳細」部分を開くと以下のような形で表示されています。

今回のケースだとCritical0件、High61件の脆弱性が検知されていることがわかります。

base_system/privilege/terraform/xxx.tf (terraform)
=====================================================================
Tests: 75 (SUCCESSES: 14, FAILURES: 61, EXCEPTIONS: 0)
Failures: 61 (HIGH: 61, CRITICAL: 0)

HIGH: IAM policy document uses sensitive action 'autoscaling:Describe*' on wildcarded resource '*'
════════════════════════════════════════
You should use the principle of least privilege when defining your IAM policies. This means you should specify each exact permission required without using wildcards, as this could cause the granting of access to certain undesired actions, resources and principals.

See https://avd.aquasec.com/misconfig/avd-aws-0057
────────────────────────────────────────
 base_system/privilege/terraform/xxx.tf:376
   via base_system/privilege/terraform/xxx.tf:376 (aws_iam_policy.xxx.xxx)
    via base_system/privilege/terraform/xxx.tf:375-438 (aws_iam_policy.xxx.xxx)
     via base_system/privilege/terraform/xxx.tf:373-439 (aws_iam_policy.xxx)
────────────────────────────────────────
 373   resource "aws_iam_policy" "xxx" {
 ...
 376 [     Version = "2012-10-17",
 ...
 439   }
────────────────────────────────────────
・
・
・(以下省略)

上記の例では、重要度「HIGH」の脆弱性が検知されています。

IAMポリシーは最小特権で付与するべきなので、ワイルドカード使うのはリスクがありますよ、という指摘のようです。

補足

検出された脆弱性の中で、この内容は指摘する対象から除外したい、というケースもあると思います。

その場合は.trivyignoreというファイルをトップディレクトリに置くことで勝手に参照して検出対象から除外してくれます。

以下のような形で記載します。

.trivyignore

AVD-AWS-0057
AVD-AWS-XXXX

(参考)

以下のようにtrivyignoresのオプションを利用することで、トップディレクトリに配置するだけでなく別のディレクトリに配置しているファイルを参照させたり、.trivyignore以外の別のファイル名を指定することもできるようです。

- name: Trivy Scan
  uses: aquasecurity/trivy-action@master
  with:
   scan-type: 'config'
   severity: 'HIGH,CRITICAL'
   scan-ref: trivy_temp_dir
   output: trivy-scan-result.txt
   trivyignores: test-trivyignore

まとめ

今回はtrivyとGitHub Actionsを活用し、Terraformでのセキュリティ上のリスクを効果的に検知する仕組みを構築しました。

今回検知されたものについては、優先度の高いものから順次改善していきたいと思います。

また、冒頭に説明した通り、trivyでは他にも様々なものを検知してくれる機能があるので、 Terraformの設定だけでなく色々な場面での活用を検討していきたいと思います。


  1. 変更が発生したディレクトリの抽出には git diff origin/main --name-only の結果をパースしています。
  2. CVSS( Common Vulnerability Scoring System ) = 共通脆弱性評価システム