Trivy, Grype, Snyk 컨테이너 스캐너를 비교하고, CI/CD 파이프라인에 취약점 스캐닝 게이트를 통합하여 안전한 이미지만 배포하는 방법을 다룹니다.
이미지 스캐닝은 컨테이너 이미지에 포함된 패키지와 라이브러리를 분석하여 알려진 취약점을 탐지하는 과정입니다. 스캐너는 이미지의 각 레이어를 풀어서 설치된 패키지 목록을 추출하고, CVE(Common Vulnerabilities and Exposures) 데이터베이스와 대조합니다.
스캐닝 시점은 크게 세 가지로 나뉩니다.
빌드 시점에 취약점이 없었더라도, 새로운 CVE가 발표되면 기존 이미지에 취약점이 생길 수 있습니다. 레지스트리의 주기적 재스캐닝이 필요한 이유입니다.
Trivy는 Aqua Security가 개발한 오픈소스 스캐너로, CNCF 프로젝트로 채택되었습니다. 컨테이너 이미지뿐만 아니라 파일시스템, Git 저장소, IaC(Infrastructure as Code) 설정, 쿠버네티스 클러스터까지 스캔할 수 있는 종합 보안 도구입니다.
# 이미지 스캔
trivy image myapp:latest
# 심각도 필터링 (HIGH, CRITICAL만)
trivy image --severity HIGH,CRITICAL myapp:latest
# JSON 형식 출력
trivy image --format json --output result.json myapp:latest
# 파일시스템 스캔
trivy fs --scanners vuln,secret,misconfig .
# 쿠버네티스 클러스터 스캔
trivy k8s --report summary clusterTrivy의 주요 강점은 다음과 같습니다.
Grype는 Anchore에서 개발한 오픈소스 취약점 스캐너입니다. Syft와 함께 사용하면 SBOM 생성부터 취약점 분석까지 파이프라인을 구성할 수 있습니다.
# 이미지 스캔
grype myapp:latest
# SBOM을 입력으로 스캔
syft myapp:latest -o spdx-json > sbom.json
grype sbom:sbom.json
# 심각도 기준으로 실패 처리
grype myapp:latest --fail-on highGrype의 특징은 Syft로 생성한 SBOM을 입력으로 받아 스캔할 수 있다는 점입니다. SBOM 생성과 스캐닝을 분리함으로써 각 단계를 독립적으로 관리할 수 있습니다.
Snyk Container는 상용 서비스로, 취약점 탐지뿐만 아니라 수정 권장사항까지 제공합니다.
# 이미지 스캔
snyk container test myapp:latest
# 베이스 이미지 업그레이드 권장사항 포함
snyk container test myapp:latest --file=Dockerfile
# 지속적 모니터링 등록
snyk container monitor myapp:latestSnyk의 차별점은 "이 취약점을 해결하려면 베이스 이미지를 어떤 버전으로 올려야 하는가"와 같은 구체적인 수정 가이드를 제공한다는 것입니다.
| 항목 | Trivy | Grype | Snyk Container |
|---|---|---|---|
| 라이선스 | Apache 2.0 | Apache 2.0 | 상용 (무료 플랜 있음) |
| OS 패키지 | O | O | O |
| 언어별 패키지 | O | O | O |
| IaC 스캐닝 | O | X | O (별도 제품) |
| 시크릿 탐지 | O | X | X |
| SBOM 생성 | O | X (Syft 필요) | X |
| 수정 가이드 | 부분적 | X | O (상세) |
| 오프라인 모드 | O | O | X |
스캐닝 결과에서 수십에서 수백 개의 취약점이 나올 수 있습니다. 모든 취약점을 동시에 해결하는 것은 불가능하므로, 효과적인 우선순위 결정이 필수적입니다.
CVSS는 취약점의 기술적 심각도를 0.0~10.0 점수로 나타냅니다. 그러나 CVSS 점수가 높다고 해서 반드시 실제 공격에 악용되는 것은 아닙니다.
EPSS는 해당 취약점이 향후 30일 내에 실제로 악용될 확률을 예측합니다. CVSS와 EPSS를 함께 사용하면 더 실질적인 우선순위를 정할 수 있습니다.
| 조합 | CVSS | EPSS | 대응 |
|---|---|---|---|
| 긴급 대응 | 높음 (7.0+) | 높음 (0.5+) | 즉시 패치 |
| 계획적 해결 | 높음 (7.0+) | 낮음 | 다음 릴리즈에 포함 |
| 모니터링 | 낮음 | 높음 (0.5+) | 모니터링 강화 |
| 수용 가능 | 낮음 | 낮음 | 정기 업데이트 시 처리 |
스캐너가 실제로는 영향이 없는 취약점을 보고하는 경우가 있습니다. 이를 **False Positive(오탐)**이라 합니다. 오탐을 효과적으로 관리하는 방법은 다음과 같습니다.
Trivy에서는 .trivyignore 파일로 특정 CVE를 무시할 수 있습니다.
# 이 취약점은 우리 환경에서 영향 없음 (해당 기능 미사용)
CVE-2024-12345
# 업스트림 패치 대기 중, 2026-04-01까지 유예
CVE-2024-67890 exp:2026-04-01오탐 무시 처리는 반드시 이유와 만료일을 기록해야 합니다. 무분별한 무시 처리는 실제 취약점을 놓치는 원인이 됩니다.
취약점 스캐닝 결과에 따라 이미지 배포를 자동으로 차단하는 정책을 설정할 수 있습니다. 이를 **Scanning Gate(스캐닝 게이트)**라고 합니다.
일반적으로 다음과 같은 정책을 사용합니다.
# CRITICAL, HIGH 취약점이 있으면 종료 코드 1 반환
trivy image --severity CRITICAL,HIGH --exit-code 1 myapp:latest
# 수정 가능한 취약점만 대상으로 차단
trivy image --severity CRITICAL,HIGH \
--ignore-unfixed \
--exit-code 1 \
myapp:latest--ignore-unfixed 플래그는 아직 패치가 제공되지 않은 취약점을 무시합니다. 패치할 수 없는 취약점으로 인해 배포가 무한정 차단되는 상황을 방지합니다.
CI/CD 파이프라인에 스캐닝을 통합하면 취약한 이미지가 프로덕션에 도달하기 전에 차단할 수 있습니다.
name: Container Image Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: "1"
ignore-unfixed: true
- name: Upload Trivy scan results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
- name: Run Grype as secondary scanner
uses: anchore/scan-action@v4
if: always()
with:
image: myapp:${{ github.sha }}
fail-build: true
severity-cutoff: high
output-format: sarif이 워크플로우의 핵심 포인트를 살펴보겠습니다.
exit-code: "1"로 설정하여 취약점 발견 시 워크플로우가 실패하도록 합니다.if: always(): Trivy가 실패해도 Grype 스캔과 결과 업로드가 계속 실행됩니다. - name: Run Trivy with table output
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: table
output: trivy-table.txt
severity: CRITICAL,HIGH
- name: Comment PR with scan results
if: github.event_name == 'pull_request'
uses: marocchino/sticky-pull-request-comment@v2
with:
header: trivy-scan
path: trivy-table.txtPR에 스캔 결과를 코멘트로 남기면, 리뷰어가 별도 도구 없이 취약점 현황을 바로 확인할 수 있습니다.
CI/CD 외에도 컨테이너 레지스트리에서 주기적으로 이미지를 재스캔하는 것이 중요합니다. 새로운 CVE가 발표되면 기존에 안전하던 이미지도 취약해질 수 있기 때문입니다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: registry-scan
namespace: security
spec:
schedule: "0 6 * * *" # 매일 오전 6시
jobTemplate:
spec:
template:
spec:
containers:
- name: scanner
image: aquasec/trivy:latest
command:
- trivy
- image
- --severity
- CRITICAL,HIGH
- --format
- json
- --output
- /reports/daily-scan.json
- registry.example.com/myapp:production
volumeMounts:
- name: reports
mountPath: /reports
volumes:
- name: reports
persistentVolumeClaim:
claimName: scan-reports
restartPolicy: OnFailureAmazon ECR, Google Artifact Registry, Harbor 등 주요 컨테이너 레지스트리는 내장 스캐닝 기능을 제공합니다. 자체 스캐닝 인프라를 구축하기 전에 레지스트리의 기본 기능을 먼저 확인하세요.
이번 장에서는 이미지 스캐닝과 취약점 관리의 전체 흐름을 다루었습니다.
다음 장에서는 이미지에 포함된 모든 구성 요소를 목록화하는 **SBOM(소프트웨어 자재 명세서)**을 다룹니다. SBOM이 왜 필요한지, SPDX와 CycloneDX 형식의 차이, 그리고 Syft와 Trivy로 SBOM을 생성하고 관리하는 방법을 실습합니다.
이 글이 도움이 되셨나요?
SBOM(소프트웨어 자재 명세서)의 개념과 필요성, SPDX와 CycloneDX 형식을 비교하고, Syft와 Trivy로 SBOM을 생성하여 공급망 가시성을 확보하는 방법을 실습합니다.
최소 베이스 이미지, 멀티스테이지 빌드, 루트 없는 컨테이너 등 Dockerfile 보안 모범 사례와 불변 이미지 전략을 실습합니다.
Sigstore 에코시스템(Cosign, Fulcio, Rekor)으로 컨테이너 이미지에 키리스 서명을 적용하고, SLSA 프레임워크 기반의 빌드 출처 증명을 구현합니다.