Docker의 콘텐츠 트러스트

Docker의 콘텐츠 트러스트

네트워크로 연결된 시스템끼리 데이터를 주고받을 때, '신뢰(trust)'는 아주 중요한 문제예요. 특히 인터넷처럼 안전하지 않은 채널을 통해 통신할 때는 데이터의 무결성(integrity)과 게시자(publisher)를 확실히 검증하는 게 필수적이죠. Docker Engine으로 공개 또는 비공개 레지스트리에 이미지를 push하고 pull 할 때, 콘텐츠 트러스트(Content Trust)를 활성화하면 어떤 채널을 통해서든 레지스트리에서 받은 모든 데이터의 무결성과 게시자를 검증할 수 있게 됩니다.

출처: 공식문서

본문

Docker 콘텐츠 트러스트(DCT)란?

Docker Content Trust(DCT)는 원격 Docker 레지스트리와 주고받는 데이터에 디지털 서명을 사용할 수 있게 해주는 기능이에요. 이 서명 덕분에 클라이언트 측이나 런타임 환경에서 특정 이미지 태그의 무결성과 게시자를 검증할 수 있습니다.

DCT를 통해 이미지 게시자는 자신의 이미지에 서명을 할 수 있고, 이미지 소비자는 pull 하는 이미지가 실제로 서명된 것인지 확인할 수 있어요. 게시자는 개인이나 조직이 수동으로 콘텐츠에 서명할 수도 있고, 자동화된 소프트웨어 공급망이 릴리스 프로세스의 일부로 서명을 수행할 수도 있습니다.

경고

Docker Content Trust(DCT)는 곧 지원이 종료될 예정이에요. notary.docker.io의 Notary v1 서비스는 2026년 12월 8일에 중단됩니다. 자세한 내용은 Docker Content Trust (DCT) 문서를 참고하세요.

이미지 태그와 DCT

개별 이미지 레코드는 다음과 같은 식별자 구조를 가집니다:

[REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]

하나의 이미지 REPOSITORY에는 여러 개의 태그가 붙을 수 있어요. 예를 들어 mongo 이미지에는 latest3.1.2 같은 태그가 모두 존재할 수 있죠. 이미지 게시자는 이미지와 태그 조합을 여러 번 빌드하면서 매번 이미지 내용을 변경할 수 있습니다.

DCT는 이미지의 TAG 부분과 연결됩니다. 각 이미지 저장소에는 게시자가 이미지 태그에 서명할 때 사용하는 키 세트가 있고, 게시자는 어떤 태그에 서명할지 자유롭게 결정할 수 있어요.

하나의 이미지 저장소에 서명된 태그와 서명되지 않은 태그가 함께 존재할 수 있습니다. 예를 들어 Mongo 이미지 저장소를 생각해 볼게요. latest 태그는 서명되지 않았는데 3.1.6 태그는 서명되어 있을 수 있어요. 태그에 서명할지 말지는 전적으로 이미지 게시자의 책임입니다. 아래 그림처럼 어떤 태그는 서명되어 있고, 어떤 태그는 그렇지 않을 수 있죠.

게시자는 특정 태그에 서명하거나 하지 않을 수 있어요. 그 결과, 서명되지 않은 태그와 같은 이름의 서명된 태그의 콘텐츠가 서로 일치하지 않을 수도 있습니다. 예를 들어 게시자가 someimage:latest 이미지를 push 하면서 서명을 했다고 해볼게요. 나중에 같은 게시자가 서명 없이 someimage:latest 이미지를 다시 push 하면, 이 두 번째 push는 서명되지 않은 latest 태그를 교체하지만 서명된 latest 버전에는 영향을 주지 않습니다. 이렇게 태그별로 서명 여부를 선택할 수 있기 때문에, 게시자는 공식적으로 서명하기 전에 서명되지 않은 버전의 이미지를 반복해서 작업할 수 있는 거예요.

이미지 소비자는 DCT를 활성화해서 사용하는 이미지가 서명된 것임을 보장할 수 있습니다. 소비자가 DCT를 켜면 서명된 신뢰할 수 있는 이미지만 pull, run, build 할 수 있어요. DCT를 활성화하는 건 레지스트리에 일종의 '필터'를 적용하는 것과 비슷합니다. 소비자에게는 서명된 이미지 태그만 보이고, 서명되지 않은 이미지 태그는 '보이지 않게' 되는 거죠.

반면 DCT를 활성화하지 않은 소비자에게는 Docker 이미지를 다루는 방식이 전혀 바뀌지 않아요. 서명 여부와 관계없이 모든 이미지가 그대로 보입니다.

Docker 콘텐츠 트러스트 키

이미지 태그에 대한 신뢰는 서명 키를 통해 관리됩니다. DCT를 사용하는 작업을 처음 실행하면 키 세트가 생성되는데, 키 세트는 다음과 같은 클래스의 키로 구성됩니다:

  • 이미지 태그에 대한 DCT의 루트 역할을 하는 오프라인 키
  • 태그에 서명하는 저장소 또는 태깅 키
  • 저장소의 신선도(freshness) 보안을 보장하는 타임스탬프 키 같은 서버 관리 키

아래 그림은 다양한 서명 키와 그 관계를 보여줍니다:

경고

루트 키는 한 번 잃어버리면 복구할 수 없어요. 다른 키를 잃어버린 경우 Docker Hub Support에 이메일을 보내면 되지만, 루트 키를 잃어버리면 그 전에 이 저장소의 서명된 태그를 사용했던 모든 소비자의 수동 개입이 필요합니다.

루트 키는 안전한 곳에 백업해 두는 것이 좋아요. 루트 키는 새 저장소를 만들 때만 필요하기 때문에, 오프라인 하드웨어에 보관하는 것이 이상적입니다. 키를 안전하게 보호하고 백업하는 방법에 대한 자세한 내용은 DCT용 키 관리 문서를 꼭 읽어보세요.

Docker 콘텐츠 트러스트로 이미지 서명하기

Docker CLI에서는 $ docker trust 명령어 구문으로 컨테이너 이미지에 서명하고 push 할 수 있어요. 이 기능은 Notary 기능 세트 위에 구축되어 있습니다. 자세한 내용은 Notary GitHub 저장소를 참고하세요.

이미지에 서명하기 위한 전제 조건은 Notary 서버(예: Docker Hub)가 연결된 Docker 레지스트리가 필요하다는 거예요. 설정 방법은 Notary 배포 문서를 참고하세요.

Docker 이미지에 서명하려면 델리게이션(delegation) 키 쌍이 필요합니다. 이 키는 $ docker trust key generate 명령으로 로컬에서 생성하거나, 인증 기관(CA)에서 생성할 수 있어요.

먼저 델리게이션 개인 키를 로컬 Docker 트러스트 저장소에 추가합니다. (기본적으로 ~/.docker/trust/에 저장됩니다.) $ docker trust key generate로 델리게이션 키를 생성했다면 개인 키가 자동으로 로컬 트러스트 저장소에 추가되지만, 별도의 키를 가져오는 경우에는 $ docker trust key load 명령을 사용해야 해요.

$ docker trust key generate jeff
Generating key for jeff...
Enter passphrase for new jeff key with ID 9deed25:
Repeat passphrase for new jeff key with ID 9deed25:
Successfully generated and loaded private key. Corresponding public key available: /home/ubuntu/Documents/mytrustdir/jeff.pub

이미 키가 있다면 이렇게 로드할 수도 있습니다:

$ docker trust key load key.pem --name jeff
Loading key from "key.pem"...
Enter passphrase for new jeff key with ID 8ae710e:
Repeat passphrase for new jeff key with ID 8ae710e:
Successfully imported key from key.pem

다음으로 델리게이션 공개 키를 Notary 서버에 추가해야 해요. 이 작업은 Notary에서 Global Unique Name(GUN)이라고 부르는 특정 이미지 저장소에 한정됩니다. 해당 저장소에 델리게이션을 처음 추가하는 경우라면, 이 명령이 로컬 Notary 표준 루트 키를 사용해 저장소를 초기화하기도 합니다. 저장소 초기화와 델리게이션의 역할에 대해 더 알고 싶다면 콘텐츠 트러스트 델리게이션 문서를 확인해 보세요.

$ docker trust signer add --key cert.pem jeff registry.example.com/admin/demo
Adding signer "jeff" to registry.example.com/admin/demo...
Enter passphrase for new repository key with ID 10b5e94:

마지막으로 델리게이션 개인 키를 사용해 특정 태그에 서명하고 레지스트리에 push 합니다.

$ docker trust sign registry.example.com/admin/demo:1
Signing and pushing trust data for local image registry.example.com/admin/demo:1, may overwrite remote trust data
The push refers to repository [registry.example.com/admin/demo]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1

또는 키를 이미 가져온 상태라면, DCT 환경 변수를 설정한 뒤 $ docker push 명령으로 이미지를 push 할 수도 있어요.

$ export DOCKER_CONTENT_TRUST=1

$ docker push registry.example.com/admin/demo:1
The push refers to repository [registry.example.com/admin/demo:1]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1

태그나 저장소의 원격 트러스트 데이터는 $ docker trust inspect 명령으로 확인할 수 있습니다:

$ docker trust inspect --pretty registry.example.com/admin/demo:1

Signatures for registry.example.com/admin/demo:1

SIGNED TAG DIGEST SIGNERS
1 3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e jeff

List of signers and their keys for registry.example.com/admin/demo:1

SIGNER KEYS
jeff 8ae710e3ba82

Administrative keys for registry.example.com/admin/demo:1

 Repository Key: 10b5e94c916a0977471cc08fa56c1a5679819b2005ba6a257aa78ce76d3a1e27
 Root Key: 84ca6e4416416d78c4597e754f38517bea95ab427e5f95871f90d460573071fc

태그의 원격 트러스트 데이터는 $ docker trust revoke 명령으로 제거할 수 있어요:

$ docker trust revoke registry.example.com/admin/demo:1
Enter passphrase for signer key with ID 8ae710e:
Successfully deleted signature for registry.example.com/admin/demo:1

Docker 콘텐츠 트러스트로 클라이언트 검증하기

콘텐츠 트러스트는 Docker 클라이언트에서 기본적으로 비활성화되어 있어요. 활성화하려면 DOCKER_CONTENT_TRUST 환경 변수를 1로 설정하면 됩니다. 이렇게 하면 서명이 없는 태그 이미지는 사용할 수 없게 됩니다.

Docker 클라이언트에서 DCT가 활성화되면, 태그가 있는 이미지를 다루는 docker CLI 명령은 콘텐츠 서명이나 명시적인 콘텐츠 해시를 반드시 포함해야 해요. DCT와 함께 동작하는 명령은 다음과 같습니다:

  • push
  • build
  • create
  • pull
  • run

예를 들어 DCT가 활성화된 상태에서 docker pull someimage:latestsomeimage:latest가 서명된 경우에만 성공합니다. 하지만 명시적인 콘텐츠 해시를 사용하는 작업은 해시가 존재하기만 하면 항상 성공해요:

$ docker pull registry.example.com/user/image:1
Error: remote trust data does not exist for registry.example.com/user/image: registry.example.com does not have trust data for registry.example.com/user/image

$ docker pull registry.example.com/user/image@sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a
sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1: Pulling from user/image
ff3a5c916c92: Pull complete
a59a168caba3: Pull complete
Digest: sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1
Status: Downloaded newer image for registry.example.com/user/image@sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1

더 알아보기