Sigstore 에코시스템(Cosign, Fulcio, Rekor)으로 컨테이너 이미지에 키리스 서명을 적용하고, SLSA 프레임워크 기반의 빌드 출처 증명을 구현합니다.
이전 장들에서 이미지 스캐닝과 SBOM 생성을 다루었습니다. 그러나 이것만으로는 한 가지 핵심 질문에 답할 수 없습니다. "이 이미지가 정말로 우리 CI/CD 파이프라인에서 빌드된 것인가?"
**Image Signing(이미지 서명)**은 이미지의 출처와 무결성을 보장합니다. 서명이 있으면 다음을 확인할 수 있습니다.
Sigstore는 소프트웨어 아티팩트의 서명, 검증, 투명성을 위한 오픈소스 프로젝트입니다. Linux Foundation 산하의 **OpenSSF(Open Source Security Foundation)**에서 관리합니다.
Sigstore는 세 가지 핵심 컴포넌트로 구성됩니다.
Cosign은 컨테이너 이미지, SBOM, 기타 OCI 아티팩트에 서명하고 검증하는 CLI 도구입니다.
# Homebrew (macOS/Linux)
brew install cosign
# Go로 설치
go install github.com/sigstore/cosign/v2/cmd/cosign@latestFulcio는 코드 서명용 인증 기관(CA)입니다. OIDC(OpenID Connect) 토큰을 기반으로 단기 인증서를 발급합니다. 개발자가 별도의 키 쌍을 관리할 필요 없이, GitHub, Google, Microsoft 등의 ID 제공자를 통해 자신의 정체를 증명하면 서명용 인증서를 받을 수 있습니다.
Rekor는 서명 기록을 저장하는 **Transparency Log(투명성 로그)**입니다. 모든 서명 이벤트가 변조 불가능한 로그에 기록되어, 누구든지 특정 이미지가 언제, 누구에 의해 서명되었는지 검증할 수 있습니다.
Fulcio가 발급하는 인증서는 보통 10분 내외의 매우 짧은 유효 기간을 가집니다. 키 유출 위험을 최소화하기 위한 설계입니다. 서명 당시의 유효성은 Rekor 투명성 로그를 통해 사후에도 검증할 수 있습니다.
전통적인 서명 방식에서는 개인 키를 생성하고 안전하게 보관해야 했습니다. **Keyless Signing(키리스 서명)**은 이 부담을 제거합니다.
# 이미지 빌드 및 푸시
docker build -t registry.example.com/myapp:v1.0 .
docker push registry.example.com/myapp:v1.0
# 키리스 서명 (브라우저에서 OIDC 인증)
cosign sign registry.example.com/myapp:v1.0
# 서명 검증
cosign verify registry.example.com/myapp:v1.0 \
--certificate-identity=user@example.com \
--certificate-oidc-issuer=https://accounts.google.comcosign sign 명령을 실행하면 브라우저가 열리고 OIDC 인증을 요청합니다. 인증이 완료되면 Fulcio에서 단기 인증서를 받아 서명하고, Rekor에 기록합니다.
GitHub Actions에서는 별도 인증 없이 **Workload Identity(워크로드 아이덴티티)**를 사용하여 자동으로 서명할 수 있습니다.
name: Build, Sign, and Push
on:
push:
branches: [main]
jobs:
build-sign-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # OIDC 토큰 발급에 필요
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
id: build
uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Sign image with Cosign (keyless)
run: |
cosign sign --yes \
ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
- name: Generate and attach SBOM
run: |
syft ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }} \
-o cyclonedx-json > sbom.json
cosign attach sbom \
--sbom sbom.json \
ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
cosign sign --yes \
--attachment sbom \
ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}위 워크플로우에서 주목할 점들을 정리합니다.
id-token: write 권한이 있으면 GitHub Actions가 자동으로 OIDC 토큰을 발급합니다.--yes 플래그는 확인 프롬프트를 건너뛰어 자동화에 적합합니다.이미지 태그가 아닌 **다이제스트(digest)**로 서명해야 합니다. 태그는 다른 이미지를 가리키도록 변경할 수 있지만, 다이제스트는 이미지 내용의 해시이므로 변경할 수 없습니다.
키리스 서명이 권장되지만, 에어갭 환경 등 외부 서비스에 접근할 수 없는 경우에는 전통적인 키 기반 서명을 사용합니다.
# 키 쌍 생성
cosign generate-key-pair
# 키로 이미지 서명
cosign sign --key cosign.key registry.example.com/myapp:v1.0
# 공개 키로 서명 검증
cosign verify --key cosign.pub registry.example.com/myapp:v1.0키 기반 서명에서는 개인 키의 안전한 보관이 핵심입니다. **KMS(Key Management Service)**를 사용하는 것이 가장 안전합니다.
# AWS KMS
cosign sign --key awskms:///arn:aws:kms:ap-northeast-2:123456789:key/abc-123 \
registry.example.com/myapp:v1.0
# HashiCorp Vault
cosign sign --key hashivault://transit/keys/cosign \
registry.example.com/myapp:v1.0서명을 생성하는 것만으로는 충분하지 않습니다. 클러스터에 배포되는 모든 이미지의 서명을 자동으로 검증하는 정책이 필요합니다.
Kyverno는 쿠버네티스 네이티브 정책 엔진으로, 이미지 서명 검증을 선언적으로 정의할 수 있습니다.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-cosign-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: "https://rekor.sigstore.dev"이 정책이 적용되면, ghcr.io/myorg/ 하위의 이미지를 포함하는 모든 파드는 GitHub Actions에서 서명된 이미지만 배포할 수 있습니다. 서명이 없거나 검증에 실패하면 파드 생성이 차단됩니다.
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8scosignsignature
spec:
crd:
spec:
names:
kind: K8sCosignSignature
validation:
openAPIV3Schema:
type: object
properties:
allowedRegistries:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8scosignsignature
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not startswith(container.image, "ghcr.io/myorg/")
msg := sprintf(
"이미지 %v는 허용된 레지스트리가 아닙니다",
[container.image]
)
}Kyverno가 이미지 서명 검증에 더 직관적인 문법을 제공하므로, 서명 검증이 주 목적이라면 Kyverno를 먼저 고려하세요. OPA Gatekeeper는 더 복잡한 정책 로직이 필요할 때 강점이 있습니다.
**SLSA(Supply chain Levels for Software Artifacts)**는 소프트웨어 공급망의 무결성을 보장하기 위한 프레임워크입니다. 빌드 프로세스의 신뢰 수준을 4개 레벨로 정의합니다.
| 레벨 | 요구사항 | 의미 |
|---|---|---|
| SLSA 1 | 빌드 프로세스 문서화 | 빌드 출처를 추적할 수 있음 |
| SLSA 2 | 호스팅된 빌드 서비스 + 출처 증명 생성 | 빌드 프로세스가 변조되기 어려움 |
| SLSA 3 | 격리된 빌드 + 소스 검증 | 빌드가 소스와 정확히 일치함을 보장 |
| SLSA 4 | 2인 리뷰 + 재현 가능한 빌드 | 최고 수준의 공급망 무결성 |
**Provenance(출처 증명)**는 이미지가 어디서, 어떻게 빌드되었는지를 기록한 증명서입니다.
name: SLSA Build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Build and push
id: build
uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Generate SLSA Provenance
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
with:
image: ghcr.io/${{ github.repository }}
digest: ${{ steps.build.outputs.digest }}Provenance에는 다음 정보가 포함됩니다.
서명된 이미지의 검증 과정을 상세히 살펴보겠습니다.
# 키리스 서명 검증 (ID + 발급자 지정)
cosign verify \
--certificate-identity="https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
ghcr.io/myorg/myapp:v1.0
# 검증 결과를 JSON으로 출력
cosign verify \
--certificate-identity-regexp=".*@myorg.com" \
--certificate-oidc-issuer="https://accounts.google.com" \
--output-text \
registry.example.com/myapp:v1.0
# SBOM 서명 검증
cosign verify-attestation \
--type cyclonedx \
--certificate-identity-regexp=".*" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
ghcr.io/myorg/myapp:v1.0
# Rekor에서 서명 기록 조회
rekor-cli search --email user@example.com
rekor-cli get --uuid <entry-uuid>이번 장에서는 Sigstore 에코시스템을 활용한 이미지 서명의 전체 흐름을 다루었습니다.
다음 장에서는 컨테이너가 실행된 이후의 보안, 즉 런타임 보안을 다룹니다. Falco를 사용하여 시스콜 모니터링, 비정상 행동 감지, 실시간 알림 시스템을 구축하는 방법을 실습합니다.
이 글이 도움이 되셨나요?
Falco의 eBPF 기반 시스콜 모니터링으로 컨테이너 런타임 위협을 실시간 감지하고, 규칙 작성부터 알림 통합까지 런타임 보안 체계를 구축합니다.
SBOM(소프트웨어 자재 명세서)의 개념과 필요성, SPDX와 CycloneDX 형식을 비교하고, Syft와 Trivy로 SBOM을 생성하여 공급망 가시성을 확보하는 방법을 실습합니다.
쿠버네티스 NetworkPolicy로 기본 거부 정책을 구현하고, Calico/Cilium 네트워크 정책과 Istio mTLS로 컨테이너 간 통신을 안전하게 제어합니다.