LoginSignup
3
3

More than 1 year has passed since last update.

Terraform でTrivy の脆弱性スキャンとSecurity Hub を統合したCIを構築する

Last updated at Posted at 2022-05-22

はじめに

本記事では、Trivy によるコンテナイメージの脆弱性スキャンとSecurity Hub を統合したCI(Continuous Integration)環境を構築するTerraform のコードをサンプルとして共有しています。コンテナイメージのセキュリティ対策の一例として参考になればと思います。

また、補足として、CodeBuild のビルド内のTrivy に関する処理について、簡単に説明をしています。

Terraform で構築する全体構成図

構成の概要

アーキテクチャは上記のAWS Security Blog を元にしています。

Dockerfile を含むコードをCodeCommit にプッシュするとCodePipeline が実行され、CodeBuild でビルドしたコンテナイメージを、Trivy で脆弱性スキャンをしています。

その結果、深刻度が CRITICAL な脆弱性を検出した場合は、ECR にはプッシュせずに、CIを停止させ、Findings(結果)をSecurity Hub に送信し、確認できるようにしています。

コンテナイメージに CRITICAL な脆弱性を検出しなかった場合は、安全なイメージとしてECR にプッシュしています。

Trivy とは

It is designed to be used in CI. Before pushing to a container registry or deploying your application, you can scan your local container image and > other artifacts easily. See Integrations for details.
CIで使用することを想定しています。コンテナレジストリへのプッシュやアプリケーションのデプロイの前に、ローカルのコンテナイメージや他のアーティファクトを簡単にスキャンすることができます。詳細はIntegrationsを参照してください。

Security Hub とは

Security Hubは、AWSアカウント、サービス、およびサポートされているサードパーティパートナー製品全体からセキュリティデータを収集し、セキュリティトレンドを分析して、最も優先度の高いセキュリティ問題を特定するのに役立ちます。

Terraform のコードと構成

$ tree aws-tf-trivy-securityhub-ci 
aws-tf-trivy-securityhub-ci
├── main.tf
└── modules
    └── codepipeline
        ├── buildspec.yml
        ├── cloudwatch_event.tf
        ├── cloudwatch_logs.tf
        ├── codebuild.tf
        ├── codecommit.tf
        ├── codepipeline.tf
        ├── ecr.tf
        ├── iam.tf
        ├── kms.tf
        ├── provider.tf
        ├── s3.tf
        └── variables.tf

2 directories, 13 files

使い方

Terraform の動作確認環境

$ terraform -version
Terraform v1.1.9
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v4.15.1

変数の設定

main.tf に下記の変数を設定します。

変数名 説明
profile 各AWSのプロファイル名
region リージョン
prefix 各AWSのリソースに付与するプレフィックス
env 環境を識別するプレフィックス
repository_name CodeCommit のリポジトリ名
branch_name ブランチ名
ecr_name ECR のリポジトリ名
main.tf
module "codepipeline" {
  source = "./modules/codepipeline"

  Account = {
    profile = "YOUR AWS ACCOUNT PROFILE NAME"
    region  = "ap-northeast-1"
  }

  # Project Prefix
  prefix = "prefix"

  # Environment Prefix
  env = "test"

  # Codecommit Repository Name
  repository_name = "trivy-securityhub-ci"
  # Branch Name
  branch_name = "master"

  # ECR Repository Name
  ecr_name = "trivy-securityhub-ci"
}

Terraform の実行

$ terraform init
$ terraform plan
$ terraform apply

Security Hub の有効化

Security Hub を有効化していない環境であれば、有効化します。

Security Hub と Aqua Security の統合

Security Hub と Aqua Security を統合します。
[Security Hub] > [統合] > Aqua Security で検索 > [結果を受け入れる]

Dockerfile をCodeCommit にプッシュ

動作確認として、脆弱性を含むテスト用のDockerfile をCodeCommit にプッシュします。

Dockerfile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

FROM composer:1.7.2

RUN git clone https://github.com/aquasecurity/trivy-ci-test.git && cd trivy-ci-test && rm Cargo.lock && rm Pipfile.lock

CMD apk add --no-cache mysql-client
ENTRYPOINT ["mysql"]

CodePipeline > CodeCommit > CodeBuild の実行

CodeCommit の変更イベントを検知してCodePipeline > CodeBuild が実行されます。

CodeBuildでTrivy による脆弱性スキャン

Trivy のコンテナイメージのスキャンによって、脆弱性が検出され、ビルドのステータスが失敗となり、Findings がSecurity Hub に送信されます。

ビルドログ

フェーズ詳細

Security Hub でFindings を確認

Security Hub で検出結果を確認できます。
[Security Hub] > [統合] > [結果を参照]

検出結果

補足: CodeBuild のビルドの仕様について

Trivy を使った脆弱性スキャンを組み込んだビルドの各フェーズごとの処理について説明します。

参考:

buildspec

buildspec.yml は下記で構成しています。

buildspec.yml
version: 0.2

env:
  variables:
    DOCKER_BUILDKIT: "1"

phases:
  install:
    runtime-versions:
      docker: 19
    commands:
      - echo Trivy Install...
      # - TRIVY_VERSION=0.28.0
      - TRIVY_VERSION=$(curl -sS https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
      - rpm -ivh https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.rpm
      - trivy --version
      - echo Install completed on `date`
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - echo $AWS_DEFAULT_REGION
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME
      - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t ${REPOSITORY_URI}:${IMAGE_TAG} .
      - echo Build completed on `date`
      - echo Trivy Scan started on `date`
      - AWS_REGION=$AWS_DEFAULT_REGION
      - AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID
      - trivy image --no-progress --format template --template "@/usr/local/share/trivy/templates/asff.tpl" -o tmp-report.asff --ignore-unfixed --exit-code 1 --severity CRITICAL ${REPOSITORY_URI}:${IMAGE_TAG}
  post_build:
    commands:
      - echo Trivy Scan completed on `date`
      - |
        if [ $CODEBUILD_BUILD_SUCCEEDING == "1" ]; then
          docker push ${REPOSITORY_URI}:${IMAGE_TAG} ;
          printf "[{\"name\":\"${IMAGE_REPO_NAME}\",\"imageUri\":\"%s\"}]" $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json ;
          echo Build stage successfully completed on `date`
        else
          cat tmp-report.asff | jq '.Findings' > report.asff ;
          aws securityhub batch-import-findings --findings file://report.asff --region $AWS_DEFAULT_REGION ;
        fi
artifacts:
  files:
    - imagedefinitions.json

phases/install

このビルドフェーズでは、Trivy を下記コマンドでインストールしています。

#TRIVY_VERSION=0.28.0 バージョンを固定したい場合は変数にバージョンを指定してください
TRIVY_VERSION=$(curl -sS https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
rpm -ivh https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.rpm

参考: Installation

phases/build

このビルドフェーズでは、ビルドしたイメージをTrivy でスキャンし、深刻度が CRITICAL な脆弱性を検出した場合は、イメージに問題があるとし、ビルドステージを失敗とします。(CIを停止とします。)
そして、Amazon Security Finding Format (ASFF) に準拠した Findings を生成しています。

AWS_REGION=$AWS_DEFAULT_REGION
AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID
trivy image --no-progress --format template --template "@/usr/local/share/trivy/templates/asff.tpl" -o tmp-report.asff --ignore-unfixed --exit-code 1 --severity CRITICAL ${REPOSITORY_URI}:${IMAGE_TAG}

参考: Scan Option と ASFFについて

phases/post_build

このビルドフェーズでは、コンテナイメージに CRITICAL な脆弱性を検出しなかった場合は、安全なイメージとしてECR にプッシュし、ビルドステージを正常とします。

CRITICAL な脆弱性を検出した場合は、生成したFindings をSecurity Hubに送信しています。
このサンプルでは、AWS CLI を使って送信しています。

if [ $CODEBUILD_BUILD_SUCCEEDING == "1" ]; then
   docker push ${REPOSITORY_URI}:${IMAGE_TAG} ;
   printf "[{\"name\":\"${IMAGE_REPO_NAME}\",\"imageUri\":\"%s\"}]" $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json ;
   echo Build stage successfully completed on `date`
else
   cat tmp-report.asff | jq '.Findings' > report.asff ;
   aws securityhub batch-import-findings --findings file://report.asff --region $AWS_DEFAULT_REGION ;
fi

参考: Upload findings to Security Hub

さいごに

初歩的な内容ですが、DevSecOps の仕組みを検討する上で少しでも参考になれば幸いです。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3