자체 빌드 이미지 레포명을 security-images에서 hardened-containers로 정정한다
실제 레포명은 hardened-containers인데 이관 커밋 이후 문서·워크플로·차트 주석에 잘못된 이름 security-images가 남아 있었다. 텍스트 참조 전체를 정정하고 doc/migrations/의 이관 핸드오프 문서도 파일명까지 리네임했다. env var/secret 이름(SECURITY_IMAGES_REPO, SECURITY_IMAGES_DISPATCH_TOKEN)은 GitHub Secret 재등록이 필요한 별도 운영 작업이라 이번 텍스트 정정 범위에서 제외했다. 덧붙여 scripts/build/patch-catalog-tag.py의 docstring이 삭제된 build-image.yml을 호출자로 여전히 가리키고 있던 것도 실제 호출 경로(check-rebuild-needed.py / apply-published-tags.py → catalog-tag-update.yml)로 고쳤다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,9 +9,9 @@
|
||||
" - 예외는 '위험을 수용한다'는 기록이다. 숫자를 지우는 수단으로 쓰면 목표 자체가 무의미해진다.",
|
||||
"",
|
||||
"예외를 늘리기 전에 먼저 검토할 것: 상위 태그로 교체 / 베이스 OS 교체 /",
|
||||
"그래도 안 되면 별도 레포 security-images 에서 자체 빌드. 예외는 마지막 수단이다.",
|
||||
"그래도 안 되면 별도 레포 hardened-containers 에서 자체 빌드. 예외는 마지막 수단이다.",
|
||||
"자체 빌드 이미지(docker.io/paasup/*)는 그 레포도 같은 이미지를 스캔하므로,",
|
||||
"그 이미지에 대한 예외는 이 파일과 security-images 레포의 cve-exceptions.json",
|
||||
"그 이미지에 대한 예외는 이 파일과 hardened-containers 레포의 cve-exceptions.json",
|
||||
"양쪽에 각각 등록해야 두 게이트 모두 통과한다."
|
||||
],
|
||||
"exceptions": [
|
||||
|
||||
@@ -35,7 +35,7 @@ dev 클러스터 `apisix` 네임스페이스에 이미 `data-apisix-etcd-0` PVC
|
||||
1. 패치가 멈춘 이미지는 시간이 지날수록 미수정 CVE 가 구조적으로 누적된다 —
|
||||
"CRITICAL/HIGH 0건" 을 유지 가능한 상태로 지속할 수 없다.
|
||||
2. 버전 고정 없는 `latest`-only 배포는 **롤링 태그 금지** 규칙과 애초에 양립하지 않는다
|
||||
(자체 빌드 이미지에 적용되는 이 규칙은 `security-images` 레포의 `docs/image-authoring.md`
|
||||
(자체 빌드 이미지에 적용되는 이 규칙은 `hardened-containers` 레포의 `docs/image-authoring.md`
|
||||
가 갖는다).
|
||||
|
||||
`groundhog2k/etcd` 는 업스트림 etcd 프로젝트의 원본 이미지(`quay.io/coreos/etcd`)를 그대로
|
||||
|
||||
@@ -10,11 +10,11 @@ CVE 건수·커버리지·패키지 버전 같은 수치는 게이트가 매 실
|
||||
|---|---|---|---|
|
||||
| [0003](0003-etcd-chart-selection.md) | etcd 는 `groundhog2k/etcd` 단일 차트 · replicas=1 로 배포한다 | 2026-07-31 | 확정 |
|
||||
|
||||
**이미지 자체 빌드 ADR(0001·0002·0004)은 별도 레포 `security-images` 로 이관됐다** —
|
||||
**이미지 자체 빌드 ADR(0001·0002·0004)은 별도 레포 `hardened-containers` 로 이관됐다** —
|
||||
그 이미지들의 빌드 정의와 함께 그 레포의 `docs/decisions/` 에 있다. 카탈로그가 무엇을
|
||||
배포하는가(차트)와 그 이미지를 어떻게 만드는가(자체 빌드)가 다른 레포에 놓이며 근거도
|
||||
함께 옮긴 것이다. 이관 배경은
|
||||
[doc/migrations/self-build-images-to-security-images.md](../migrations/self-build-images-to-security-images.md).
|
||||
[doc/migrations/self-build-images-to-hardened-containers.md](../migrations/self-build-images-to-hardened-containers.md).
|
||||
번호가 0003 하나만 남아 이어지지 않아 보이지만, 다른 세 번호가 예약돼 있던 것일 뿐이고
|
||||
새 ADR 은 0005 부터 잇는다.
|
||||
|
||||
|
||||
+14
-14
@@ -1,7 +1,7 @@
|
||||
# 자체 빌드 하드닝 이미지 → `security-images` 레포 이관
|
||||
# 자체 빌드 하드닝 이미지 → `hardened-containers` 레포 이관
|
||||
|
||||
이 문서는 자체 빌드 이미지 프레임워크·이미지 정의를 별도 public 레포
|
||||
`security-images` 로 분리한 작업의 기록이다. 무엇이 어디로 갔고, 두 레포가 지금
|
||||
`hardened-containers` 로 분리한 작업의 기록이다. 무엇이 어디로 갔고, 두 레포가 지금
|
||||
무엇으로 계약하는지, 아직 안 된 것이 무엇인지를 담는다.
|
||||
|
||||
## 배경
|
||||
@@ -11,7 +11,7 @@
|
||||
- **차트 카탈로그 축** — "무엇을 배포 중인가" (`manifests/helm/`, warn-only 게이트)
|
||||
- **자체 빌드 축** — "그 이미지를 어떻게 만드는가" (`images/`, 강제 게이트)
|
||||
|
||||
`security-images` 가 public 이 될 예정이라 세 조건이 붙었다: **외부 의존성 없이
|
||||
`hardened-containers` 가 public 이 될 예정이라 세 조건이 붙었다: **외부 의존성 없이
|
||||
단독 동작**(클론 + `docker` + `trivy` 만으로 빌드·게이트 완결), **슬림 게이트**(이미지
|
||||
판정에 불필요한 차트 축 로직 제거), **산업화**(내부 호스트명·개인 계정·이슈/PR 번호
|
||||
제거). 이 조건들이 원래 `.claude/image-authoring.md` 가 적어 두었던 "레포 분리 후
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
## 무엇이 옮겨갔는가
|
||||
|
||||
| 카탈로그(이 레포)에 있던 것 | `security-images` 로 |
|
||||
| 카탈로그(이 레포)에 있던 것 | `hardened-containers` 로 |
|
||||
|---|---|
|
||||
| `images/<image>/` 8개 | 그대로(경로 보존) |
|
||||
| `scripts/build/build-hardened-image.sh` | 그대로(경로 보존) |
|
||||
@@ -38,15 +38,15 @@
|
||||
| 경로 | 역할 |
|
||||
|---|---|
|
||||
| `catalog/image-map/<image>.env` | 자체 빌드 이미지 → 어느 차트의 어느 필드(`CHART_DIRS`·`TAG_STYLE`·`TAG_BLOCK`). 옛 `images/<image>/catalog.env` 의 카탈로그 레이아웃 정보만 뗀 것 |
|
||||
| `scripts/build/check-rebuild-needed.py` | 드리프트 탐지(A 파트만) — 배포 중인 이미지가 새 CVE 로 규정을 벗어났는가. 핀 판단(B 파트)은 제거했다 — `security-images` 의 `suggest-go-upgrades.py` 가 갖는다 |
|
||||
| `scripts/build/apply-published-tags.py` | `security-images` 의 `published.json` 을 카탈로그 values 에 반영 |
|
||||
| `scripts/build/check-rebuild-needed.py` | 드리프트 탐지(A 파트만) — 배포 중인 이미지가 새 CVE 로 규정을 벗어났는가. 핀 판단(B 파트)은 제거했다 — `hardened-containers` 의 `suggest-go-upgrades.py` 가 갖는다 |
|
||||
| `scripts/build/apply-published-tags.py` | `hardened-containers` 의 `published.json` 을 카탈로그 values 에 반영 |
|
||||
| `scripts/build/patch-catalog-tag.py` | 변경 없음(원래도 카탈로그 파일만 다루는 순수 도구였다) |
|
||||
| `.github/workflows/self-build-drift-check.yml` | 주간, 배포 중인 이미지 스캔 → 재빌드 필요하면 `security-images` 의 `build-image.yml` 을 `workflow_dispatch` 로 트리거만 |
|
||||
| `.github/workflows/self-build-drift-check.yml` | 주간, 배포 중인 이미지 스캔 → 재빌드 필요하면 `hardened-containers` 의 `build-image.yml` 을 `workflow_dispatch` 로 트리거만 |
|
||||
| `.github/workflows/catalog-tag-update.yml` | 매일, `published.json` 조회 → 카탈로그 values 패치 → 브랜치 push |
|
||||
|
||||
## 계약 — `published.json` 하나뿐
|
||||
|
||||
`security-images` 는 **이 카탈로그를 모른다.** 게이트 PASS + 레지스트리 push 가 실제로
|
||||
`hardened-containers` 는 **이 카탈로그를 모른다.** 게이트 PASS + 레지스트리 push 가 실제로
|
||||
일어났을 때만 자기 레포의 `published.json` 을 갱신한다:
|
||||
|
||||
```json
|
||||
@@ -55,20 +55,20 @@
|
||||
|
||||
`catalog-tag-update.yml` 이 이 파일을 **public raw URL** 로 읽어간다 — 인증도, 그
|
||||
레포의 시크릿도 필요 없다. 의존 방향은 **카탈로그 → 이미지 단방향**이다.
|
||||
`repository_dispatch` 같은 역방향 알림은 쓰지 않는다 — 그러려면 `security-images`
|
||||
`repository_dispatch` 같은 역방향 알림은 쓰지 않는다 — 그러려면 `hardened-containers`
|
||||
시크릿에 이 카탈로그 쓰기 권한 PAT 을 둬야 하는데, 그건 그 레포의 "외부 의존성 없이
|
||||
단독 동작" 원칙과 충돌한다.
|
||||
|
||||
## 원래 계획과 달라진 점
|
||||
|
||||
`.claude/image-authoring.md`(삭제됨, 원문은 `security-images` 의 git 히스토리에)의
|
||||
`.claude/image-authoring.md`(삭제됨, 원문은 `hardened-containers` 의 git 히스토리에)의
|
||||
"레포 분리 후 무엇이 끊기는가" 는 게이트를 **composite action** 으로 공개해 두 축이
|
||||
`uses:` 로 공유하는 안을 제시했다. 실제로는 그렇게 하지 않았다 — 두 가지 이유다.
|
||||
|
||||
1. **`suggest-go-upgrades.py` 가 `cve-gate.py` 를 `importlib` 로 모듈 로드했다.**
|
||||
composite action 은 워크플로 스텝만 공유하고 파이썬 모듈 임포트를 공유하지 못한다.
|
||||
`effective_severity` 함수를 게이트의 "정식 소유"로 옮기고(이 레포의 `cve-gate.py` ·
|
||||
`security-images` 의 `image-gate.py` 양쪽에 독립적으로), 두 게이트가 완전히 갈라지는
|
||||
`hardened-containers` 의 `image-gate.py` 양쪽에 독립적으로), 두 게이트가 완전히 갈라지는
|
||||
쪽을 택했다.
|
||||
2. **게이트 규칙 분기를 허용하기로 했다.** 차트 축은 warn-only, 자체 빌드 축은 강제라
|
||||
강제력부터 다르다. 공유 action 대신 각자 자기 게이트를 소유하고, `max(벤더, NVD)`
|
||||
@@ -82,13 +82,13 @@
|
||||
## 아직 안 된 것
|
||||
|
||||
- **`SECURITY_IMAGES_DISPATCH_TOKEN` 시크릿 미등록.** `self-build-drift-check.yml` 이
|
||||
`security-images` 의 `build-image.yml` 을 `workflow_dispatch` 로 부르려면
|
||||
`hardened-containers` 의 `build-image.yml` 을 `workflow_dispatch` 로 부르려면
|
||||
그 레포에 `workflow_dispatch` 권한이 있는 PAT 이 필요하다. 등록 전까지 트리거 스텝은
|
||||
명시적으로 실패한다.
|
||||
- **`security-images` 레포 자체가 아직 GitHub 에 없다.** 로컬 레포만 있는 상태에서
|
||||
- **`hardened-containers` 레포 자체가 아직 GitHub 에 없다.** 로컬 레포만 있는 상태에서
|
||||
이 이관을 진행했다 — GitHub 레포 생성·push, `DOCKERHUB_USER`/`DOCKERHUB_TOKEN` 시크릿
|
||||
등록이 선행돼야 위 두 워크플로가 실제로 동작한다.
|
||||
- **`published.json` 의 `digest` 필드 대부분 공란.** `security-images` 가 아직 실제로
|
||||
- **`published.json` 의 `digest` 필드 대부분 공란.** `hardened-containers` 가 아직 실제로
|
||||
이미지를 재빌드·push 하지 않아서다. 다음 빌드가 채운다 — MEMORY.md 의 "카탈로그 태그가
|
||||
클러스터보다 앞서 있다" 항목이 이 필드를 쓸 계획이다.
|
||||
- **`manifests/applicationset/**` 는 `catalog/image-map/` 대상이 아니다.** 옛
|
||||
@@ -11,9 +11,9 @@ GitHub Actions([.github/workflows/sbom.yml](../.github/workflows/sbom.yml))로
|
||||
| | 다룬다 — **차트 카탈로그 축** | 다루지 않는다 — **자체 빌드 축** |
|
||||
|---|---|---|
|
||||
| 질문 | 우리가 배포하는 이미지에 무엇이 있는가 | 그 이미지를 어떻게 만드는가 |
|
||||
| 실행 위치 | 이 레포 | 별도 레포 `security-images` |
|
||||
| 워크플로 | `sbom.yml` · `cve-edge-post.yml` | `security-images` 의 `build-image.yml`(이 레포에서는 `self-build-drift-check.yml` 이 필요할 때 그것을 부른다) |
|
||||
| 단일 출처 | **이 문서** | `security-images` 레포의 `docs/image-authoring.md` |
|
||||
| 실행 위치 | 이 레포 | 별도 레포 `hardened-containers` |
|
||||
| 워크플로 | `sbom.yml` · `cve-edge-post.yml` | `hardened-containers` 의 `build-image.yml`(이 레포에서는 `self-build-drift-check.yml` 이 필요할 때 그것을 부른다) |
|
||||
| 단일 출처 | **이 문서** | `hardened-containers` 레포의 `docs/image-authoring.md` |
|
||||
|
||||
**카탈로그 values 가 자체 빌드 이미지(`docker.io/paasup/*`)를 가리키므로 그 이미지도 이 문서의
|
||||
스캔 대상이다** — 축이 갈린 것은 "누가 만들고 결정하는가" 이고 "누가 스캔되는가" 가 아니다.
|
||||
@@ -138,7 +138,7 @@ docker buildx build --platform linux/amd64 \
|
||||
> 적용하지 않는다.** 같은 스캔 데이터에서 다른 숫자가 나올 수 있다.
|
||||
|
||||
**PR 을 실제로 막는 게이트는 이 축에 없다** — `sbom.yml` 은 warn-only 다. 강제 게이트는
|
||||
자체 빌드 축(`security-images` 레포의 `images/**` PR)에만 있다.
|
||||
자체 빌드 축(`hardened-containers` 레포의 `images/**` PR)에만 있다.
|
||||
|
||||
> **`manifests/applicationset/**` 를 스캔하지 않는 것은 의도다.** 그 아래 `dip-values.yaml` 은
|
||||
> dip-console 이 배포 values 를 만들 때 쓰는 **참조 파일**이고, 같은 이미지를 `manifests/helm/`
|
||||
@@ -195,7 +195,7 @@ Repo Secret `CVE_API_KEY`).
|
||||
|
||||
**`sbom.yml` 에서는 아직 `--warn-only` 다** — 게이트가 실패해도 워크플로/PR 을 막지 않는다. 카탈로그
|
||||
차트 전체가 아직 이 게이트로 트리아지된 적이 없어, 강제 전환 전에 먼저 전체 스캔 1회로 현황을
|
||||
파악해야 한다(`MEMORY.md`). 자체 빌드 축(`security-images` 레포의 `build-image.yml`)의
|
||||
파악해야 한다(`MEMORY.md`). 자체 빌드 축(`hardened-containers` 레포의 `build-image.yml`)의
|
||||
게이트는 이미 강제다 — 단, 그 게이트는 별도 레포가 소유한다.
|
||||
|
||||
> **커버리지 자가진단(`CoverageProbe`).** `scan-sbom.sh` 는 os-pkgs findings 가 0건인
|
||||
@@ -208,11 +208,11 @@ Repo Secret `CVE_API_KEY`).
|
||||
## 자체 빌드 축은 이 문서가 다루지 않는다
|
||||
|
||||
게이트가 상위 태그 교체·베이스 OS 교체로 해소되지 않는 차단 CVE 를 찾으면 자체 빌드로 간다.
|
||||
그 축은 **별도 레포 `security-images`** 가 갖는다 — 빌드·검증·게이트·push 전부 그 레포
|
||||
그 축은 **별도 레포 `hardened-containers`** 가 갖는다 — 빌드·검증·게이트·push 전부 그 레포
|
||||
안에서 이루어지고, 단일 출처는 그 레포의 `docs/image-authoring.md` 다. 이 카탈로그에는
|
||||
"어느 차트가 그 이미지를 가리키는가"(`catalog/image-map/`)와 드리프트 탐지
|
||||
(`scripts/build/check-rebuild-needed.py`, 주간 `self-build-drift-check.yml`)만 남아 있다 —
|
||||
이관 배경은 [doc/migrations/](migrations/self-build-images-to-security-images.md).
|
||||
이관 배경은 [doc/migrations/](migrations/self-build-images-to-hardened-containers.md).
|
||||
|
||||
이 문서가 알아야 할 것은 하나뿐이다: **자체 빌드 이미지도 카탈로그가 가리키는 한 위 스캔·게이트
|
||||
대상이다.** 실제로 `docker.io/paasup/*` 가 게이트 리포트에 등장한다.
|
||||
@@ -224,7 +224,7 @@ Repo Secret `CVE_API_KEY`).
|
||||
| 종류 | 이름 | 용도 |
|
||||
|------|------|------|
|
||||
| **Variable** | `SBOM_PIPELINE_IMAGE` | 실행 이미지 태그 (예: `docker.io/paasup/sbom-pipeline:20260820`) |
|
||||
| Secret | `DOCKERHUB_USER` / `DOCKERHUB_TOKEN` | **docker.io + docker.getcollate.io rate limit 회피**. getcollate(openmetadata)는 Docker Hub 프록시라 익명 pull 시 rate limit(TOOMANYREQUESTS)에 걸림 → Docker Hub 자격증명으로 인증. `security-images` 레포도 자체 push 용으로 별도 등록된 같은 이름의 시크릿을 쓴다(이 레포와는 무관하게 그 레포에 따로 등록) |
|
||||
| Secret | `DOCKERHUB_USER` / `DOCKERHUB_TOKEN` | **docker.io + docker.getcollate.io rate limit 회피**. getcollate(openmetadata)는 Docker Hub 프록시라 익명 pull 시 rate limit(TOOMANYREQUESTS)에 걸림 → Docker Hub 자격증명으로 인증. `hardened-containers` 레포도 자체 push 용으로 별도 등록된 같은 이름의 시크릿을 쓴다(이 레포와는 무관하게 그 레포에 따로 등록) |
|
||||
| Secret | `NGC_API_KEY` | **nvcr.io 인증**(NVIDIA nemo/nim — 없으면 pull 불가) |
|
||||
| Secret | `CVE_API_KEY` | `cve-edge-post.yml` 의 외부 엔드포인트 인증 |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user