Docker 이미지 취약점 스캔: Trivy와 Docker Scout
Trivy, Docker Scout, Grype를 이용한 컨테이너 이미지 CVE 스캔, CI/CD 파이프라인 통합, SBOM 생성, 취약점 우선순위 결정 전략을 실전 예제와 함께 설명합니다.
지난 글에서 이미지 서명으로 변조를 방지하는 방법을 다뤘다. 이번에는 이미지 **내부의 취약점(CVE)**을 찾아내는 스캔 방법을 살펴본다. 서명이 “이 이미지가 내가 만든 것임”을 보장한다면, 취약점 스캔은 “이 이미지가 안전한가”를 검사한다.
왜 이미지를 스캔해야 하는가
컨테이너 이미지는 수십~수백 개의 패키지를 포함한다. 베이스 이미지(예: node:20-alpine)만 해도 이미 다수의 패키지가 들어있고, 그 중 일부는 알려진 CVE를 가진다.
# 이미지 안에 포함된 패키지 예시
docker run --rm node:20-alpine sh -c "apk info | wc -l"
# → 수십 개의 Alpine 패키지
# 그 중 취약한 패키지가 있을 수 있다
취약점 스캔을 CI에 통합해 새로운 CVE가 프로덕션에 배포되기 전에 차단하는 것이 목표다.
Trivy
Aqua Security의 오픈소스 스캐너로 가장 널리 쓰인다.
# 설치 (Homebrew)
brew install aquasecurity/trivy/trivy
# Linux (스크립트)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# 기본 스캔
trivy image node:20-alpine
# 심각도 필터 (HIGH 이상만)
trivy image --severity HIGH,CRITICAL node:20-alpine
# JSON 출력
trivy image --format json --output result.json node:20-alpine
# 로컬 빌드된 이미지 스캔
trivy image myapp:latest
Trivy 출력 해석
node:20-alpine (alpine 3.19.0)
Total: 3 (HIGH: 1, MEDIUM: 2)
┌──────────────┬───────────────┬──────────┬──────────────────┐
│ Library │ Vulnerability │ Severity │ Fixed Version │
├──────────────┼───────────────┼──────────┼──────────────────┤
│ openssl │ CVE-2024-XXXX │ HIGH │ 3.1.5-r0 │
│ libcrypto3 │ CVE-2024-YYYY │ MEDIUM │ 3.1.5-r0 │
└──────────────┴───────────────┴──────────┴──────────────────┘
Fixed Version이 있으면 베이스 이미지를 최신 버전으로 업데이트하거나 해당 패키지만 업그레이드해 해결할 수 있다.
Dockerfile에서 패키지 업그레이드
FROM node:20-alpine
# 알려진 취약한 패키지만 선택 업그레이드
RUN apk upgrade --no-cache openssl libcrypto3
# 전체 업그레이드 (트레이드오프: 재현성 감소)
# RUN apk upgrade --no-cache
Docker Scout
Docker 공식 취약점 스캐너로 Docker Desktop과 CLI에 통합되어 있다.
# Docker Scout 활성화 (로그인 필요)
docker login
# 빠른 요약
docker scout quickview myimage:latest
# CVE 전체 목록
docker scout cves myimage:latest
# 심각도 필터
docker scout cves --only-severity critical,high myimage:latest
# 베이스 이미지 비교 (어떤 이미지로 바꾸면 CVE가 줄어드는지)
docker scout recommendations myimage:latest
Scout의 recommendations 명령은 더 안전한 베이스 이미지를 자동으로 추천해준다.
# 출력 예시
# Base image recommendation:
# node:20-alpine → node:20.11-alpine3.19
# (reduces CVE count from 15 to 3)
SBOM(Software Bill of Materials)
SBOM은 이미지에 포함된 모든 소프트웨어 구성 요소 목록이다. 새 CVE가 발표됐을 때 어떤 이미지가 영향받는지 빠르게 파악할 수 있다.
# Trivy로 SBOM 생성 (CycloneDX 형식)
trivy image --format cyclonedx --output sbom.json myapp:latest
# Syft(Anchore)로 SBOM 생성
syft myapp:latest -o cyclonedx-json > sbom.json
# Grype로 SBOM 기반 스캔 (이미지 없이 SBOM만으로 스캔 가능)
grype sbom:sbom.json
CI/CD 통합
취약점 스캔은 이미지 빌드 직후, 레지스트리 push 전에 실행하는 것이 이상적이다.
GitHub Actions (Trivy)
- name: Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: 'CRITICAL,HIGH'
exit-code: '1' # 취약점 발견 시 빌드 실패
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
SARIF 형식으로 업로드하면 GitHub Security 탭에서 결과를 시각적으로 확인할 수 있다.
GitLab CI
trivy-scan:
image: aquasec/trivy:latest
script:
- trivy image
--exit-code 1
--severity CRITICAL,HIGH
--no-progress
$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
로컬 pre-push 훅
#!/bin/sh
# .git/hooks/pre-push
IMAGE=$(grep 'image:' Dockerfile | head -1 | awk '{print $2}')
trivy image --exit-code 1 --severity CRITICAL $IMAGE
취약점 우선순위 결정
스캔 결과에 수백 개의 CVE가 나왔다고 해서 모두 즉시 고쳐야 하는 것은 아니다.
# 실제로 수정 가능한 취약점만 보기 (fix-availability 필터)
trivy image --ignore-unfixed myapp:latest
# CVSSv3 점수 8.0 이상만
trivy image --severity CRITICAL myapp:latest
우선순위 기준:
- Fix Available + CRITICAL/HIGH — 즉시 패치
- No Fix + CRITICAL — 베이스 이미지 교체 또는 distroless/scratch 고려
- MEDIUM with Fix — 다음 릴리즈에 포함
- LOW/NEGLIGIBLE — 무시 또는
.trivyignore에 등록
.trivyignore로 오탐 제외
# .trivyignore
# 오탐 또는 영향 없는 CVE 제외 (재현 불가하거나 non-exploitable)
CVE-2023-XXXXX
CVE-2024-YYYYY
런타임 스캔
이미지 빌드 시점뿐만 아니라 이미 실행 중인 컨테이너도 스캔할 수 있다.
# 실행 중인 컨테이너 스캔
trivy image --input <(docker export mycontainer)
# 또는 컨테이너 ID로
trivy image $(docker inspect --format='{{.Image}}' mycontainer)
스캔 도구 선택 가이드
- 개인 프로젝트 / 오픈소스: Trivy (무료, 기능 풍부)
- Docker Hub 중심 워크플로우: Docker Scout (CLI 통합 편리)
- Anchore 엔터프라이즈 연동: Grype + Syft
- Kubernetes 환경: Trivy Operator (클러스터 내 지속 스캔)
지난 글: Docker Content Trust: 이미지 서명으로 공급망 공격 방지
다음 글: Docker 읽기 전용 루트 파일 시스템: —read-only 완전 활용
읽어주셔서 감사합니다. 😊