소개
Kubernetes 환경에서 Microservice 는 모두 image 기반으로 배포되고 운영이 됩니다. 그런만큼 image 보안에 대해 신경을 많이 써야하는데요.
image 보안을 위해서는 image 에 대한 보안 스캔을 통해 취약점을 최소화하고 안전한 이미지 기반으로 서비스를 배포 해야합니다.
Image Vulnerability Scanner 를 위한 오픈소스들은 여러가지가 존재합니다. AWS 를 사용하는 환경이라면 ECR 에서 제공해주는 Image Scan 기능도 많이들 사용을 하고 있는데요.
이번에는 AWS 환경에서 ECR 의 Image Scan 기능을 사용하는 것이 아닌 별도의 Trivy 이미지 스캐닝 툴을 CI/CD 에 녹이는 구성을 만들어 보려합니다.
AWS ECR 의 Image Scan 은 Clair 기반으로 동작하는데요. 이것이 싫거나 스캔을 돌릴때 좀더 디테일한 설정을 통해 스캔 설정을 하고 싶을때 사용하면 좋은 방법일 듯합니다.
Trivy
Trivy 는 Aqua Security에서 개발한 오픈 소스 image vulnerability scanner 입니다. 빠르고 광범위한 취약점 데이터베이스를 기반으로 스캐닝을 수행합니다.
Github Action 이나 kubernetes operator, VS Code 와도 통합이 가능한데요. 이번에는 Github Action 을 통해 CI/CD pipeline 에서 취약점 점검을 진행하고 결과를 확인해보도록 하겠습니다.
Github Action – Trivy 통합
구성은 아래와 같이 AWS 환경에서 구성하였습니다.
- Application 을 개발하여 Repository 에 Pull 하면,
- Github Action 을 통해 CI/CD 가 진행됩니다.
- Github Action 에서는 먼저 Image Build 를 하고,
- Build 된 Image 는 Trivy 를 통해 취약점 스캔이 진행됩니다.
- 스캔 결과를 출력하고,(soft fail,
exit-code: "0"
설정하여 취약점 발견되어도 실패 안하도록) - kustomaize 에 의해 image tag 를 수정하고,
- ECR 에 Image 최종 Push 하고, EKS 에 배포까지 하게 됩니다.
pipeline 절차는 위와 같습니다.
이제 Github Action workflow 코드를 보면, 위의 절차 그대로 확인이 가능합니다.
Github Action 에서는 trivy-action 패키지가 제공이 되고 있어, 참고하여 Pipeline 을 입맛에 맞게 구성하면 되겠습니다.
위 구성도와 같이 github secrets 에 secrets 값들을 미리 설정합니다. (DB 정보는 개인적으로 배포한 어플리케이션과 연동될 DB의 정보키이니 참고만 하면 될듯합니다.)
name: Build and Push Docker image to ECR
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
jobs:
cd:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v3
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
with:
mask-password: "true"
- name: Build, tag, and push docker image to Amazon ECR
id: push-ecr
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: checkingress
IMAGE_TAG: ${{ github.sha }}
run: |
echo "::set-output name=ecr_repository::$REPOSITORY"
echo "::set-output name=image_tag::$IMAGE_TAG"
echo "::set-output name=ecr_registry::$REGISTRY"
docker build --build-arg DB_HOST=${{ secrets.DB_HOST }} \
--build-arg DB_PORT=${{ secrets.DB_PORT }} \
--build-arg DB_USER=${{ secrets.DB_USER }} \
--build-arg DB_PASSWORD=${{ secrets.DB_PASSWORD }} \
--build-arg DB_DATABASE=${{ secrets.DB_DATABASE }} \
-t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
docker images
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: "${{ steps.login-ecr.outputs.ecr_registry}}/checkingress:${{ github.sha }}"
format: "table"
exit-code: "0"
ignore-unfixed: true
vuln-type: "os,library"
severity: "CRITICAL,HIGH"
- name: Push image to Amazon ECR
run: |
docker push ${{ steps.push-ecr.outputs.ecr_registry}}/${{ steps.push-ecr.outputs.ecr_repository }}:${{ steps.push-ecr.outputs.image_tag }}
- name: Get image tag(verion)
id: image
run: |
VERSION=$(echo \${{ github.sha }} | cut -c1-8)
echo VERSION=\$VERSION
echo "::set-output name=version::\$VERSION"
- name: Install and configure kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
aws eks --region ap-northeast-2 update-kubeconfig --name service
- name: Install Kustomize
run: |
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/
- name: Deploy to EKS
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: checkingress
IMAGE_TAG: ${{ github.sha }}
run: |
cd manifest/overlays
kustomize edit set image $REGISTRY/$REPOSITORY=$REGISTRY/$REPOSITORY:$IMAGE_TAG
kustomize build . | kubectl apply -f -
- code 를 check-out 하고,
- AWS ECR 를 assume role 기반으로 로그인 해놓습니다.
- Dockerfile 기반으로 Image Build 를 합니다.
- Build 된 Image 기반으로 Trivy vulnerability 스캐닝을 시작합니다.
- image 를 ECR 로 push 합니다.
- kustomize 로 tag 최신화 하고, eks 에 배포까지 하게됩니다.
개인적으로 eks 자동 배포까지 포함한 workflow 로 eks 배포까지는 크게 중요한 부분이 아닐듯하고, Trivty vulnerability 스캐닝 부분만 확인하면 될듯합니다.
steps 에서 Trivy 스캐너를 돌리는 부분만 집중적으로 살펴 보면,
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: "${{ steps.login-ecr.outputs.registry}}/checkingress:${{ github.sha }}"
format: "table"
exit-code: "0"
ignore-unfixed: true
vuln-type: "os,library"
severity: "CRITICAL,HIGH"
image-ref
: 스캔할 image 참조로 build 결과 만들어진 image 입니다.format
: output format 으로 table 형식으로 지정하였습니다.exit-code: "0"
: soft-fail 로 취약점이 발견되더라도 workflow 실패를 일으키지 않습니다.ignore-unfixed : true
: 패치 fix 가 안된 취약점은 무시합니다.vuln-type : "os,library"
: 스캔 유형을 운영체제와 라이브러리로 지정합니다.severity : "CRITICAL,HIGH"
: high 이상의 심각도만 검사합니다.
만약 다른 설정으로 하고자 한다면 여기서 inputs key 를 확인하여 필요에 따라 사용을 하면 되겠습니다.
스캔 결과는..
스캔이 잘 마무리 되어 결과를 확인할 수 있네요.
결론
Trivy 스캔 결과를 다른 로그 시스템과 통합하고 싶다면 step 을 추가하면 되고, Slack 이나 다른 SNS 로의 전달도 얼마든지 Action workflow 로 구성이 가능할 듯합니다.
위에서는 Trivy 스캔 결과가 Action workflow 흐름에 영향을 주지 않도록 exit-code: "0"
로 설정을 하였었는데요. 만약 실환경에서 스캔결과에 따라 이미지 배포를 강제하고 싶다면 1
로 설정하여 hard-scan 을 하면 안전한 이미지 배포를 강제화 할 수 있으니 참고하면 될듯합니다.