이미지 서명 — Cosign과 공급망 보안
Cosign으로 컨테이너 이미지에 서명하고 검증하는 방법, 키 기반 서명과 Keyless 서명(OIDC + Rekor) 비교, Kyverno ClusterPolicy로 서명되지 않은 이미지 배포 차단, SBOM 첨부 방법을 다룹니다.
지난 글에서 Trivy로 이미지 취약점을 스캔하는 방법을 다뤘다. 이번 글에서는 공급망 보안(Supply Chain Security)의 핵심인 이미지 서명을 살펴본다. 이미지를 서명하면 “이 이미지가 신뢰할 수 있는 파이프라인에서 만들어졌고 이후 변조되지 않았음”을 암호학적으로 증명할 수 있다.
왜 이미지 서명이 필요한가
취약점 스캔은 알려진 CVE를 찾아낸다. 하지만 이미지 레지스트리가 해킹되어 이미지가 악성 코드로 교체되는 경우는 스캔으로 막기 어렵다. 이미지 서명은 이런 공급망 공격(Supply Chain Attack)을 방어한다.
- 빌드 시 CI/CD 파이프라인이 이미지에 서명을 첨부
- 배포 시 Kubernetes Admission Control이 서명 유효성 검증
- 서명 없는 이미지는 클러스터에 진입 불가
Cosign 설치 및 기본 사용법
Cosign은 Sigstore 프로젝트의 이미지 서명 도구다.
# 설치
brew install cosign # macOS
curl -O https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
# 버전 확인
cosign version
키 기반 서명
키 쌍을 생성해 서명한다. 개인키는 안전하게 보관하거나 KMS에 저장한다.
# 키 쌍 생성 (cosign.key, cosign.pub 생성)
cosign generate-key-pair
# 이미지 서명 (이미지가 레지스트리에 먼저 푸시되어야 함)
cosign sign --key cosign.key \
my-registry/myapp:v2.0.0
# 서명 검증
cosign verify --key cosign.pub \
my-registry/myapp:v2.0.0
# KMS (AWS KMS) 사용
cosign sign --key awskms:///arn:aws:kms:us-east-1:123:key/xxx \
my-registry/myapp:v2.0.0
서명은 이미지 다이제스트에 대해 생성되고, OCI 레지스트리에 이미지와 함께 저장된다. 서명은 별도의 태그(sha256-xxx.sig)로 푸시된다.
Keyless 서명 — 키 관리 없이 OIDC로 서명
Keyless 방식은 개인키 없이 OIDC 토큰(GitHub Actions, GCP, AWS IAM)을 사용해 일시적 키를 생성하고 Rekor 투명성 로그에 기록한다.
# .github/workflows/sign.yaml
- name: Sign image with Cosign
env:
COSIGN_EXPERIMENTAL: "1" # Keyless 활성화
run: |
cosign sign \
--oidc-issuer https://token.actions.githubusercontent.com \
my-registry/myapp@${{ steps.build.outputs.digest }}
GitHub Actions는 자동으로 OIDC 토큰을 발급하므로, ACTIONS_ID_TOKEN_REQUEST_URL 환경 변수가 있으면 추가 설정 없이 Keyless 서명이 동작한다. 서명은 Rekor(https://rekor.sigstore.dev)에 투명하게 기록된다.
# Keyless 서명 검증
cosign verify \
--certificate-identity-regexp ".*github.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
my-registry/myapp:v2.0.0
Kyverno로 서명되지 않은 이미지 차단
# ClusterPolicy — 서명 검증 (키 기반)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Audit # 처음엔 Audit으로 시작
background: false
rules:
- name: check-image-signature
match:
any:
- resources:
kinds: [Pod]
namespaces: [production]
verifyImages:
- imageReferences:
- "my-registry/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIK...
-----END PUBLIC KEY-----
Audit 모드에서 시작해 서명되지 않은 이미지 목록을 파악한 후, Enforce로 전환해 차단을 활성화하는 단계적 접근이 중요하다. 모든 이미지에 일괄 적용하면 시스템 이미지나 사이드카 이미지가 차단될 수 있다.
SBOM 생성 및 첨부
Software Bill of Materials(SBOM)은 이미지에 포함된 모든 패키지 목록이다. Cosign으로 SBOM을 이미지에 첨부하면 감사(Audit) 추적이 가능하다.
# Syft로 SBOM 생성
syft my-registry/myapp:v2.0.0 -o cyclonedx-json > sbom.json
# Cosign으로 SBOM 첨부
cosign attest --key cosign.key \
--type cyclonedx \
--predicate sbom.json \
my-registry/myapp:v2.0.0
# SBOM 검증 및 조회
cosign verify-attestation \
--key cosign.pub \
--type cyclonedx \
my-registry/myapp:v2.0.0 | jq '.payload | @base64d | fromjson'
Cosign의 attest 명령으로 서명된 SBOM, 취약점 스캔 결과, 빌드 메타데이터 등을 이미지에 첨부할 수 있다. 이를 통해 “이 이미지는 X 환경에서 Y 시간에 Z가 스캔했고 취약점이 없었음”을 증명할 수 있다.
지난 글: 컨테이너 이미지 스캔 — Trivy와 취약점 관리
다음 글: Image Pull Secrets — 프라이빗 레지스트리 인증
읽어주셔서 감사합니다. 😊