Harbor 레지스트리 + Cosign 서명 + Trivy 스캔 + 배포 차단 정책(OPA/Kyverno)으로 컨테이너 공급망 보안 완성
이미지 한 장이 프로덕션 전부를 흔듭니다. 오늘은 사설 레지스트리(Harbor)에 취약점 스캔(Trivy)과 이미지 서명(Cosign)을 붙이고, 배포 차단 정책(OPA Gatekeeper 또는 Kyverno)으로 “서명+무중대취약”일 때만 배포되게 만드는 실전 구성을 단계별로 정리합니다. Ubuntu 24.04 + Kubernetes 1.28+ 기준입니다.
1) 아키텍처 한 장 요약
[빌드 파이프라인(GH Actions/GitLab CI)]
└─(푸시)─> [Harbor(Trivy 스캔)]
└─(서명 참조/저장)─> [Cosign(OCI 레퍼런스: <image>.sig)]
K8s 배포
└─ Admission(OPA/Kyverno): "이 이미지, 서명 OK? 중대취약 0?"
└─ 조건 만족 = 허용 / 불만족 = 차단
핵심은 이미지 무결성(서명)과 보안 기준(취약점 임계)을 클러스터 입장 시 검사한다는 점입니다. 레지스트리/CI가 아닌 Kubernetes 게이트에서 “막아내야” 안전합니다.
2) Harbor 설치(도커 컴포즈, Trivy 내장)
Harbor는 docker-compose 번들로 간단히 시작할 수 있습니다.
# 2-1) 바이너리 다운로드(버전은 환경에 맞게)
curl -LO https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-online-installer-v2.10.0.tgz
tar xzf harbor-online-installer-v2.10.0.tgz && cd harbor
# 2-2) 설정 템플릿 생성
cp harbor.yml.tmpl harbor.yml
# harbor.yml에서 hostname, https(선택), 관리자 비번, data volume 경로 등 수정
# Trivy 스캐너는 기본 내장(enabled=true)
# 2-3) 설치
./prepare
sudo ./install.sh
# 2-4) 접속
# http(s)://<hostname>/ 로 로그인(admin / 비번)
Harbor > Projects에서 private 프로젝트를 만들고, Configuration → Vulnerability에서 “scan on push” 옵션을 활성화하면 푸시 즉시 Trivy가 스캔합니다.
3) 개발자가 이미지 푸시하기(로그인/태깅/푸시)
# 로컬에서 Harbor 로그인
docker login harbor.example.com
# 이미지 태깅 후 푸시
docker build -t order-api:1.2.3 .
docker tag order-api:1.2.3 harbor.example.com/private/order-api:1.2.3
docker push harbor.example.com/private/order-api:1.2.3
푸시 후 Harbor UI에서 Vulnerabilities 탭을 열면 Trivy 결과가 보입니다. “Critical/High 0” 또는 기준 이하일 때만 프로덕션으로 승격되도록 정책을 잡겠습니다.
4) Cosign으로 이미지 서명(키 페어 / 키리스)
4-1) 설치 & 키 생성
curl -sSL https://raw.githubusercontent.com/sigstore/cosign/main/install.sh | sudo bash
cosign version
# 키 페어(패스워드 프롬프트 발생)
cosign generate-key-pair
# cosign.key / cosign.pub 생성
4-2) 서명 & 검증
# 서명
cosign sign --key cosign.key harbor.example.com/private/order-api:1.2.3
# 검증
cosign verify --key cosign.pub harbor.example.com/private/order-api:1.2.3
# 결과에 “Verified OK” 및 주체(subject) 메타가 출력
Cosign은 OCI 레이아웃을 이용해 레지스트리에 .sig 아티팩트를 같이 저장합니다.
따라서 별도 스토리지 없이 “이미지 옆”에 서명이 보관됩니다.
5) CI 파이프라인에 서명·메타데이터 첨부
GitHub Actions 예시(요지):
name: build-sign-push
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: harbor.example.com
username: ${{ secrets.HARBOR_USER }}
password: ${{ secrets.HARBOR_PASS }}
- name: Build & Push
run: |
TAG=harbor.example.com/private/order-api:${GITHUB_SHA::7}
docker build -t $TAG .
docker push $TAG
- name: Cosign Sign
env:
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
run: |
printf '%s' "${{ secrets.COSIGN_KEY }}" > cosign.key
printf '%s' "${{ secrets.COSIGN_PUB }}" > cosign.pub
cosign sign --key cosign.key \
--annotations git_sha=${GITHUB_SHA} \
--annotations built_at=$(date -u +%FT%TZ) \
$TAG
빌드 정보가 annotations로 서명에 포함되어 감사 추적성이 좋아집니다(누가/언제/무엇을 배포했는지).
6) Kubernetes에서 “서명 필수 + 중대취약 0” 정책 강제
두 가지 길이 있습니다. Kyverno(표현 간단) 또는 OPA Gatekeeper(Rego 기반). 여기서는 Kyverno를 예시로 듭니다.
6-1) Kyverno 설치
kubectl create -f https://raw.githubusercontent.com/kyverno/kyverno/release-1.12/config/install.yaml
kubectl -n kyverno get pods
6-2) Cosign 서명 요구 정책(이미지 당 1개 이상 서명)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-cosign-signature
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-image-signature
match:
any:
- resources:
kinds: ["Pod","Deployment","StatefulSet","DaemonSet","Job","CronJob"]
verifyImages:
- image: "harbor.example.com/private/*"
key: |
-----BEGIN PUBLIC KEY-----
(여기에 cosign.pub 내용)
-----END PUBLIC KEY-----
attestations: []
이제 harbor.example.com/private/... 이미지는 cosign.pub로 검증 가능한 서명이 없으면 Admission에서 거절됩니다.
6-3) Trivy 결과 기반 “중대취약 0” 정책
Harbor는 스캔 결과를 Harbor API로 제공합니다. 간단한 방식은 이미지 태그 규칙 또는
레이블(Annotation)로 “검증 완료 이미지만 배포”하는 것입니다. 더 엄격하게 하려면
policy-controller(Cosign의 정책 엔진)나 Kyverno의 externalData를 이용해
API 응답을 평가합니다. 여기서는 실무에서 손쉬운 “태그 규약” + “서명에 스캔 요약을 담기” 조합을 보여줍니다.
# 빌드 파이프라인에서 Trivy CLI로 사전 스캔 후,
# "nohigh" 같은 태그를 추가 푸시(고위험/치명 0일 때만)
trivy image --exit-code 1 --severity CRITICAL,HIGH $TAG || exit 1
docker tag $TAG ${TAG}-nohigh
docker push ${TAG}-nohigh
# 그리고 서명도 nohigh 태그에 대해 진행
cosign sign --key cosign.key ${TAG}-nohigh
Kyverno로 “-nohigh 태그만 허용”하면 손쉽게 “중대취약 0”을 강제할 수 있습니다.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: allow-only-nohigh-tag
spec:
validationFailureAction: Enforce
background: false
rules:
- name: require-nohigh-tag
match:
any:
- resources:
kinds: ["Pod","Deployment","StatefulSet","DaemonSet","Job","CronJob"]
validate:
message: "이미지는 반드시 -nohigh 태그여야 합니다(High/Critical 0)."
pattern:
spec:
template:
spec:
containers:
- image: "harbor.example.com/private/*-nohigh"
엄격한 API 연동까지 하려면 Kyverno context + HTTPReference로 Harbor API를 조회하거나,
policy-controller를 도입해
Cosign attestation에 Trivy SBOM/결과를 싣고 정책으로 평가하세요.
7) 배포 예시(허용/거부 확인)
# (허용) 서명 + -nohigh 태그
kubectl -n prod set image deploy/order-api app=harbor.example.com/private/order-api:1a2b3c4-nohigh
# (거부) 서명 없음 or -nohigh 아님
kubectl -n prod set image deploy/order-api app=harbor.example.com/private/order-api:latest
# Admission 웹훅에서 "require-cosign-signature / require-nohigh-tag" 위반으로 실패
8) 운영 체크리스트
- 레지스트리: Harbor “scan on push” 활성화, 프로젝트 별 권한 최소화
- 서명 키: cosign.key는 External Secrets 등으로 K8s에 안전 보관(또는 키리스 OIDC 활용)
- CI: “빌드 → 스캔 → 태깅(-nohigh) → 서명 → 푸시” 순서 고정
- 정책: Kyverno/Gatekeeper 정책의 Enforce 모드 적용 전 Audit로 시뮬레이션
- 가시성: Harbor 웹훅/알림, Kyverno 정책 위반 이벤트를 Slack/Alertmanager로 통지
9) 자주 겪는 문제와 해결
- 서명 검증 실패: 이미지 도메인/태그 불일치, cosign.pub 오타, 프록시로 인한 레지스트리 접근 실패 점검
- Trivy 스캔 누락: 프로젝트 설정에서 “scan on push” 켜기, 오프라인 DB 동기화 실패 시 네트워크/프록시 확인
- 정책 오탐: Init/Sidecar 이미지까지 규칙이 적용되는지 구조 확인,
match.exclude로 예외 처리 - 퍼포먼스: Admission 병목 시 Kyverno 리소스 상향, 정책 단순화(정규식 최소화)
10) 마무리
공급망 보안은 레지스트리-빌드-클러스터를 한 줄로 묶는 일입니다. Harbor(스캔) + Cosign(서명) + Kyverno/OPA(정책) 삼박자를 맞추면 “검증된 이미지”만 프로덕션에 들어옵니다. 오늘은 한 서비스부터 시작해 보세요. 푸시 즉시 스캔, -nohigh 태그, 서명 필수 정책만으로도 체감 효과가 큽니다.
'배포·CI,CD·클라우드' 카테고리의 다른 글
| Testcontainers로 DB 통합 테스트 완벽 구축: Docker 없이 자동 실행하는 실전 예제 가이드 (0) | 2025.11.29 |
|---|---|
| 클라우드 환경에서의 비용 효율화 — AWS/GCP 비용 줄이는 실무 전략 (0) | 2025.11.21 |
| Velero로 Kubernetes 백업·복구: S3/MinIO 연동, PV 스냅샷, 네임스페이스 단위 복원 (0) | 2025.11.02 |
| External Secrets로 시크릿 자동 주입: AWS Secrets Manager/KMS · Kubernetes 네이티브 시크릿 관리 (1) | 2025.11.01 |
| Argo CD로 GitOps 파이프라인 구축: App-of-Apps, 환경 분리, 자동 동기화/PR 기반 승격 (0) | 2025.11.01 |