[テンプレート公開]GitHub ActionsでECRのイメージスキャンでCI/CDするワークフローを組んでみた
夜にエナドリをキメるとめちゃめちゃ作業が捗ることに気がついてしまった、もこ@札幌オフィスです。
つい先月、ECRでイメージスキャン機能が提供されました!
これにより、ECR単体で「デプロイ前にコンテナイメージに脆弱性がないかをスキャン」することや、「CloudWatch Events / Lambdaと連携して実行中のイメージに新たな脆弱性がないかを定期的にスキャン」することができます。
今回は、GitHub Actionsを使ってコンテナをECSにデプロイする前にスキャンしてクリティカルな脆弱性があったらワークフローを停止するようなものを作っていきます。
テンプレート
裏側ではNode.js + AWS SDKを利用して、イメージのスキャンを開始、スキャン結果を取得、パイプラインを進めるか否かの判定をするようにしています。
GitHub Actionsのテンプレートは公開していますので、ymlから数行追加するだけでワークフローにコンテナスキャンを埋め込めるようにしました。
https://github.com/mokocm/ecr-imagescan-action
使い方
ワークフローでイメージをスキャンしたい任意のタイミングでこのようにmokocm/ecr-imagescan-action@v3
を利用する形で追記してあげればOKです。
1 2 3 4 5 6 7 8 | - name : Image Scan id: image-scan uses: mokocm/ecr-imagescan-action@v3 with: image-digest: "スキャンするイメージのダイジェスト(SHA256)" image-tag: "スキャンするイメージのTag" repository-name: "ECRのリポジトリ" trigger-severity: "INFORMATIONAL | LOW | MEDIUM | HIGH | CRITICALの中からトリガーするレベルを指定" |
イメージのdigestが必要なため、少し工夫する必要があります。
やってみる
実際にやっていきましょう。
3分クッキング感で申し訳ないですが、デプロイするコンテナ、デプロイ先のECS、デプロイまで出来るGitHub Actionsワークフローを用意します。
今回は下記記事にて構築したGitHub Actionsをベースに組み込んでいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | - name : Login to Amazon ECR jobs: deploy: name: Deploy runs-on: ubuntu-latest steps: - name : Checkout uses: actions/checkout@v1 - name : Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: $ { { secrets.AWS_ACCESS_KEY_ID } } aws-secret-access-key: $ { { secrets.AWS_SECRET_ACCESS_KEY } } aws-region: ap-northeast-1 - name : Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name : Build , tag , and push image to Amazon ECR id: build-image env: ECR_REGISTRY: $ { { steps.login-ecr.outputs.registry } } ECR_REPOSITORY: githubactions-nginx IMAGE_TAG: $ { { github.sha } } run: | # Build a docker container and # push it to ECR so that it can # be deployed to ECS. docker build -t $ECR_REGISTRY/$ECR_REPOSITORY : $IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY : $IMAGE_TAG echo : : set-output name=image : : $ECR_REGISTRY/$ECR_REPOSITORY : $IMAGE_TAG echo : : set-output name=digest : : $(docker images --digests | grep $ECR_REGISTRY/$ECR_REPOSITORY | grep $IMAGE_TAG | awk '{print $3}' ) echo : : set-output name=repository-name : : $ECR_REPOSITORY - name : Image Scan id: image-scan uses: mokocm/ecr-imagescan-action@v3 with: image-digest: $ { { steps.build-image.outputs.digest } } image-tag: $ { { github.sha } } repository-name: $ { { steps.build-image.outputs.repository-name } } trigger-severity: "CRITICAL" - name : Fill in the new image ID in the Amazon ECS task definition id: task-def uses: aws-actions/amazon-ecs-render-task-definition@v1 with: task-definition: task-definition.json container-name: nginx image: $ { { steps.build-image.outputs.image } } - name : Deploy Amazon ECS task definition uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: $ { { steps.task-def.outputs.task-definition } } service: spot-ecs cluster: spot-ecs wait-for-service-stability: true |
追記した部分はハイライトした部分のみとなっています。
usesで mokocm/ecr-imagescan-action@v3
を利用するようにし、image-digest
, image-tag
, repository-name
, trigger-severity
を代入します。
GitHub Actionsでは echo ::set-output name={変数名}::{outputしたいデータ}
のように指定することで、
次のステップで {{ steps.{StepId}.outputs.{変数名} }}
のように変数の橋渡しができます!
動作確認
脆弱性があるコンテナを流す
DockerHubで公開されているNginxイメージの中で一番古く、4年前が最終更新のnginx:1.9.9
のイメージで試してみます。
masterブランチにPushすると、正常にイメージスキャンを開始し、ワークフローが停止しました!
脆弱性がないコンテナを流す
続いて脆弱性のないコンテナを流してみましょう。
今回は検証でnginx:mainline-alpine
のイメージを使っています。
コンテナスキャン結果のステータスがCOMPLATE、 findingSeverityCounts
に trigger-severity
で指定したタイプの脆弱性がなければ正常に完了します。
全体のワークフローが流れるとこんな感じになります。
今回GitHub Actionsを利用したリポジトリはこちらに上がっていますので、ご確認ください!
https://github.com/mokocm/ecr-imagescan-cicd
まとめ
GitHub Actionsを利用することで簡単にCI/CDを組み込みつつ、(スキャンした時点で)安全なイメージをデプロイするようなワークフローを簡単に作ることができました。
リリース頻度の高いサービスなどでもユニットテストと同じノリでデプロイ前にコンテナの脆弱性スキャンを出来るようなワークフローを組んで行けると、よりセキュアにコンテナ環境の運用が出来ると思います!
また、ECRのイメージスキャン機能は無料で使えるので、「とりあえずコンテナセキュリティに入門したい」ニーズも捉えられていると思います。
パイプラインに埋め込まないまでも、脆弱性があったらSNSで通知するくらいの機能なら15~30分程度で構築することができますので、是非お試しください!
以上、もこでした。