~/infra-dev.md
INFRA_DEV

Docker Content Trust 종료는 왜 “서명 도구 교체”가 아니라 배포 정책 마이그레이션일까

10분 읽기·2026.07.25·출처 5·00
오늘의 질문

컨테이너 이미지 서명을 DCT에서 Cosign/Notation으로 바꾸면, CI 명령만 바꾸면 끝일까 아니면 클러스터 admission 정책까지 함께 옮겨야 할까?

infra-dev.md
DCT migration

Docker Content Trust 종료는 왜 “서명 도구 교체”가 아니라 배포 정책 마이그레이션일까

1. 왜 지금 봐야 하나

Docker는 2026년 6월 16일 Docker Content Trust(DCT)와 notary.docker.io의 Notary v1 서비스를 완전히 종료하겠다고 안내했다. Docker의 일정에 따르면 DCT 쓰기 brownout은 2026년 7월 14일과 15일, 읽기 brownout은 8월 10일과 12일, 전체 종료는 12월 8일이다. 중요한 점은 일반 docker pull/docker push가 멈춘다는 뜻이 아니라, DOCKER_CONTENT_TRUST=1이나 docker trust sign/inspect/revoke 같은 DCT 신뢰 작업이 영향을 받는다는 것이다.

작은 팀에서는 이 이슈를 “오래된 Docker 옵션 하나가 사라지는구나” 정도로 넘기기 쉽다. 하지만 CI/CD나 Kubernetes admission 정책에서 DCT를 실제로 쓰고 있었다면 문제의 범위는 훨씬 넓다.

  • CI가 이미지를 빌드한 뒤 무엇으로 서명하는가?
  • 배포 환경은 태그가 아니라 digest를 강제하는가?
  • 서명은 이미지와 같은 OCI registry에 함께 저장되는가?
  • 클러스터는 서명된 이미지만 admission 하는가?
  • 기존 DCT 실패를 감지하던 알림은 새 검증 실패에도 울리는가?

Docker 문서는 Notary v1이 더 이상 유지보수되지 않고, 생태계가 OCI-native signing으로 이동했기 때문에 DCT를 종료한다고 설명한다. 대안으로는 repeatable pull을 위한 digest pinning, publisher identity를 위한 Sigstore/Cosign 또는 Notary Project/Notation, 배포 시점 강제를 위한 Kyverno 또는 Ratify를 제시한다. 즉, 이번 변화의 핵심은 “서명 CLI를 하나 바꾸기”가 아니라 이미지 무결성, 게시자 신원, admission enforcement를 나누어 다시 설계하는 것이다.

2. 핵심 개념

컨테이너 이미지 신뢰를 이해할 때 세 가지를 분리해야 한다.

첫째, digest pinning은 내용 고정이다. nginx:latest처럼 tag로 배포하면 같은 이름이 나중에 다른 이미지 digest를 가리킬 수 있다. 반면 image@sha256:... 형태는 정확히 그 바이트 묶음을 가리킨다. Docker 블로그도 digest pinning이 content integrity에는 도움을 주지만, publisher identity를 증명하지는 않는다고 구분한다. 쉽게 말해 digest는 “내가 검증한 바로 그 파일인가?”에는 답하지만 “누가 만들었는가?”에는 충분히 답하지 못한다.

둘째, signature는 게시자 또는 빌드 주체의 신원 증명이다. Cosign은 OIDC로 인증해 ephemeral key 방식의 keyless signing을 사용할 수 있고, GitHub·Google·Microsoft 같은 OIDC identity와 연결할 수 있다. Notation은 Notary Project의 CLI로, certificate 기반 trust policy를 구성해 이미지를 서명하고 검증한다. 두 방식 모두 현대 registry의 OCI artifact/referrer 모델과 잘 맞도록 발전하고 있다.

셋째, admission enforcement는 배포 경계에서 정책을 강제하는 일이다. 서명은 했지만 클러스터가 아무 이미지나 받아주면 운영 리스크는 그대로다. Kyverno의 verifyImages는 이미지 signature와 attestation을 검증하고, tag를 digest로 mutate하거나 digest 사용을 검증할 수 있다. Ratify는 AKS/ACR/Key Vault 예시에서 볼 수 있듯 signed image만 배포되도록 Kubernetes admission 경로에 검증 엔진을 붙이는 접근을 제공한다.

이 셋을 합치면 신뢰 체인은 다음처럼 보인다.

zsh — 생존확인.sh
CI build → image digest 확정 → Cosign/Notation 서명 → registry에 signature 저장
       → 배포 manifest는 digest 사용 → admission controller가 서명/attestation 검증
       → 검증 성공 이미지 경로만 운영 클러스터에 들어감

DCT에서 빠져나온다는 말은 이 체인을 새 도구로 다시 연결한다는 뜻이다.

3. 최신 이슈와 연결

Docker의 DCT 종료 안내에는 운영자가 바로 확인해야 할 현실적인 단서가 있다.

  • DCT는 opt-in이었기 때문에 대부분의 일반 pull은 영향을 받지 않는다.
  • 영향을 받는 팀은 DOCKER_CONTENT_TRUST=1, docker trust 명령, DCT signature를 보는 admission policy, Docker Hub publishing signing에 의존하는 팀이다.
  • brownout은 dry run이다. 특히 write brownout이 read brownout보다 먼저 오므로, publisher와 CI가 먼저 깨진다.
  • Docker는 DCT에서 Cosign/Notation, Kyverno/Ratify, Docker Hardened Images 같은 현대 대안으로 이동하라고 안내한다.

여기서 개발자에게 중요한 해석은 “서명 실패가 언제 드러나는가”다. DCT에 의존한 publisher라면 7월 write brownout 때 이미지 publish 단계가 실패할 수 있다. DCT 검증에 의존한 consumer라면 8월 read brownout 때 배포 검증이나 pull 전 검증이 실패할 수 있다. 그리고 12월 shutdown 이후에는 임시 회피가 아니라 실제 마이그레이션 없이는 복구되지 않는다.

Cosign 문서는 cosign sign $IMAGE 형태의 keyless signing, KMS key 사용, attestation, registry에 signature를 저장하는 흐름을 설명한다. Notary Project 문서는 tag가 아니라 digest 기준으로 이미지를 식별하고, notation sign, trust policy, notation verify로 이어지는 검증 흐름을 보여준다. Kyverno 문서는 verifyImages가 mutation과 validation 양쪽에서 동작해 tag를 digest로 바꾸고 검증 annotation을 강제할 수 있음을 설명한다. Ratify 문서는 signed image만 AKS에 배포하는 end-to-end 경로를 보여준다.

즉 최신 이슈는 단순히 Docker의 제품 종료가 아니다. 컨테이너 생태계가 별도 Notary v1 신뢰 서버에서 OCI registry에 붙는 signature/referrer와 admission-time policy로 이동하고 있다는 신호다.

4. 개발자 관점 해석

인프라 개발자가 이 변화에서 가져가야 할 판단 기준은 네 가지다.

1) tag 신뢰를 줄이고 digest를 운영 단위로 삼는다

app:prod는 사람이 읽기 좋지만 운영 사고 분석에는 약하다. 같은 tag가 재사용되면 “어제 배포한 prod와 지금 prod가 같은가?”를 바로 답하기 어렵다. 배포 manifest, SBOM, vulnerability scan, signature, incident timeline은 모두 digest로 연결되어야 한다.

2) signing은 CI 작업이 아니라 identity 설계다

Cosign keyless signing을 쓰면 GitHub Actions 같은 OIDC identity를 signature에 연결할 수 있다. Notation을 쓰면 certificate와 trust policy를 통해 어느 CA/subject를 신뢰할지 정한다. 어느 쪽이든 질문은 같다.

이 이미지는 어느 repo, 어느 workflow, 어느 environment 권한으로 만들어졌는가?

따라서 signing step은 docker build 뒤에 붙는 보너스 명령이 아니라 CI 권한 모델의 일부다.

3) 검증은 배포 직전에 강제해야 한다

이미지를 서명해도 kubectl apply가 unsigned image를 받아주면 공격자는 우회 경로를 찾는다. Kyverno나 Ratify 같은 admission-time verifier를 붙여야 정책이 실제 경계가 된다. 특히 운영 클러스터에서는 “서명하지 않은 이미지는 배포되지 않는다”가 사람의 약속이 아니라 API server 앞의 규칙이어야 한다.

4) 마이그레이션은 한 번에 켜지 말고 shadow/evaluate로 시작한다

서명 정책은 잘못 켜면 배포 전체를 막는다. 먼저 inventory를 만들고, 어떤 namespace/image pattern이 unsigned인지 관찰한 뒤, staging에서 enforce하고, production은 alert-only 또는 audit 모드로 충분히 본 다음 강제하는 편이 안전하다.

5. 내 프로젝트에 적용할 체크포인트

아래 체크리스트를 오늘 바로 훑어보자.

DCT 의존성 찾기

  • CI/CD 설정에서 DOCKER_CONTENT_TRUST 검색
  • shell profile, Dockerfile, Compose 파일, deployment script에서 docker trust 검색
  • Kubernetes admission 정책에서 DCT/Notary v1 signature 검증 여부 확인
  • Docker Hub publisher라면 signing workflow가 DCT인지 확인

digest 운영 단위 만들기

  • 배포 manifest가 tag만 쓰는지 확인
  • CI가 build 후 image digest를 artifact로 남기는지 확인
  • vulnerability scan 결과와 SBOM이 digest에 연결되는지 확인
  • rollback runbook이 tag가 아니라 digest 기준인지 확인

signing 방식 선택

  • GitHub Actions/GCP/Azure 등 OIDC 기반 CI라면 Cosign keyless signing 후보 검토
  • 기존 PKI, certificate trust, enterprise registry 정책이 강하면 Notation 후보 검토
  • key를 직접 관리한다면 KMS 또는 Key Vault 연동을 우선 검토
  • signature가 사용하는 registry의 OCI artifact/referrer 지원 여부 확인

admission enforcement 설계

  • Kubernetes라면 Kyverno verifyImages 또는 Ratify 같은 검증기를 staging에 먼저 붙이기
  • mutateDigest, verifyDigest, required 같은 옵션을 어떤 namespace부터 켤지 정하기
  • unsigned third-party image 예외 목록을 만들고 만료일 붙이기
  • 검증 실패 로그가 알림으로 이어지는지 확인

6. 오늘 10분 액션

10분만 있다면 아래 순서로 움직이면 된다.

  1. repo 루트에서 DOCKER_CONTENT_TRUSTdocker trust를 검색한다.
  2. 현재 production manifest 하나를 골라 image가 tag인지 digest인지 확인한다.
  3. 최근 배포된 image digest 하나를 찾아 SBOM/scan/signature와 연결되는지 확인한다.
  4. 다음 스프린트 작업으로 “staging에서 Cosign 또는 Notation으로 샘플 이미지 서명하기”를 만든다.
  5. Kubernetes를 쓴다면 Kyverno verifyImages나 Ratify를 audit 모드로 검토하는 티켓을 만든다.

작은 팀이라면 처음부터 완벽한 supply-chain security를 만들 필요는 없다. 하지만 DCT 종료 시점에 맞춰 최소한 “tag를 digest로 고정하고, CI identity로 서명하고, 배포 경계에서 검증한다”는 세 단계는 잡아야 한다.

7. 더 볼 자료

  • Docker의 DCT 종료 글: brownout 일정과 영향 범위를 가장 먼저 확인할 자료다.
  • Sigstore Cosign 문서: OIDC 기반 keyless signing, KMS key, attestation, registry 저장 방식을 볼 수 있다.
  • Notary Project quickstart: Notation으로 digest 기준 이미지 서명과 trust policy 검증을 실습할 수 있다.
  • Kyverno verifyImages: admission 단계에서 signature/attestation과 digest 사용을 강제하는 Kubernetes 정책을 볼 수 있다.
  • Ratify quickstart: AKS/ACR/Key Vault 환경에서 signed image만 배포하는 end-to-end 흐름을 볼 수 있다.

중복 회피 메모

로컬 content/generated와 Supabase 최근 infra_dev 글을 확인했다. 최근 인프라 글은 Cloud Run multi-region service health, GKE custom rollout sequencing, GitHub Linked Artifacts 기반 code-to-cloud traceability, Kubernetes In-place Pod Resize, Terraform state secret, GitHub Actions step-level parallelism, OpenTelemetry semantic convention, RDS delayed replica, GitHub secret scanning triage를 다뤘다. 이번 글은 supply-chain security와 CI/CD 보안을 다시 다루지만 GitHub artifact 추적성이나 OIDC subject 설계가 아니라, Docker Content Trust/Notary v1 종료라는 현재 일정에 맞춰 컨테이너 이미지 신뢰 체인을 digest pinning, Cosign/Notation signing, Kyverno/Ratify admission enforcement로 마이그레이션하는 운영 절차에 집중해 중복을 피했다.

오늘 10분 액션+5%

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)