はじめに
本記事では、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 のリポジトリ名 |
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 にプッシュします。
# 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 は下記で構成しています。
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 の仕組みを検討する上で少しでも参考になれば幸いです。