
sigstore
DevSecOps
GitHub-Actions
공급망보안
keyless
오늘 후배가 "이미지 서명하려는데 KMS 키 발급받아야 하냐"고 물어서 답해준 김에 정리한다. 2026년에 아직도 cosign에 개인 키 파일 물려서 쓰고 있다면 이 글이 도움 될 거다. GitHub Actions만 쓰면 키 관리 자체가 필요 없다.
keyless가 뭐하는 건가
cosign keyless는 서명할 때 개인 키 대신 CI의 OIDC 토큰을 쓴다. 흐름은 이렇다.
- GitHub Actions가 워크플로우 실행 중에 짧은 수명의 OIDC 토큰을 발급한다. 이 토큰에는 리포지토리, 브랜치, 워크플로우 경로 같은 identity 정보가 들어있다.
- cosign이 이 토큰을 Sigstore의 Fulcio에 넘긴다. Fulcio는 토큰을 검증하고 5~10분짜리 X.509 인증서를 발급한다.
- cosign이 이 인증서로 이미지를 서명하고, 서명 기록을 Rekor라는 공개 투명성 로그에 남긴다.
- 인증서는 만료돼서 사라진다. 훔쳐갈 개인 키 자체가 없다.
키 파일을 어디에 어떻게 보관할지 고민할 필요가 없고, 유출 걱정도 없다. 서명 주체가 "특정 리포의 특정 워크플로우"로 못 박히기 때문에 오히려 감사(audit)도 편하다.
실제 코드
GitHub Actions 스니펫이다. 핵심은 id-token: write 권한이다. 이거 안 주면 OIDC 토큰이 안 나온다.
name: build-and-sign
on:
push:
tags: ['v*']
jobs:
sign:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # 이거 필수. 빼먹으면 401 뜬다.
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: build
run: |
IMAGE=ghcr.io/${{ github.repository }}:${{ github.ref_name }}
docker build -t $IMAGE .
docker push $IMAGE
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' $IMAGE)
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
- uses: sigstore/cosign-installer@v3
- name: Sign
env:
COSIGN_EXPERIMENTAL: "1"
run: cosign sign --yes ${{ steps.build.outputs.digest }}
cosign sign --yes만 치면 브라우저가 뜨거나 프롬프트가 뜨지 않는다. Actions runner의 OIDC를 알아서 잡아서 Fulcio로 넘긴다. --yes는 "브라우저 로그인 없이 진행할게요" 확인용이다.
검증할 때
배포 파이프라인이나 admission controller에서 확인할 때는 이렇게 한다.
cosign verify \
--certificate-identity-regexp="https://github.com/myorg/myrepo/.github/workflows/.*" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
ghcr.io/myorg/myrepo@sha256:...
주의할 게 있는데, --certificate-identity를 그냥 리포 URL로만 쓰면 안 되고 워크플로우 경로까지 매칭해야 한다. 안 그러면 같은 리포의 아무 워크플로우나 서명할 수 있으니 의미가 반감된다. 우리 팀은 릴리스 전용 워크플로우 경로를 regex로 고정해 두고 쓴다.
함정 하나
fork에서 돌아가는 PR 워크플로우에서는 id-token: write가 자동으로 제한된다. 서명은 반드시 main 브랜치나 태그 이벤트에서만 하도록 워크플로우를 분리하는 게 좋다. 이거 모르고 PR CI에 서명 스텝 넣었다가 왜 안 되는지 하루 헤맨 적 있다.
키 관리 안 하는 김에 SLSA provenance도 같이 붙일 수 있는데, 그건 다음 글에서 다뤄보려고 한다.
태그: cosign, sigstore, DevSecOps, GitHub-Actions, 공급망보안, keyless
'IT > DevSecOps' 카테고리의 다른 글
| CVE 홍수 속에서 진짜 급한 것만 골라내기 — EPSS + KEV 실전 가이드 (0) | 2026.08.22 |
|---|---|
| Kyverno vs OPA Gatekeeper, 2026년에는 뭘 쓸까 (0) | 2026.08.21 |
| External Secrets Operator, IRSA로 시작하는 실전 가이드 (1) | 2026.08.13 |
| Vault vs AWS Secrets Manager, 우리 팀은 왜 하이브리드로 갔나 (0) | 2026.08.08 |
| Kyverno CEL 전환, 지금부터 준비하는 법 (0) | 2026.08.05 |