Files
2nd/10_Wiki/Topics/Coding/DevSec_Container_Scanning.md
T
2026-05-09 21:08:02 +09:00

259 lines
5.8 KiB
Markdown

---
id: devsec-container-scanning
title: Container Scanning — Trivy / Grype / 정책
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [devsecops, container, security, vibe-coding]
tech_stack: { language: "Docker / CI", applicable_to: ["DevOps"] }
applied_in: []
aliases: [Trivy, Grype, Snyk Container, Docker Scout, base image, distroless]
---
# Container Scanning
> Image 안 vulnerable lib → exploit. **Trivy / Grype / Docker Scout / Snyk** 가 CVE 검사. **Distroless / Chainguard / Alpine 작은 base** + multi-stage = 작은 attack surface.
## 📖 핵심 개념
- CVE: 알려진 취약점.
- Base image: 작을수록 좋음.
- Multi-stage: build 에 사용한 도구 prod 안 안 옴.
- Distroless: shell / package manager 없는 image.
## 💻 코드 패턴
### Trivy (가장 인기)
```bash
# Image 검사
trivy image myapp:latest
# Severity 필터
trivy image --severity HIGH,CRITICAL myapp:latest
# JSON 출력 (CI)
trivy image --format json --output report.json myapp:latest
# IaC 검사
trivy config terraform/
# Filesystem (Dockerfile 빌드 전)
trivy fs --scanners vuln,secret,misconfig .
```
### CI 통합
```yaml
# .github/workflows/security.yml
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'HIGH,CRITICAL'
exit-code: '1'
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: 'trivy-results.sarif' }
```
### Grype (alternative)
```bash
grype myapp:latest
grype dir:./
grype --output json --file report.json myapp:latest
```
### Docker Scout
```bash
docker scout cves myapp:latest
docker scout recommendations myapp:latest # base image 권장
```
### Multi-stage Dockerfile
```dockerfile
# Build stage — 큰 toolchain
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime stage — 작고 안전
FROM node:20-alpine
RUN addgroup -g 1001 -S nodejs && adduser -S app -u 1001
WORKDIR /app
COPY --from=builder --chown=app:nodejs /app/dist ./dist
COPY --from=builder --chown=app:nodejs /app/node_modules ./node_modules
USER app
EXPOSE 3000
CMD ["node", "dist/index.js"]
```
### Distroless (가장 작고 안전)
```dockerfile
FROM node:20 AS builder
# build 작업
FROM gcr.io/distroless/nodejs20-debian12
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/node_modules /app/node_modules
WORKDIR /app
USER nonroot
CMD ["dist/index.js"]
```
→ Shell 없음. Exploit 어려움.
### Chainguard images (modern)
```dockerfile
FROM cgr.dev/chainguard/node:latest
# 매일 패치, 최소 CVE
```
### Alpine vs Debian
```
Alpine: ~5MB, musl libc — 일부 native module 호환 X
Debian slim: ~80MB, glibc — 호환성 좋음
Distroless: ~50MB, 매우 안전
Chainguard: 0 CVE 목표
```
### .dockerignore
```
node_modules
.git
.env
*.log
coverage
.next
.cache
dist
```
→ 빌드 context 작게 + secret leak 방지.
### Secrets in build (안전)
```dockerfile
# ❌ ENV 에 secret
ENV API_KEY=secret
# ❌ ARG 에 secret 도 layer 에 보임
ARG API_KEY
RUN echo $API_KEY > /tmp/key
# ✅ Build secret (BuildKit)
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm install
```
```bash
docker build --secret id=npmrc,src=$HOME/.npmrc .
```
### SBOM 생성 (Software Bill of Materials)
```bash
# Trivy
trivy image --format spdx-json --output sbom.json myapp:latest
# Syft
syft myapp:latest -o spdx-json > sbom.json
# 또는 Docker scout
docker scout sbom myapp:latest
```
→ Compliance / supply chain.
### Vulnerability database 업데이트
```bash
trivy image --download-db-only
```
CI 매번 자동 download.
### 정책 (OPA / Kyverno)
```yaml
# Kubernetes admission — 위험 image 거부
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: { name: disallow-critical-cves }
spec:
validationFailureAction: Enforce
rules:
- name: scan
match: { resources: { kinds: [Pod] } }
validate:
message: "Image has CRITICAL CVEs"
pattern:
spec:
containers:
- image: "!*:latest" # 또는 trivy + admission webhook
```
### Daily rebuild (security patch)
```yaml
# Schedule — base image 패치 적용
on:
schedule: [{ cron: '0 6 * * *' }]
jobs:
rebuild:
steps:
- run: docker pull node:20-alpine # latest base
- run: docker build -t myapp:nightly .
- run: trivy image --exit-code 1 --severity CRITICAL myapp:nightly
- if: success()
run: docker push registry/myapp:nightly
```
### Image signing (Sigstore / cosign)
```bash
cosign sign --key cosign.key registry/myapp:v1
cosign verify --key cosign.pub registry/myapp:v1
```
→ Supply chain attack 방어.
### Allowed registries (admission)
```
prod 만 trusted registry: company.io/...
public docker.io 차단 / proxy
```
## 🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| OSS / 무료 | Trivy / Grype |
| Docker 친화 | Docker Scout |
| Enterprise | Snyk / Aqua / Sysdig |
| 최소 attack surface | Distroless / Chainguard |
| 규제 / SBOM | Syft + cosign |
| Quick start | Alpine multi-stage |
## ❌ 안티패턴
- **Latest tag prod**: 추적 불가. semver.
- **Root user**: container escape 시 host 권한.
- **모든 거 한 stage**: 큰 image + build 도구 노출.
- **Secret in image layer**: leak.
- **Scan 안 함**: CVE 모름.
- **CRITICAL ignore**: scan 의미 없음.
- **Base 안 update**: 옛 patch 가 누적.
- **Daily rebuild 안**: 새 CVE 패치 안 됨.
## 🤖 LLM 활용 힌트
- Trivy + multi-stage + non-root + distroless 4종.
- Daily rebuild + signing.
- SBOM 가 compliance 표준.
## 🔗 관련 문서
- [[DevOps_Kubernetes_Basics]]
- [[DevSec_Supply_Chain]]
- [[DevOps_CI_CD_Pipeline_Patterns]]