開発チームのセキュリティ意識を高めるため、GitHub ActionsでDockerイメージのセキュリティスキャンを行い、その結果をプルリク(PR)のコメントとして自動的に追加するような仕組みをCIに組み込みました。
scan-and-comment
ジョブの概要
このジョブは、コードのチェックアウト、Dockerイメージのビルド、セキュリティスキャン、結果の整形、そしてPRへのコメント投稿までの一連のステップを自動化します。Dockerイメージの脆弱性スキャンにはTrivyを使用しています。Trivyについての詳細は別記事で触れています。
scan-and-comment
ジョブの詳細
以下が、scan-and-comment
ジョブを追加した後のCIワークフロー全文です。
name: backend-ci
on:
push:
branches:
- main
- staging
- develop
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- "backend/**"
workflow_dispatch: #手動動作確認用
jobs:
setup:
...略...
test:
...略...
lint:
...略...
tsc:
...略...
scan-and-comment:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'opened'
steps:
- name: Checkout code
uses: actions/checkout@v4
# 本番環境で使用されるDockerイメージをビルドし、その後のステップで脆弱性スキャンを行う
- name: Build Docker Image
run: docker build --build-arg ENV=prod -t local-image:latest -f ./backend/Dockerfile.ecs ./backend
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: local-image:latest
format: "table"
severity: "CRITICAL,HIGH"
output: trivy-result.txt
- name: Check Trivy result file
run: cat trivy-result.txt
- name: Format Trivy Scan Result
run: |
if [ -s trivy-result.txt ]; then
echo -e "## Vulnerability Scan Results\n<details><summary>Details</summary>\n\n\`\`\`\n$(cat trivy-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
else
echo -e "## Vulnerability Scan Results\nNo vulnerabilities were detected." > formatted-trivy-result.md
fi
- name: Comment PR with Trivy scan results
uses: marocchino/sticky-pull-request-comment@v2
with:
path: formatted-trivy-result.md
- name: Clean up Trivy result file
run: rm -f trivy-result.txt formatted-trivy-result.md
1. コードのチェックアウト
if: github.event_name == 'pull_request' && github.event.action == 'opened'
により、このジョブはPRが新しく作成されたときにのみ実行されます(PRの同期や再開時にはジョブがトリガーされません)。
また、チェックアウトで最新のコードを取得します。
scan-and-comment:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'opened'
steps:
- name: Checkout code
uses: actions/checkout@v4
2. Dockerイメージのビルド
本番環境と同じ設定でDockerイメージをビルドします。これにより、本番環境で発生する可能性のある脆弱性を検出できます。
- name: Build Docker Image
run: docker build --build-arg ENV=prod -t local-image:latest -f ./backend/Dockerfile.ecs ./backend
以下がビルドに使用するDockerfile
です。本番環境でプロダクションビルドを行なうようにしているため、ビルド時に本番環境用のパラメータENV=prod
を指定して、セキュリティスキャンの精度を高めています。
また、local-image:latest
は脆弱性スキャンのみに使用するDockerイメージで、任意の名前をつけています。
FROM node:20.15.0 AS builder
WORKDIR /app
COPY package*.json ./
COPY yarn.lock ./
# Huskyを無視して依存関係をインストール
# yarn buildのためにこちらでproductionモードは使用しない
RUN yarn install --frozen-lockfile --ignore-scripts
COPY . .
RUN yarn build
ARG ENV
# stg環境とprod環境の場合はnode_modulesを削除してproductionモードで再インストールする
RUN if [ "$ENV" = "prod" ] || [ "$ENV" = "stg" ]; then \
rm -rf node_modules && yarn install --frozen-lockfile --ignore-scripts --production; \
fi
FROM node:20.15.0-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
EXPOSE 8080
CMD ["node", "dist/main"]
3. Trivyによるイメージスキャン
Trivyを使用してイメージのセキュリティスキャンを実施し、結果をテキストファイルに保存します。
以下が使用するアクションです。
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: local-image:latest
format: "table"
severity: "CRITICAL,HIGH"
output: trivy-result.txt
- name: Check Trivy result file
run: cat trivy-result.txt
4. スキャン結果の整形
スキャンの結果に基づき、詳細な情報を含むコメントフォーマットを作成します。
- name: Format Trivy Scan Result
run: |
if [ -s trivy-result.txt ]; then
echo -e "## Vulnerability Scan Results\n<details><summary>Details</summary>\n\n\`\`\`\n$(cat trivy-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
else
echo -e "## Vulnerability Scan Results\nNo vulnerabilities were detected." > formatted-trivy-result.md
fi
5. PRへのコメント投稿
フォーマットされたスキャン結果をPRにコメントとして投稿します。
- name: Comment PR with Trivy scan results
uses: marocchino/sticky-pull-request-comment@v2
with:
path: formatted-trivy-result.md
6. クリーンアップ
最後に使用したファイルを削除し、環境をクリーンな状態に保ちます。
- name: Clean up Trivy result file
run: rm -f trivy-result.txt formatted-trivy-result.md