Docker Hardened Image 저장소 미러링하기
Docker Hardened Image 저장소 미러링하기
DHI 저장소를 조직 또는 서드파티 레지스트리로 미러링하는 방법과, 웹훅을 통한 동기화 자동화, regctl/regsync로 attestation까지 포함해 복사하는 방법을 알아봐요.
출처: 문서
본문
미러링에는 DHI Select 또는 Enterprise 구독이 필요해요. 구독이 없으면 미러링 없이 dhi.io에서 Docker Hardened Images를 직접 풀할 수 있어요. DHI Select 또는 Enterprise 구독이 있으면 다음을 얻기 위해 조직으로 미러링해야 해요.
- 컴플라이언스 변형(FIPS 지원 또는 STIG 준비 이미지)
- ELS(Extended Lifecycle Support) 변형(애드온 필요)
- 이미지 또는 Helm 차트 커스터마이즈
- 에어갭(air-gapped) 또는 제한된 네트워크 환경
- SLA 기반 보안 업데이트
미러링 방법
이 주제는 Docker Hardened Image(DHI) 저장소의 두 가지 미러링 유형을 다뤄요.
- 조직으로 미러링: DHI 저장소를 Docker Hub의 조직 네임스페이스로 미러링.
- 서드파티 레지스트리로 미러링: Amazon ECR, Google Artifact Registry, 프라이빗 Harbor 인스턴스 같은 다른 컨테이너 레지스트리로 저장소 미러링.
DHI 저장소를 조직으로 미러링
DHI 미러링 권한이 포함된 커스텀 역할을 가진 조직 소유자, 편집자, 멤버는 미러를 생성·조회·관리할 수 있어요. CLI나 Terraform을 사용할 때는 역할 기반 접근 없이도 적절한 권한 범위의 조직 액세스 토큰(OAT)으로 미러링할 수 있어요.
DHI 미러링 권한이 포함된 커스텀 역할을 가진 멤버가 미러를 만들면, Docker가 조직에 dhi-mirroring-admins 팀을 자동 생성·관리하고 해당 멤버를 추가하며, 새 미러에 대한 팀 접근 권한을 부여해요. 이렇게 하면 멤버가 조직 소유자나 편집자 접근 없이도 자신이 만든 미러를 관리할 수 있어요. 조직 소유자나 편집자가 만든 미러는 이 팀을 사용하지 않아요. 이 팀에서 멤버를 제거하면 미러를 보고 관리하는 능력에 영향을 줄 수 있어요.
이미지와 차트 저장소를 Docker Hub의 조직 네임스페이스로 미러링할 수 있어요. 미러링은 저장소를 조직 내에서 사용 가능하게 만들고 환경에 맞게 커스터마이즈할 수 있게 해줘요.
- 이미지 저장소: 미러링을 통해 패키지, OCI 아티팩트(예: 커스텀 인증서나 추가 도구), 환경 변수, 레이블, 기타 구성 설정을 추가해 이미지를 커스터마이즈할 수 있어요. 자세한 내용은 Customize a Docker Hardened Image를 봐요.
- 차트 저장소: 미러링을 통해 차트 내 이미지 참조를 커스터마이즈할 수 있어요. 커스터마이즈된 이미지를 사용하거나 이미지를 서드파티 레지스트리로 미러링했고 차트가 그 커스텀 위치를 참조해야 할 때 특히 유용해요. 자세한 내용은 Customize a Docker Hardened Helm chart를 봐요.
Docker Hub
- Docker Hub로 이동해 로그인해요.
- My Hub를 선택해요.
- 네임스페이스 드롭다운에서 조직을 선택해요.
- Hardened Images > Catalog를 선택해요.
- DHI 저장소를 선택해 상세 정보를 봐요.
- 저장소를 미러링해요.
- 이미지 저장소를 미러링하려면 Use this image > Mirror repository를 선택하고 화면 지침을 따라요. ELS 애드온이 있으면 Enable support for end-of-life versions도 선택할 수 있어요.
- Helm 차트 저장소를 미러링하려면 Get Helm chart를 선택하고 화면 지침을 따라요.
모든 태그가 미러링을 마치는 데 몇 분이 걸릴 수 있어요.
CLI
docker login으로 Docker 자격 증명, Read & Write 권한의 개인 액세스 토큰(PAT), 또는 조직 액세스 토큰(OAT)으로 인증해요. OAT를 사용할 때 사용 가능한 작업은 토큰의 권한 범위에 따라 달라져요.
- 미러링된 저장소를 나열하려면 OAT가 관련 저장소에 대해 읽기(pull) 접근 권한이 있어야 해요. 결과는 OAT가 접근할 수 있는 저장소로 제한돼요.
- 기존 대상 저장소에 미러를 만들려면 OAT가 해당 저장소에 push 접근 권한이 있어야 해요. 아직 존재하지 않는 새 대상 저장소에 미러를 만들려면 OAT가 조직 전체 저장소 접근(예: pull 또는 push가 있는
<org>/*)이 있어야 해요. 향후 저장소 이름에 대한 저장소 범위 접근은 충분하지 않아요. - 미러링을 중지하려면 OAT가 관련 저장소에 push 접근 권한이 있어야 해요.
- 공개 저장소 읽기 전용 접근의 OAT는 미러링된 저장소를 나열하거나 관리할 수 없어요.
docker dhi mirror 명령을 사용해요.
$ docker dhi mirror start --org my-org \
dhi/golang,my-org/dhi-golang \
dhi/nginx,my-org/dhi-nginx \
dhi/prometheus-chart,my-org/dhi-prometheus-chart
의존성과 함께 미러링:
$ docker dhi mirror start --org my-org dhi/golang,my-org/dhi-golang --dependencies
조직의 미러링된 이미지 나열:
$ docker dhi mirror list --org my-org
미러링된 이미지를 이름이나 유형으로 필터링:
$ docker dhi mirror list --org my-org --filter python
$ docker dhi mirror list --org my-org --type image
$ docker dhi mirror list --org my-org --type helm-chart
Terraform
DHI Terraform 공급자를 사용해 DHI 미러를 코드형 인프라(IaC)로 관리할 수 있어요.
미러링하려는 각 저장소마다 dhi_mirror 리소스를 정의해요.
resource "dhi_mirror" "golang" {
source_namespace = "dhi"
source_name = "golang"
destination_name = "dhi-golang"
}
resource "dhi_mirror" "nginx" {
source_namespace = "dhi"
source_name = "nginx"
destination_name = "dhi-nginx"
}
ELS(Extended Lifecycle Support) 변형을 활성화하려면 els 속성을 설정해요.
resource "dhi_mirror" "golang" {
source_namespace = "dhi"
source_name = "golang"
destination_name = "dhi-golang"
els = true
}
terraform apply를 실행해 미러를 만들어요.
리소스 속성의 전체 목록은 Terraform Registry 문서를 봐요.
미러링 후 저장소는 dhi- 접두사로 조직의 저장소 목록에 나타나며 계속 업데이트된 이미지를 받아요. 다른 Docker Hub 저장소처럼 동작하므로 접근과 권한을 관리하고, 웹훅을 구성하고, 다른 표준 Hub 기능을 사용할 수 있어요. 자세한 내용은 Docker Hub repositories를 봐요.
저장소 미러링 중지
미러링을 중지한 후에도 저장소는 남지만 더 이상 업데이트를 받지 않아요. 마지막으로 미러링한 이미지나 차트는 계속 사용할 수 있어요.
[!NOTE]
ELS 버전의 미러링만 중지하려면 미러링된 저장소의 Settings 탭에서 ELS 옵션을 해제하면 돼요.
Docker Hub
- Docker Hub로 이동해 로그인해요.
- My Hub를 선택해요.
- 네임스페이스 드롭다운에서 DHI에 접근 권한이 있는 조직을 선택해요.
- Hardened Images > Manage를 선택해요.
- Mirrored Images 또는 Mirrored Helm charts 탭을 선택해요.
- 미러링을 중지할 저장소의 맨 오른쪽 열에서 메뉴 아이콘을 선택해요.
- Stop mirroring을 선택해요.
CLI
docker login으로 Docker 자격 증명, Read & Write 권한의 개인 액세스 토큰(PAT), 또는 관련 저장소에 push 접근 권한이 있는 조직 액세스 토큰(OAT)으로 인증해요.
docker dhi mirror 명령을 사용해요.
$ docker dhi mirror stop --org my-org dhi-golang
Terraform
미러링을 중지하려면 Terraform 구성에서 dhi_mirror 리소스를 제거하고 terraform apply를 실행해요. 저장소는 조직에 남지만 더 이상 업데이트를 받지 않아요.
DHI 저장소를 서드파티 레지스트리로 미러링
DHI 저장소를 Docker Hub의 조직으로 미러링한 후, 선택적으로 Amazon ECR, Google Artifact Registry, GitHub Container Registry, 프라이빗 Harbor 인스턴스 같은 다른 컨테이너 레지스트리로 미러링할 수 있어요.
Docker CLI, Docker Hub Registry API, 서드파티 레지스트리 도구, CI/CD 자동화 같은 표준 워크플로로 이미지를 미러링할 수 있어요.
하지만 attestation을 포함한 전체 보안 컨텍스트를 보존하려면 관련 OCI 아티팩트도 미러링해야 해요. DHI 저장소는 이미지 레이어를 dhi.io(커스터마이즈된 이미지는 docker.io)에, 서명된 attestation을 별도 레지스트리(registry.scout.docker.com)에 저장해요.
둘 다 복사하려면 SBOM, 취약점 보고서, SLSA provenance 같은 첨부 아티팩트와 함께 이미지를 미러링하는 것을 지원하는 OCI 인식 CLI인 regctl을 사용할 수 있어요. 지속적인 동기화에는 regsync를 사용할 수 있어요.
웹훅으로 동기화 자동화
외부 레지스트리나 시스템을 미러링된 Docker Hardened Images와 동기화된 상태로 유지하고, 업데이트가 발생할 때 알림을 받으려면 Docker Hub의 미러링된 저장소에 웹훅을 구성할 수 있어요. 웹훅은 새 이미지 태그가 푸시되거나 업데이트될 때마다 정의된 URL로 POST 요청을 보내요.
예를 들어 새 태그가 미러링될 때마다 https://ci.example.com/hooks/dhi-sync의 CI/CD 시스템을 호출하는 웹훅을 구성할 수 있어요. 이 웹훅이 트리거한 자동화는 Docker Hub에서 업데이트된 이미지를 풀해 Amazon ECR, Google Artifact Registry, GitHub Container Registry 같은 내부 레지스트리로 푸시할 수 있어요.
다른 일반적인 웹훅 사용 사례:
- 검증 또는 취약점 스캔 워크플로 트리거
- 이미지 서명 또는 프로모션
- 다운스트림 시스템에 알림 전송
웹훅이 실행되면 Docker Hub는 표준 웹훅 페이로드를 보내요. 미러링된 DHI 저장소의 경우 페이로드에 추가 dhi_metadata 객체도 포함돼요. 이 객체는 새로 푸시된 빌드와 동일한 태그의 이전 빌드 사이에 무엇이 변경되었는지(취약점 수정, 패키지 변경, 구성 변경 포함) 설명해요.
[!NOTE]
Docker Hub는 미러링된 DHI 저장소의 푸시에만
dhi_metadata를 추가해요. 다른 저장소의 웹훅은 표준 페이로드를 전달해요.
각 DHI 빌드는 서명된 변경 로그(changelog) attestation을 생성해요. 웹훅 전달 시 Docker Hub는 푸시된 이미지의 변경 로그를 가져와 dhi_metadata로 페이로드에 포함해요.
DHI 변경 로그는 아키텍처별로 생성되므로 dhi_metadata는 아키텍처별 매니페스트 digest를 키로 하는 맵이에요. 멀티 플랫폼 이미지 푸시에는 변경 로그가 있는 각 플랫폼의 항목이 포함돼요. 단일 항목을 가정하지 말고, 관심 있는 플랫폼에 대해 digest 키를 매칭해요.
dhi_metadata 필드
각 플랫폼 항목은 다음 필드를 포함해요.
| 필드 | 유형 | 설명 |
|---|---|---|
schema_version |
integer | dhi_metadata 스키마 버전. |
change_categories |
string 배열 | 이 빌드에서 무엇이 변경되었는지에 대한 높은 수준의 요약. Change categories 참조. |
previous_version |
object | 이 빌드가 비교되는 이전 빌드. tag와 digest 포함. |
changes |
object | 이전 버전과의 상세 diff. 다음 표 참조. |
changes 객체는 다음을 포함해요.
| 필드 | 유형 | 설명 |
|---|---|---|
vulnerabilities_fixed |
배열 | 이 빌드에서 해결된 CVE. 각 항목은 cve_id, severity, package, fixed_in_version 포함. |
packages_updated |
배열 | 버전이 변경된 패키지. 각 항목은 name, type, old_version, new_version 포함. |
packages_added |
배열 | 이 빌드에서 추가된 패키지. 각 항목은 name, type, version 포함. |
packages_removed |
배열 | 이 빌드에서 제거된 패키지. 각 항목은 name, type, version 포함. |
environment_variables_changed |
배열 | 환경 변수 변경. 각 항목은 해당하는 경우 change, key, from_value 또는 to_value 포함. |
labels_changed |
배열 | 환경 변수 변경과 같은 형태의 이미지 레이블 변경. |
configuration_changed |
배열 | 기타 이미지 구성 변경. 예: 엔트리포인트. |
변경 유형에 항목이 없으면 해당 배열은 비어 있지만 존재하며 []로 표시돼요.
변경 카테고리(Change categories)
change_categories는 빌드에 대한 빠르고 기계가 읽을 수 있는 요약을 제공해요.
| 값 | 의미 |
|---|---|
vulnerability_fix |
빌드가 하나 이상의 CVE를 해결함. changes.vulnerabilities_fixed 참조. |
version_upgrade |
하나 이상의 패키지 버전이 변경됨. changes.packages_updated 참조. |
other |
위 카테고리에 해당하지 않는 패키지, 환경 변수, 레이블, 구성 변경이 있는 빌드. |
빌드는 둘 이상의 카테고리를 가질 수 있어요. 예를 들어 CVE를 수정하고 패키지 버전도 올린 빌드는 vulnerability_fix와 version_upgrade를 모두 반환해요. 변경이 전혀 없는 빌드는 빈 배열을 반환해요.
예시: 취약점 수정과 버전 업그레이드
다음 발췌는 미러링된 DHI 저장소의 푸시에 대한 웹훅 페이로드의 dhi_metadata 객체를 보여줘요. 예시는 가독성을 위해 단일 플랫폼과 변경 하위 집합으로 축약되었어요. 실제 페이로드에는 아키텍처마다 하나의 dhi_metadata 항목이 포함돼요.
{
...
"dhi_metadata": {
"sha256:04639747b6d72bcf1d0322f2a5b122ee76d963e31bb4a070891b25f15a5001c5": {
"schema_version": 1,
"change_categories": ["vulnerability_fix", "version_upgrade"],
"previous_version": {
"tag": "2-compat-fips-dev",
"digest": "sha256:1738aa35838f520431c898b85d7cd60da71d8f997965287db4f3be27c1df32a1"
},
"changes": {
"vulnerabilities_fixed": [
{
"cve_id": "CVE-2019-9192",
"severity": "low",
"package": "glibc",
"fixed_in_version": "2.41-12+deb13u4+dhi0"
},
{
"cve_id": "CVE-2018-20796",
"severity": "low",
"package": "glibc",
"fixed_in_version": "2.41-12+deb13u4+dhi0"
}
],
"packages_updated": [
{
"name": "glibc",
"type": "deb",
"old_version": "2.41-12+deb13u4",
"new_version": "2.41-12+deb13u4+dhi0"
},
{
"name": "libc6",
"type": "deb",
"old_version": "2.41-12+deb13u4",
"new_version": "2.41-12+deb13u4+dhi0"
}
],
"packages_added": [],
"packages_removed": [],
"environment_variables_changed": [],
"labels_changed": [
{
"change": "changed",
"key": "com.docker.dhi.chain-id",
"from_value": "sha256:4567092c648d813b8c4c60c7d100fc34df817dd5cb4c7968e9a5c43bafb9e7a5",
"to_value": "sha256:62d4e2090951e812a87fb599db362677f72dee095f85889ea56df63c0999b02a"
}
],
"configuration_changed": []
}
}
}
}
예시: CVE 없는 버전 상승
빌드가 패키지 버전만 올린 경우 change_categories에는 version_upgrade가 포함되고 vulnerabilities_fixed는 비어요.
{
...
"dhi_metadata": {
"sha256:2982980b6bb3cdedafa9377bcc37405c20ed48702deef11faf13ec99d596057d": {
"schema_version": 1,
"change_categories": ["version_upgrade"],
"previous_version": {
"tag": "5-fips-dev",
"digest": "sha256:81355a1301ecc5f78dd87b68a284642d7b6bfbd86f3a37f3932fad7ecf1141e6"
},
"changes": {
"vulnerabilities_fixed": [],
"packages_updated": [
{
"name": "sqlite3",
"type": "deb",
"old_version": "3.46.1-7+deb13u2+dhi0",
"new_version": "3.46.1-7+deb13u2+dhi1"
}
],
"packages_added": [],
"packages_removed": [],
"environment_variables_changed": [],
"labels_changed": [],
"configuration_changed": []
}
}
}
}
regctl로 미러링 예시
다음 예시는 regctl을 사용해 Docker Hardened Image의 특정 태그를 관련 attestation과 함께 Docker Hub에서 다른 레지스트리로 미러링하는 방법을 보여줘요. 먼저 regctl을 설치해야 해요.
예시는 이전 섹션에서 설명한 대로 DHI 저장소를 Docker Hub의 조직 네임스페이스로 미러링했다고 가정해요. 미러링되지 않은 이미지에도 SRC_ATT_REPO와 SRC_REPO 변수를 그에 맞게 업데이트해 동일한 단계를 적용할 수 있어요.
-
특정 환경에 대한 환경 변수를 설정해요. 플레이스홀더를 실제 값으로 바꿔요.
이 예시에서는 조직 액세스 토큰(OAT)을 사용해 Docker 조직으로 인증해요. OAT는 미러링하려는 모든 DHI 저장소에 대해 최소한 풀(pull) 접근 권한이 있어야 해요. 토큰 범위 내의 저장소만 접근 가능해요. 또는
read only접근 권한이 있는 개인 액세스 토큰(PAT)으로 Docker Hub 사용자로 인증할 수 있어요.[!WARNING]
다음 예시는 데모 목적으로 자격 증명을 명령줄에 직접 내보내요. 이는 셸 히스토리와 프로세스 목록에 민감한 토큰을 노출해요. 프로덕션 환경에서는 제한된 권한의 파일에서 읽기, 런타임에 로드되는 환경 파일, 시크릿 관리 도구 같은 안전한 방법을 사용해요.
$ export DOCKER_ORG="YOUR_DOCKER_ORG" $ export DOCKER_OAT="YOUR_DOCKER_OAT" $ export DEST_REG="registry.example.com" $ export DEST_REPO="mirror/dhi-python" $ export DEST_REG_USERNAME="YOUR_DESTINATION_REGISTRY_USERNAME" $ export DEST_REG_TOKEN="YOUR_DESTINATION_REGISTRY_TOKEN" $ export SRC_REPO="docker.io/${DOCKER_ORG}/dhi-python" $ export SRC_ATT_REPO="registry.scout.docker.com/${DOCKER_ORG}/dhi-python" $ export TAG="3.13-alpine3.21" -
regctl로 Docker Hub, attestation이 있는 Scout 레지스트리, 대상 레지스트리에 로그인해요.$ echo $DOCKER_OAT | regctl registry login -u "$DOCKER_ORG" --pass-stdin docker.io $ echo $DOCKER_OAT | regctl registry login -u "$DOCKER_ORG" --pass-stdin registry.scout.docker.com $ echo $DEST_REG_TOKEN | regctl registry login -u "$DEST_REG_USERNAME" --pass-stdin "$DEST_REG" -
--referrers와 referrer 엔드포인트를 사용해 이미지와 attestation을 미러링해요.$ regctl image copy \ "${SRC_REPO}:${TAG}" \ "${DEST_REG}/${DEST_REPO}:${TAG}" \ --referrers \ --referrers-src "${SRC_ATT_REPO}" \ --referrers-tgt "${DEST_REG}/${DEST_REPO}" \ --force-recursive -
아티팩트가 보존되었는지 확인해요.
먼저 특정 태그와 플랫폼의 digest를 가져와요. 예를 들어
linux/amd64.DIGEST="$(regctl manifest head "${DEST_REG}/${DEST_REPO}:${TAG}" --platform linux/amd64)"첨부된 아티팩트(SBOM, provenance, VEX, 취약점 보고서)를 나열해요.
$ regctl artifact list "${DEST_REG}/${DEST_REPO}@${DIGEST}"또는
docker scout로 첨부된 아티팩트를 나열해요.$ docker scout attest list "registry://${DEST_REG}/${DEST_REPO}@${DIGEST}"
regsync로 지속 미러링 예시
regsync는 조직의 미러링된 DHI 저장소에서 풀하고 attestation을 포함해 외부 레지스트리로 푸시하는 것을 자동화해요. YAML 구성 파일을 읽고 태그를 필터링할 수 있어요.
다음 예시는 Node 24와 Python 3.12 Debian 13 변형을 동기화하고 Alpine과 Debian 12를 제외하는 regsync.yaml 파일을 사용해요.
version: 1
# Optional: inline creds if not relying on prior CLI logins
# creds:
# - registry: docker.io
# user: <your-docker-org>
# pass: "{{file \"/run/secrets/docker_oat\"}}"
# - registry: registry.scout.docker.com
# user: <your-docker-org>
# pass: "{{file \"/run/secrets/docker_oat\"}}"
# - registry: registry.example.com
# user: <service-user>
# pass: "{{file \"/run/secrets/dest_token\"}}"
sync:
- source: docker.io/<your-org>/dhi-node
target: registry.example.com/mirror/dhi-node
type: repository
fastCopy: true
referrers: true
referrerSource: registry.scout.docker.com/<your-org>/dhi-node
referrerTarget: registry.example.com/mirror/dhi-node
tags:
allow: [ "24.*" ]
deny: [ ".*alpine.*", ".*debian12.*" ]
- source: docker.io/<your-org>/dhi-python
target: registry.example.com/mirror/dhi-python
type: repository
fastCopy: true
referrers: true
referrerSource: registry.scout.docker.com/<your-org>/dhi-python
referrerTarget: registry.example.com/mirror/dhi-python
tags:
allow: [ "3.12.*" ]
deny: [ ".*alpine.*", ".*debian12.*" ]
구성 파일로 드라이 런을 하려면 다음 명령을 실행할 수 있어요. 먼저 regsync를 설치해야 해요.
$ regsync check -c regsync.yaml
구성 파일로 동기화를 실행하려면:
$ regsync once -c regsync.yaml
다음 단계(What next)
미러링 후 Pull a DHI를 참조해 미러링된 이미지를 풀·사용하는 방법을 알아봐요.