🚀 Chapter 4 — Production: Phần E — Security Gate với Amazon ECR
Ở Chapter 4D, chúng ta đã hoàn thành:
GitHub
↓
GitHub Actions
↓
Reusable Workflow
↓
Docker Build
↓
Git SHA Tag
↓
Amazon ECR
↓
Immutable Image Tag
Tuy nhiên, image đã được build và push lên ECR chưa có nghĩa là image đó an toàn để deploy.
Trong phần này, chúng ta sẽ bổ sung Security Gate:
Docker Build
↓
Push Image lên ECR
↓
Vulnerability Scan
↓
Security Gate
├── FAIL → Dừng pipeline
└── PASS → Cho phép deploy
Amazon ECR hỗ trợ quét vulnerability cho container image. Basic scanning tập trung vào OS packages, trong khi Enhanced scanning tích hợp với Amazon Inspector và có thể quét cả OS packages và programming-language packages. Trong lab này, chúng ta sử dụng ECR Basic Scanning với Scan on Push để dễ triển khai và tập trung vào logic Security Gate.
1. Mục tiêu
Sau phần này, chúng ta sẽ có:
✅ Image được scan sau khi push lên ECR
✅ GitHub Actions chờ kết quả scan
✅ Pipeline bị fail nếu có vulnerability nghiêm trọng
✅ Pipeline tiếp tục nếu image đạt điều kiện security
✅ Không deploy image chưa được kiểm tra
✅ Có thể áp dụng cho nhiều service của Online Boutique
Kiến trúc mới:
Developer
↓
git push
↓
GitHub
↓
GitHub Actions
↓
Reusable Workflow
↓
Test
↓
Docker Build
↓
Push Image lên ECR
↓
ECR Vulnerability Scan
↓
Security Gate
├── FAIL → Stop
└── PASS → Continue
2. Kiểm tra môi trường
Trên máy local, di chuyển vào project:
cd online-boutique-cicd
Kiểm tra Git:
git status
Kiểm tra workflow:
find .github/workflows -type f
Ví dụ:
.github/workflows/
├── reusable-ci-cd.yml
└── scale-ci-cd.yml
Kiểm tra AWS CLI:
aws --version
Kiểm tra AWS account:
aws sts get-caller-identity
Kiểm tra AWS Region:
aws configure get region
Nếu chưa có region, đặt region Tokyo:
aws configure set region ap-northeast-1
Kiểm tra các ECR repository:
aws ecr describe-repositories \
--query 'repositories[?starts_with(repositoryName, `online-boutique-`)].repositoryName' \
--output table
Ví dụ:
online-boutique-frontend
online-boutique-cartservice
online-boutique-paymentservice
online-boutique-checkoutservice
3. Kiểm tra trạng thái scanning của ECR
Kiểm tra repository frontend:
aws ecr describe-repositories \
--repository-names online-boutique-frontend \
--query 'repositories[0].imageScanningConfiguration' \
--output table
Nếu kết quả tương tự:
scanOnPush
----------
False
thì cần bật Scan on Push.
4. Bật Scan on Push cho repository
Chạy lệnh:
aws ecr put-image-scanning-configuration \
--repository-name online-boutique-frontend \
--image-scanning-configuration scanOnPush=true
Kiểm tra lại:
aws ecr describe-repositories \
--repository-names online-boutique-frontend \
--query 'repositories[0].imageScanningConfiguration' \
--output table
Kết quả mong đợi:
scanOnPush
----------
True
Scan on Push sẽ tự động kích hoạt quá trình scan khi image được push vào repository.
5. Bật Scan on Push cho các service khác
Chạy lệnh:
for repo in \
online-boutique-frontend \
online-boutique-cartservice \
online-boutique-productcatalogservice \
online-boutique-checkoutservice \
online-boutique-paymentservice \
online-boutique-shippingservice
do
echo "Enabling scan on push for $repo"
aws ecr put-image-scanning-configuration \
--repository-name "$repo" \
--image-scanning-configuration scanOnPush=true
done
Kiểm tra:
for repo in \
online-boutique-frontend \
online-boutique-cartservice \
online-boutique-productcatalogservice \
online-boutique-checkoutservice \
online-boutique-paymentservice \
online-boutique-shippingservice
do
echo "===== $repo ====="
aws ecr describe-repositories \
--repository-names "$repo" \
--query 'repositories[0].{Repository:repositoryName,ScanOnPush:imageScanningConfiguration.scanOnPush}' \
--output table
done
Kết quả mong đợi:
ScanOnPush
----------
True
6. Kiểm tra image đã có trong ECR
Kiểm tra image của frontend:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--query 'sort_by(imageDetails,&imagePushedAt)[-1].{Tags:imageTags,Digest:imageDigest,Pushed:imagePushedAt}' \
--output table
Nếu repository đã có image, bạn có thể lấy tag:
IMAGE_TAG=$(aws ecr describe-images \
--repository-name online-boutique-frontend \
--query 'sort_by(imageDetails,&imagePushedAt)[-1].imageTags[0]' \
--output text)
Kiểm tra:
echo "$IMAGE_TAG"
Ví dụ:
a81c92f
Nếu repository chưa có image, chúng ta sẽ push image mới thông qua GitHub Actions ở các bước sau.
7. Xem kết quả vulnerability scan bằng AWS CLI
Đặt biến:
REPOSITORY_NAME=online-boutique-frontend
IMAGE_TAG=a81c92f
Thay a81c92f bằng tag thực tế trong ECR.
Lấy kết quả scan:
aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageTag="$IMAGE_TAG" \
--query 'imageScanFindings.findingSeverityCounts' \
--output table
Ví dụ:
CRITICAL HIGH MEDIUM LOW INFORMATIONAL
-------- ---- ------ --- -------------
0 1 4 8 2
Kiểm tra trạng thái scan:
aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageTag="$IMAGE_TAG" \
--query 'imageScanStatus' \
--output table
Ví dụ:
status
------
COMPLETE
Một số trạng thái thường gặp:
IN_PROGRESS
COMPLETE
FAILED
UNSUPPORTED
Nếu kết quả là IN_PROGRESS, cần chờ scan hoàn thành trước khi đánh giá Security Gate.
8. Tạo script kiểm tra Security Gate
Tạo thư mục scripts:
mkdir -p scripts
Tạo file:
nano scripts/ecr-security-gate.sh
Thêm nội dung:
#!/usr/bin/env bash
set -euo pipefail
REPOSITORY_NAME="${1:?Repository name is required}"
IMAGE_TAG="${2:?Image tag is required}"
AWS_REGION="${AWS_REGION:-ap-northeast-1}"
MAX_WAIT_SECONDS="${MAX_WAIT_SECONDS:-300}"
POLL_INTERVAL_SECONDS="${POLL_INTERVAL_SECONDS:-10}"
echo "========================================"
echo "ECR Security Gate"
echo "========================================"
echo "Repository: $REPOSITORY_NAME"
echo "Image tag: $IMAGE_TAG"
echo "Region: $AWS_REGION"
echo "========================================"
elapsed_seconds=0
while true; do
scan_status="$(
aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageTag="$IMAGE_TAG" \
--region "$AWS_REGION" \
--query 'imageScanStatus.status' \
--output text 2>/dev/null || true
)"
echo "Scan status: ${scan_status:-UNKNOWN}"
if [[ "$scan_status" == "COMPLETE" ]]; then
break
fi
if [[ "$scan_status" == "FAILED" || "$scan_status" == "UNSUPPORTED" ]]; then
echo "Security Gate: FAIL"
echo "Image scan did not complete successfully."
exit 1
fi
if (( elapsed_seconds >= MAX_WAIT_SECONDS )); then
echo "Security Gate: FAIL"
echo "Timed out while waiting for image scan."
exit 1
fi
sleep "$POLL_INTERVAL_SECONDS"
elapsed_seconds=$((elapsed_seconds + POLL_INTERVAL_SECONDS))
done
critical_count="$(
aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageTag="$IMAGE_TAG" \
--region "$AWS_REGION" \
--query 'imageScanFindings.findingSeverityCounts.CRITICAL || `0`' \
--output text
)"
high_count="$(
aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageTag="$IMAGE_TAG" \
--region "$AWS_REGION" \
--query 'imageScanFindings.findingSeverityCounts.HIGH || `0`' \
--output text
)"
medium_count="$(
aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageTag="$IMAGE_TAG" \
--region "$AWS_REGION" \
--query 'imageScanFindings.findingSeverityCounts.MEDIUM || `0`' \
--output text
)"
low_count="$(
aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageTag="$IMAGE_TAG" \
--region "$AWS_REGION" \
--query 'imageScanFindings.findingSeverityCounts.LOW || `0`' \
--output text
)"
echo ""
echo "========================================"
echo "Vulnerability Summary"
echo "========================================"
echo "CRITICAL: $critical_count"
echo "HIGH: $high_count"
echo "MEDIUM: $medium_count"
echo "LOW: $low_count"
echo "========================================"
if [[ "$critical_count" != "0" || "$high_count" != "0" ]]; then
echo "Security Gate: FAIL"
echo "Critical or High vulnerabilities were detected."
exit 1
fi
echo "Security Gate: PASS"
echo "No Critical or High vulnerabilities were detected."
Lưu file:
CTRL + O
ENTER
CTRL + X
Cấp quyền thực thi:
chmod +x scripts/ecr-security-gate.sh
9. Kiểm tra script
Chạy script với image đã tồn tại:
./scripts/ecr-security-gate.sh \
online-boutique-frontend \
a81c92f
Thay a81c92f bằng image tag thực tế.
Ví dụ kết quả:
========================================
ECR Security Gate
========================================
Repository: online-boutique-frontend
Image tag: a81c92f
Region: ap-northeast-1
========================================
Scan status: COMPLETE
========================================
Vulnerability Summary
========================================
CRITICAL: 0
HIGH: 0
MEDIUM: 3
LOW: 7
========================================
Security Gate: PASS
No Critical or High vulnerabilities were detected.
Nếu có vulnerability mức CRITICAL hoặc HIGH:
Security Gate: FAIL
Critical or High vulnerabilities were detected.
Kiểm tra exit code:
echo $?
Kết quả:
0
nghĩa là Security Gate thành công.
Kết quả:
1
nghĩa là Security Gate thất bại.
10. Xem chi tiết vulnerability
Để xem danh sách vulnerability:
aws ecr describe-image-scan-findings \
--repository-name online-boutique-frontend \
--image-id imageTag=a81c92f \
--query 'imageScanFindings.findings[].{Severity:severity,Name:name,Description:description}' \
--output table
Thay a81c92f bằng image tag thực tế.
Chỉ xem các vulnerability mức CRITICAL:
aws ecr describe-image-scan-findings \
--repository-name online-boutique-frontend \
--image-id imageTag=a81c92f \
--query 'imageScanFindings.findings[?severity==`CRITICAL`].{Name:name,Description:description}' \
--output table
Chỉ xem các vulnerability mức HIGH:
aws ecr describe-image-scan-findings \
--repository-name online-boutique-frontend \
--image-id imageTag=a81c92f \
--query 'imageScanFindings.findings[?severity==`HIGH`].{Name:name,Description:description}' \
--output table
11. Thêm Security Gate vào GitHub repository
Kiểm tra file reusable workflow:
nano .github/workflows/reusable-ci-cd.yml
Sau bước Push Docker image, thêm step:
- name: Run ECR security gate
env:
AWS_REGION: ${{ vars.AWS_REGION }}
ECR_REPOSITORY: ${{ inputs.ecr_repository }}
IMAGE_TAG: ${{ steps.image-tag.outputs.tag }}
run: |
chmod +x scripts/ecr-security-gate.sh
./scripts/ecr-security-gate.sh \
"$ECR_REPOSITORY" \
"$IMAGE_TAG"
Ví dụ flow của job:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Generate image tag
id: image-tag
shell: bash
run: |
SHORT_SHA="${GITHUB_SHA::7}"
echo "tag=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Login to Amazon ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2
- name: Build Docker image
env:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.registry }}
ECR_REPOSITORY: ${{ inputs.ecr_repository }}
IMAGE_TAG: ${{ steps.image-tag.outputs.tag }}
SERVICE_PATH: ${{ inputs.service_path }}
run: |
docker build \
-t "$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" \
"$SERVICE_PATH"
- name: Push Docker image
env:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.registry }}
ECR_REPOSITORY: ${{ inputs.ecr_repository }}
IMAGE_TAG: ${{ steps.image-tag.outputs.tag }}
run: |
docker push \
"$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
- name: Run ECR security gate
env:
AWS_REGION: ${{ vars.AWS_REGION }}
ECR_REPOSITORY: ${{ inputs.ecr_repository }}
IMAGE_TAG: ${{ steps.image-tag.outputs.tag }}
run: |
chmod +x scripts/ecr-security-gate.sh
./scripts/ecr-security-gate.sh \
"$ECR_REPOSITORY" \
"$IMAGE_TAG"
Giữ nguyên các phần khác trong workflow hiện tại. Chỉ bổ sung step
Run ECR security gatesau bước push image.
12. Kiểm tra GitHub Actions variables
Workflow sử dụng:
${{ vars.AWS_REGION }}
Vào GitHub repository:
Settings
↓
Secrets and variables
↓
Actions
↓
Variables
Tạo variable:
Name:
AWS_REGION
Value:
ap-northeast-1
Kiểm tra các secrets cần có:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
Nếu workflow hiện tại đã sử dụng các secrets này ở Chapter 4D thì không cần tạo lại.
13. Commit thay đổi
Kiểm tra file đã thay đổi:
git status
Kiểm tra nội dung thay đổi:
git diff
Thêm file:
git add \
scripts/ecr-security-gate.sh \
.github/workflows/reusable-ci-cd.yml
Commit:
git commit -m "add ECR security gate"
Push lên GitHub:
git push origin main
14. Kiểm tra GitHub Actions
Mở repository trên GitHub:
Actions
↓
Workflow
↓
Latest run
Kiểm tra các step:
Checkout source
↓
Generate image tag
↓
Configure AWS credentials
↓
Login to Amazon ECR
↓
Build Docker image
↓
Push Docker image
↓
Run ECR security gate
Nếu image đạt điều kiện:
Run ECR security gate
↓
Security Gate: PASS
↓
SUCCESS
Nếu image có vulnerability mức CRITICAL hoặc HIGH:
Run ECR security gate
↓
Security Gate: FAIL
↓
FAILURE
Khi step Security Gate thất bại, các step phía sau sẽ không được thực hiện.
15. Kiểm tra bằng cách tạo một workflow test
Để kiểm tra logic fail của pipeline, có thể tạo một workflow test riêng.
Tạo file:
nano .github/workflows/security-gate-test.yml
Thêm:
name: Security Gate Test
on:
workflow_dispatch:
jobs:
test-security-gate:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Run security gate
env:
AWS_REGION: ${{ vars.AWS_REGION }}
run: |
chmod +x scripts/ecr-security-gate.sh
./scripts/ecr-security-gate.sh \
online-boutique-frontend \
a81c92f
Thay a81c92f bằng image tag thực tế.
Commit và push:
git add .github/workflows/security-gate-test.yml
git commit -m "add security gate test workflow"
git push origin main
Chạy thủ công:
GitHub
↓
Actions
↓
Security Gate Test
↓
Run workflow
16. Áp dụng cho nhiều service
Trong workflow gọi reusable workflow, các service có thể sử dụng chung Security Gate:
jobs:
frontend:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: frontend
service_path: ./src/frontend
ecr_repository: online-boutique-frontend
secrets: inherit
cartservice:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: cartservice
service_path: ./src/cartservice
ecr_repository: online-boutique-cartservice
secrets: inherit
paymentservice:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: paymentservice
service_path: ./src/paymentservice
ecr_repository: online-boutique-paymentservice
secrets: inherit
Mỗi service sẽ có flow riêng:
frontend
↓
Build
↓
Push ECR
↓
Security Gate
cartservice
↓
Build
↓
Push ECR
↓
Security Gate
paymentservice
↓
Build
↓
Push ECR
↓
Security Gate
Nhưng tất cả đều sử dụng cùng một script:
scripts/ecr-security-gate.sh
17. Kết quả cuối cùng
Sau Chapter 4E, pipeline sẽ trở thành:
Developer
↓
GitHub Push
↓
GitHub Actions
↓
Reusable Workflow
↓
Test
↓
Docker Build
↓
Git SHA Tag
↓
Push Image lên ECR
↓
ECR Vulnerability Scan
↓
Security Gate
├── CRITICAL/HIGH → FAIL
│ ↓
│ Stop Pipeline
│
└── Không có CRITICAL/HIGH
↓
PASS
↓
Cho phép bước sau
18. Kiểm tra hoàn thành
[ ] Đã bật Scan on Push cho ECR
[ ] Đã push image mới lên ECR
[ ] Đã kiểm tra trạng thái scan
[ ] Đã xem vulnerability findings
[ ] Đã tạo ecr-security-gate.sh
[ ] Script trả về exit code 0 khi PASS
[ ] Script trả về exit code 1 khi FAIL
[ ] Đã thêm Security Gate vào reusable workflow
[ ] GitHub Actions chờ kết quả scan
[ ] Pipeline dừng khi có CRITICAL hoặc HIGH
[ ] Đã áp dụng cho nhiều Online Boutique services
Kết luận
Ở Chapter 4D, chúng ta đảm bảo:
Một tag chỉ đại diện cho một image cụ thể
Ở Chapter 4E, chúng ta bổ sung:
Không phải image nào cũng được phép đi tiếp
Nguyên tắc mới:
Build artifact một lần, lưu trữ bằng immutable tag, scan trước khi release và chỉ cho phép artifact đạt security gate đi tiếp.
Phần tiếp theo có thể xây dựng Chapter 4F — Artifact Promotion: Promote cùng một image từ Dev → Staging → Production mà không build lại.
All rights reserved