왜 컨테이너 이미지 보안이 중요한가
컨테이너 기반 인프라가 일반화되면서 이미지 보안은 현대 DevSecOps의 핵심 과제가 됐습니다. 2024년 Sysdig 보고서에 따르면 프로덕션 컨테이너의 87%가 심각(High) 이상의 취약점을 포함하고 있으며, 이 중 다수는 단순한 베이스 이미지 업데이트로 해결할 수 있는 것들입니다.
공격 표면은 크게 세 가지로 나뉩니다:
- 빌드 시: 취약한 베이스 이미지, 불필요한 패키지, 하드코딩된 시크릿
- 레지스트리: 서명되지 않은 이미지, 접근 제어 미비
- 런타임: 과도한 권한, 민감한 마운트, 비정상 프로세스 실행
빌드 단계 보안 강화
1. 최소화된 베이스 이미지 사용
# 나쁜 예 — 불필요한 패키지 포함
FROM ubuntu:22.04
# 좋은 예 — distroless로 최소화
FROM gcr.io/distroless/nodejs20-debian12
# 또는 Alpine 기반
FROM node:20-alpine3.19Alpine은 5MB 수준으로 공격 표면을 대폭 줄이고, distroless는 셸조차 없어 컨테이너 탈출 시 공격자가 할 수 있는 일을 최소화합니다.
2. 멀티스테이지 빌드
# 빌드 스테이지
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 프로덕션 스테이지 — 빌드 도구 제외
FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER nonroot
EXPOSE 3000
CMD ["dist/server.js"]3. 루트 사용자 금지
# 전용 비권한 사용자 생성
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuserKubernetes에서는 PodSecurityContext로 이를 강제할 수 있습니다:
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false취약점 스캐닝: Trivy 실전 활용
Trivy는 현재 가장 널리 쓰이는 오픈소스 컨테이너 스캐너입니다.
기본 스캔
# 이미지 스캔
trivy image nginx:1.25
# 심각도 필터링 (HIGH, CRITICAL만)
trivy image --severity HIGH,CRITICAL nginx:1.25
# JSON 출력 (CI 파이프라인용)
trivy image --format json --output results.json myapp:latest
# 특정 CVE 무시 (false positive 처리)
trivy image --ignorefile .trivyignore myapp:latestCI/CD 통합 (GitHub Actions)
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.IMAGE_TAG }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: 1 # 취약점 발견 시 빌드 실패
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarifSBOM 생성
SBOM(Software Bill of Materials)은 이미지에 포함된 모든 컴포넌트를 목록화합니다:
# CycloneDX 형식으로 SBOM 생성
trivy image --format cyclonedx --output sbom.json myapp:latest
# SPDX 형식
trivy image --format spdx-json --output sbom.spdx.json myapp:latest이미지 서명: Cosign + Sigstore
# Cosign 설치 및 키 생성
cosign generate-key-pair
# 이미지 서명
cosign sign --key cosign.key registry.example.com/myapp:latest
# 서명 검증
cosign verify --key cosign.pub registry.example.com/myapp:latest
# keyless 서명 (OIDC 기반, GitHub Actions)
cosign sign --yes registry.example.com/myapp:${{ github.sha }}Kubernetes에서 서명된 이미지만 허용하려면 Kyverno나 OPA Gatekeeper 정책을 사용합니다:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: check-image
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences: ["registry.example.com/*"]
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
...런타임 보안: Falco
Falco는 컨테이너 런타임에서 비정상 행위를 실시간 탐지합니다.
# falco-rules.yaml — 주요 탐지 규칙 예시
- rule: Terminal shell in container
desc: 컨테이너 내 셸 실행 탐지
condition: >
spawned_process and container and
proc.name in (shell_binaries)
output: >
Shell spawned in container (user=%user.name container=%container.name
image=%container.image.repository)
priority: WARNING
- rule: Write below etc
desc: /etc 디렉토리 쓰기 시도
condition: >
open_write and container and fd.directory=/etc
output: File opened for writing below /etc (user=%user.name file=%fd.name)
priority: ERROR레지스트리 보안 체크리스트
| 항목 | 권장 설정 |
|---|---|
| 접근 제어 | RBAC + 최소 권한 원칙 |
| 이미지 스캔 | 푸시 시 자동 스캔 활성화 |
| 서명 정책 | 서명된 이미지만 pull 허용 |
| 취약 이미지 | CRITICAL 발견 시 자동 격리 |
| 감사 로깅 | 모든 pull/push 이벤트 기록 |
| 보존 정책 | 오래된 이미지 자동 삭제 |
보안 강화 로드맵
- 즉시 적용 (1주): Trivy를 CI 파이프라인에 통합, CRITICAL 취약점 빌드 차단
- 단기 (1달): 멀티스테이지 빌드 + distroless/Alpine 마이그레이션, non-root 사용자 강제
- 중기 (3달): Cosign 이미지 서명 도입, Kyverno 서명 검증 정책 적용
- 장기 (6달): Falco 런타임 탐지, SBOM 자동 생성 및 취약점 추적 체계 완성
이미지 보안은 한 번 설정하고 끝나는 게 아닙니다. 새로운 CVE가 매일 발표되므로, 배포된 이미지를 주기적으로 재스캔하고 베이스 이미지를 최신으로 유지하는 자동화 체계를 구축하는 것이 핵심입니다.
취약점 스캔 결과 대응 의사결정표
Trivy가 수백 건을 쏟아낼 때, 전부 고치려다 아무것도 못 고치는 것이 최악입니다.
| 상황 | 판단 | 조치 |
|---|---|---|
| Critical + 수정 버전 존재 | 즉시 대응 | 베이스 이미지/패키지 버전 업 → 재빌드·재배포 |
| Critical인데 패치 미출시 | 노출면 평가 | 해당 라이브러리가 실제 실행 경로에 있는지 확인 — 미사용이면 예외 등록(만료일 필수), 사용 중이면 완화책(네트워크 격리 등) 문서화 |
| 취약점 대부분이 베이스 이미지 유래 | 구조 개선 | distroless/alpine 등 최소 이미지로 교체가 개별 패치보다 효과적 |
| 동일 CVE가 매주 재등장 | 프로세스 문제 | 베이스 이미지 주기적 리빌드 파이프라인(주 1회) 부재 — 자동화 |
| 오탐 판단 | 근거 기록 | .trivyignore에 사유·검토자·만료일 주석과 함께 등록 |
운영 체크리스트
- CI 게이트 기준 명문화 — 예: Critical=차단, High=경고 후 7일 내 조치
- 예외(ignore) 목록에 만료일 — 영구 예외 금지
- 런타임 이미지와 스캔 이미지의 태그 일치 검증 (latest 스캔은 무의미)
- 서명 검증을 배포 어드미션에서 강제해야 서명이 의미를 가짐
자주 묻는 질문 (FAQ)
Q. 컨테이너 이미지 보안 검증은 어떻게 하나요? A. ① 취약점 스캔 — Trivy·Grype로 OS·라이브러리 CVE 검사 ② 이미지 서명 검증 — Cosign으로 서명·검증해 변조 방지 ③ SBOM 생성 — 구성요소 목록화 ④ 베이스 이미지 최소화(distroless·slim)로 공격면 축소. CI 파이프라인에서 스캔을 게이트로 걸어 실패 시 배포를 막는 것이 핵심입니다.
Q. 이미지 스캔은 언제 하나요? A. 빌드 시(CI), 레지스트리 등록 후, 런타임(주기적 재스캔) 3단계 모두 권장합니다. CVE는 배포 이후에도 새로 공개되므로 이미 떠 있는 이미지도 주기적으로 재검증해야 합니다.
이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.