자체 빌드 드리프트 스캔·재빌드 트리거를 제거하고 CI 파이프라인을 정리한다

hardened-containers가 이미 rescan.yml로 매일 자율 재스캔·재빌드하고, sbom.yml이
custom-values.yaml 기준으로 자체 빌드 이미지를 다른 카탈로그 이미지와 동일하게
스캔하고 있어 self-build-drift-check.yml의 트리거·전용 스캔이 순수 중복이었다
(SECURITY_IMAGES_DISPATCH_TOKEN도 등록된 적 없어 트리거 스텝은 항상 실패하던
죽은 코드). check-rebuild-needed.py가 더하던 fixable/no-fix 구분도 cve-gate.py
리포트에 이미 있어 흡수할 필요 없이 삭제했다. 근거는 ADR 0005.

곁들여 CI 위생 문제(trivy DB 캐시 없음, concurrency 없음, catalog-tag-update.yml의
브랜치 누적)를 함께 고치고, hardened-containers의 docs/image-authoring.md가
docs/image-authoring/ 로 분할된 것을 반영해 관련 링크를 정정했다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
wbsong111
2026-08-26 09:48:48 +09:00
parent 9e5b6633db
commit f5b8e4b91f
20 changed files with 339 additions and 452 deletions
+155
View File
@@ -0,0 +1,155 @@
# CI 파이프라인 아키텍처
이 문서는 `.github/workflows/`에 있는 3개 워크플로가 **무엇을 트리거로 도는지**, **잡 흐름이
어떻게 이어지는지**를 한눈에 보여준다. 각 워크플로 내부 메커니즘(스캔 방식, 게이트 판정
로직 등)의 단일 출처는 별도 문서다 — 여기서 복제하지 않는다. 스타일은 별도 레포
`hardened-containers``docs/architecture.md`를 참고했다.
## 1. 두 축, 세 워크플로
카탈로그는 "무엇을 배포 중인가"(차트 카탈로그 축)와 "그 이미지를 어떻게 만드는가"(자체
빌드 축)를 분리해서 다룬다 — 축·소유 레포·게이트 강제력 비교는
[CLAUDE.md](../CLAUDE.md)의 "CVE/SBOM 게이트 작업" 표 참고. 이 레포에 있는 워크플로 3개는
그 두 축 위에 이렇게 나뉜다.
| 워크플로 | 축 | 트리거 | 게이트 |
|---|---|---|---|
| `sbom.yml` | 차트 카탈로그(자체 빌드 이미지 포함) | `pull_request`(`manifests/helm/**`) · `workflow_dispatch` · 매주 일 18:00 UTC | warn-only |
| `cve-edge-post.yml` | 차트 카탈로그 | `workflow_dispatch`만 (스케줄 주석 처리됨) | 없음 — 원시 집계 POST |
| `catalog-tag-update.yml` | 자체 빌드 | `workflow_dispatch` · 매일 03:00 UTC | 없음 — 반영만 |
**PR 이벤트에 반응하는 워크플로는 `sbom.yml` 하나뿐이다.** 나머지 두 개는 사람이 Actions
탭에서 수동 실행하거나 스케줄로만 돈다.
> 자체 빌드 이미지의 CVE 드리프트 스캔·재빌드 트리거를 위한 전용 워크플로
> (`self-build-drift-check.yml`)는 없다 — `sbom.yml`이 자체 빌드 이미지도 다른 카탈로그
> 이미지와 동일하게 스캔·보고하고, 재빌드는 전적으로 `hardened-containers`의 `rescan.yml`이
> 자율 수행한다. 근거는 ADR [0005](decisions/0005-self-build-drift-check-removed.md).
## 2. 트리거 조건 → Job 흐름
```mermaid
flowchart TD
PR["pull_request<br/>manifests/helm/** 변경"]
WD1["workflow_dispatch<br/>chart, limit"]
SCH1["schedule<br/>매주 일 18:00 UTC"]
subgraph SBOM["sbom.yml — 차트 카탈로그 SBOM/CVE 스캔 (자체 빌드 이미지 포함)"]
S1[이미지 인벤토리 추출]
S2["PR: diff 증분만 대상<br/>스케줄·수동: 전체 또는 지정 차트"]
S3[SBOM 생성 CycloneDX]
S4[취약점 스캔]
S5["CVE 게이트 판정<br/>warn-only — 실패해도 PR 안 막음"]
S6[아티팩트 업로드 + Job Summary]
S1 --> S2 --> S3 --> S4 --> S5 --> S6
end
PR --> S1
WD1 --> S1
SCH1 --> S1
WD2["workflow_dispatch<br/>chart, limit<br/>(스케줄 없음 — 주석 처리)"]
subgraph EDGE["cve-edge-post.yml — 외부 집계 전송"]
V1[이미지 인벤토리 추출]
V2[SBOM 생성 + 취약점 스캔]
V3[CVE JSON 집계]
V4["POST edge.gke.paasup.io<br/>승인 예외·실효 등급 미적용"]
V1 --> V2 --> V3 --> V4
end
WD2 --> V1
subgraph HC["hardened-containers — 다른 레포, 독립 루프 (이 레포는 트리거하지 않는다)"]
HR1["rescan.yml<br/>매일 자율 재스캔"]
HR2{드리프트 있음?}
HB1[build-image.yml<br/>빌드→검증→SBOM→스캔→게이트]
HB2["PASS → 레지스트리 push + attest<br/>+ published.json 갱신"]
HR1 --> HR2
HR2 -->|예| HB1
HB1 --> HB2
end
WD4["workflow_dispatch"]
SCH4["schedule<br/>매일 03:00 UTC"]
subgraph SYNC["catalog-tag-update.yml — 발행 태그 카탈로그 반영"]
C1[published.json 조회]
C2["반영 대상 계산<br/>apply-published-tags.py"]
C3{변경 있음?}
C4["고정 브랜치로 push<br/>+ compare 링크 Job Summary"]
C5[변경 없음 — 종료]
C1 --> C2 --> C3
C3 -->|예| C4
C3 -->|아니오| C5
end
WD4 --> C1
SCH4 --> C1
HB2 -.->|"public raw URL 폴링<br/>인증 없음, pull 방식"| C1
C4 -.->|"CI는 GITHUB_TOKEN으로 PR 생성 불가<br/>(조직 정책, 실측 확정)"| HumanPR["사람이 compare 링크로<br/>PR 직접 오픈"]
```
세 갈래를 눈여겨볼 것:
1. **`sbom.yml`만 PR에 반응한다.** `cve-edge-post.yml`·`catalog-tag-update.yml`은 스케줄·수동뿐이다.
2. **레포 경계를 넘는 방향은 하나뿐이다 — pull.** `catalog-tag-update.yml`
`published.json`을 폴링해서 가져올 뿐, 자체 빌드가 언제 도는지는 이 레포가 전혀
모른다 — `hardened-containers``rescan.yml`이 전적으로 자율 결정하고, 이 레포는
어느 쪽으로도 그 레포를 트리거하지 않는다.
3. **끝은 항상 사람이다.** `catalog-tag-update.yml`이 브랜치까지는 push하지만 PR은 열지
않는다 — CI가 `GITHUB_TOKEN`으로 PR을 못 여는 게 조직 정책으로 실측 확정된 제약이라서다
(완화 방법은 [MEMORY.md](../MEMORY.md), [.claude/skills/cve-remediation/SKILL.md](../.claude/skills/cve-remediation/SKILL.md) 참고).
## 3. 워크플로별 상세
### `sbom.yml` — 차트 카탈로그 SBOM/CVE 스캔 (warn-only)
메커니즘 상세(스캔 대상 결정, `cve-gate.py` 판정 로직, 아티팩트 구조, trivy DB 캐싱·
concurrency)의 단일 출처는 [doc/sbom-pipeline.md](sbom-pipeline.md)다. 여기서는 트리거·잡
경계만 짚는다.
- PR에서는 `git diff`로 변경된 차트만 골라 스캔 범위를 줄인다(전체 스캔은 무겁다).
- 스케줄·수동 실행은 전체(또는 `chart` 입력으로 지정한 하나)를 스캔한다.
- **자체 빌드 이미지(`docker.io/paasup/*`)도 같은 대상이다** — `extract-helm-images.sh`
`custom-values.yaml`을 적용해 렌더하므로 별도 취급 없이 그대로 스캔된다. 자체 빌드
이미지 CVE 드리프트를 따로 보는 워크플로는 없다 — 이 리포트가 유일한 경로다.
- 실행 컨테이너(`vars.SBOM_PIPELINE_IMAGE`)에 helm/trivy/python3/bash/git이 없으면
preflight 스텝에서 즉시 실패한다.
- 게이트가 실패해도(`--warn-only`) 워크플로 자체는 성공으로 끝난다 — Job Summary와
아티팩트로만 결과를 남긴다.
### `cve-edge-post.yml` — 외부 집계 전송
- `sbom.yml`과 이미지 인벤토리·SBOM·스캔 스텝을 그대로 재사용하지만, 게이트 판정
(`cve-gate.py`)을 부르지 않는다 — 승인 예외·실효 등급(`max(벤더,NVD)`)이 적용 안 된
**원시** 집계라는 뜻이다. `sbom.yml`의 결과와 CRITICAL 건수가 다르게 보일 수 있다.
- 결과는 `(catalog, version, image)` 조합 단위 JSON으로 만들어져
`https://edge.gke.paasup.io/api/v1/cve-scans`로 POST된다(`CVE_API_KEY` 헤더 인증,
TLS 검증은 `--insecure`로 스킵).
- 스케줄이 주석 처리돼 있어 현재는 **수동 실행만** 된다.
### `catalog-tag-update.yml` — 발행 태그 카탈로그 반영
- `hardened-containers`는 이 카탈로그의 존재를 모른다 — 게이트 PASS + push가 실제로
일어났을 때만 자기 레포의 `published.json`을 갱신해둘 뿐이다. 이 워크플로가 그 파일을
**public raw URL로 폴링**해서 가져온다(인증 불필요, 상세 계약은
[doc/migrations/self-build-images-to-hardened-containers.md](migrations/self-build-images-to-hardened-containers.md)).
역방향 알림(`repository_dispatch` 등)은 의도적으로 안 쓴다 — 그러려면
`hardened-containers` 쪽에 이 카탈로그 쓰기 권한 PAT을 둬야 하는데, 그 레포의 "외부
의존성 없이 단독 동작" 원칙과 충돌하기 때문이다.
- `scripts/build/apply-published-tags.py``gate == "pass"`인 이미지만 골라
`custom-values.yaml`/`dip-values.yaml`을 패치한다(`patch-catalog-tag.py`를 내부에서
호출) — 실패한 빌드의 태그가 반영되는 일은 없다.
- 변경이 있으면 고정 브랜치(`build/catalog-tag-update`)로 force push까지만 하고
멈춘다 — 매 실행 새 브랜치를 만들지 않으므로 방치된 동일 내용 브랜치가 쌓이지 않는다.
**PR은 자동으로 열리지 않는다** — Job Summary의 compare 링크를 보고 사람이 직접 연다.
## 4. 참고
- 두 축 개념·소유 레포·문서 단일 출처 매핑: [CLAUDE.md](../CLAUDE.md) "CVE/SBOM 게이트 작업"
- 차트 카탈로그 축 스캔 메커니즘·CI 위생(캐싱·concurrency) 상세: [doc/sbom-pipeline.md](sbom-pipeline.md)
- 자체 빌드 축 이관 배경·계약: [doc/migrations/](migrations/)
- 자체 빌드 드리프트 전용 워크플로를 없앤 근거: ADR [0005](decisions/0005-self-build-drift-check-removed.md)
- 차단 CVE 대응 시 레버 판단: [.claude/skills/cve-remediation/SKILL.md](../.claude/skills/cve-remediation/SKILL.md)
- `hardened-containers` 자체 파이프라인(빌드→검증→SBOM→스캔→게이트→push, 매일 자율
재스캔 `rescan.yml`) 상세는 그 레포의 `docs/architecture.md`가 단일 출처다 — 이 문서는
그 파이프라인의 트리거·출력 지점만 다룬다.
+1 -1
View File
@@ -35,7 +35,7 @@ dev 클러스터 `apisix` 네임스페이스에 이미 `data-apisix-etcd-0` PVC
1. 패치가 멈춘 이미지는 시간이 지날수록 미수정 CVE 가 구조적으로 누적된다 —
"CRITICAL/HIGH 0건" 을 유지 가능한 상태로 지속할 수 없다.
2. 버전 고정 없는 `latest`-only 배포는 **롤링 태그 금지** 규칙과 애초에 양립하지 않는다
(자체 빌드 이미지에 적용되는 이 규칙은 `hardened-containers` 레포의 `docs/image-authoring.md`
(자체 빌드 이미지에 적용되는 이 규칙은 `hardened-containers` 레포의 `docs/image-authoring/README.md`
가 갖는다).
`groundhog2k/etcd` 는 업스트림 etcd 프로젝트의 원본 이미지(`quay.io/coreos/etcd`)를 그대로
@@ -0,0 +1,75 @@
# 0005. 자체 빌드 드리프트 스캔·재빌드 트리거를 dip-catalog에서 제거한다
- 날짜: 2026-08-26
- 상태: 확정
## 결정
dip-catalog에서 자체 빌드 이미지 재빌드 트리거와 전용 드리프트 스캔을 모두 제거한다 —
`.github/workflows/self-build-drift-check.yml``scripts/build/check-rebuild-needed.py`
삭제한다. 자체 빌드 이미지의 CVE는 `sbom.yml`이 다른 카탈로그 이미지와 동일하게
스캔·보고하고, 재빌드 여부·시점은 전적으로 `hardened-containers``rescan.yml`
자율 결정한다.
## 배경
`self-build-drift-check.yml`은 두 가지를 했다: (1) 배포 중인 자체 빌드 이미지를 스캔해
재빌드 대상을 판정하고, (2) 대상이 있으면 별도 레포 `hardened-containers`
`build-image.yml``workflow_dispatch`로 트리거했다.
## 근거
자체 빌드 이미지의 CVE는 이미 세 겹으로 덮여 있었다.
1. **빌드 시점**`hardened-containers`가 강제 zero-CVE 게이트를 통과한 이미지만 push하고,
`published.json`은 게이트 PASS + 실제 push된 것만 기록한다. 새로 발행된 이미지는
구조적으로 깨끗하다.
2. **빌드 이후 드리프트** — 그 레포의 `rescan.yml`이 매일 재스캔하고 자율적으로 재빌드한다.
dip-catalog가 별도로 트리거를 보내는 건 이 루프를 중복시킬 뿐이다. 실제로도
`SECURITY_IMAGES_DISPATCH_TOKEN` 시크릿이 한 번도 등록된 적 없어 트리거 스텝은
실행할 때마다 실패하는 죽은 코드였다.
3. **dip-catalog가 실제 배포 중인 것**`sbom.yml`이 이미 스캔한다. `extract-helm-images.sh`
`custom-values.yaml`을 적용해 `helm template`로 렌더하므로 `docker.io/paasup/*`가 그대로
스캔 대상에 들어가고, `cve-gate.py`가 같은 게이트 규칙(실효 등급 `max(벤더,NVD)` ·
승인 예외)으로 판정한다. 같은 이미지를 주 2회(일 18:00 · 월 02:00) 따로 스캔하고
있었을 뿐이다.
`check-rebuild-needed.py`가 유일하게 더하던 "수정 버전이 있는가(fixable/no-fix)" 분류조차
새 정보가 아니었다 — `cve-gate.py`의 리포트 테이블에 이미 "수정 버전" 컬럼이 있고
`FixedVersion`으로 채워진다. 이 스크립트는 같은 findings를 자체 빌드 8개 기준으로 다시
묶어 보여줄 뿐이었다.
### 탈락한 후보
- **(a) 현행 유지** — 트리거 + 별도 워크플로 그대로. 위 근거대로 순수 중복이라 기각.
- **(b) 트리거만 제거, 워크플로는 스캔·보고용으로 존치** — `sbom.yml`과 같은 이미지를
또 스캔하는 문제(주 2회 중복)가 남아 기각.
- **(c) `check-rebuild-needed.py``sbom.yml`의 리포트 스텝으로 흡수** — SBOM/리포트
파일 명명 규칙이 두 파이프라인에서 동일해(`tr '/:@' '___'` vs `re.sub(r"[/:@]","_",ref)`)
기술적으로는 가능했다. 하지만 `cve-gate.py` 리포트에 이미 "수정 버전" 컬럼이 있어
흡수해도 **새 정보가 생기지 않음**을 확인해 기각했다 — 같은 findings의 재그룹핑을
위해 스크립트와 워크플로 스텝을 유지할 이유가 없었다.
## 받아들인 비용
- 자체 빌드 8개만 좁혀 보는 전용 표가 사라진다 — `sbom.yml` 리포트에서
`docker.io/paasup/*`를 골라 봐야 한다.
- 자체 빌드 이미지 리포트 주기가 월요일 02:00 → 일요일 18:00(`sbom.yml` 스케줄)로
바뀌고, 그 워크플로가 실패하면 자체 빌드 리포트도 함께 보이지 않는다.
- dip-catalog가 옛 태그에 멈춰 있어도 스스로 재빌드를 재촉할 수단이 없다 — Job Summary
리포트를 사람이 보고 수동으로 판단해야 한다. 단, `catalog-tag-update.yml`이 매일
폴링해 보통 하루 안에 따라잡으므로 드문 시나리오다.
## 알려진 잠재 공백 (이번 결정이 만든 것이 아니라 기존부터 있던 것 — 기록만)
`extract-helm-images.sh``custom-values.yaml`만 적용해 렌더하므로 `dip-values.yaml`에만
있는 태그는 스캔되지 않는다. 현재 카탈로그에서는 두 파일의 paasup 태그가 일치해 실질
영향이 없음을 확인했다(cnpg-cluster 1.0.0/1.1.0). `manifests/applicationset/**` 미스캔
(MEMORY.md #42)과 같은 성격의 공백이다.
## 재검토 조건
- `hardened-containers``rescan.yml`이 장기간 중단되거나 신뢰할 수 없게 되면 — 그 경우
dip-catalog 쪽에 독립적인 드리프트 감시가 다시 필요해진다.
- `dip-values.yaml`에만 존재하는 자체 빌드 태그가 실제로 생기면 — 이 결정의 전제("두
values 파일의 paasup 태그가 일치한다")가 깨지므로 스캔 커버리지를 다시 검토해야 한다.
+2 -2
View File
@@ -9,14 +9,14 @@ CVE 건수·커버리지·패키지 버전 같은 수치는 게이트가 매 실
| # | 결정 | 날짜 | 상태 |
|---|---|---|---|
| [0003](0003-etcd-chart-selection.md) | etcd 는 `groundhog2k/etcd` 단일 차트 · replicas=1 로 배포한다 | 2026-07-31 | 확정 |
| [0005](0005-self-build-drift-check-removed.md) | 자체 빌드 드리프트 스캔·재빌드 트리거를 dip-catalog에서 제거한다(sbom.yml + hardened-containers의 rescan.yml로 대체) | 2026-08-26 | 확정 |
**이미지 자체 빌드 ADR(0001·0002·0004)은 별도 레포 `hardened-containers` 로 이관됐다** —
그 이미지들의 빌드 정의와 함께 그 레포의 `docs/decisions/` 에 있다. 카탈로그가 무엇을
배포하는가(차트)와 그 이미지를 어떻게 만드는가(자체 빌드)가 다른 레포에 놓이며 근거도
함께 옮긴 것이다. 이관 배경은
[doc/migrations/self-build-images-to-hardened-containers.md](../migrations/self-build-images-to-hardened-containers.md).
번호가 0003 하나만 남아 이어지지 않아 보이지만, 다른 세 번호가 예약돼 있던 것일 뿐이고
새 ADR 은 0005 부터 잇는다.
0004까지는 그 세 번호가 예약돼 있던 것이고, 새 ADR 은 0006 부터 잇는다.
## 이 문서들의 출처
@@ -27,7 +27,7 @@
| `scripts/build/suggest-go-upgrades.py` | `--apply`/`--dry-run` 신규 추가 |
| `scripts/pipeline/scan-sbom.sh` · `cve-gate.py` (이미지 판정 부분만) | `scripts/gate/scan-image.sh` · `image-gate.py` (슬림화) |
| `.github/workflows/build-image.yml` | 카탈로그 무의존으로 재구성(`catalog.env` 의존 제거, drift 모드 제거) |
| `.claude/image-authoring.md` | `docs/image-authoring.md` (정책 중심으로 재작성) |
| `.claude/image-authoring.md` | `docs/image-authoring.md` (정책 중심으로 재작성, 이후 `docs/image-authoring/`로 추가 분할됨) |
| `doc/decisions/0001·0002·0004`(이미지 ADR) | `docs/decisions/`(같은 번호 유지) |
| `.claude/skills/self-build-image/` | 그대로(문서 경로만 수정) |
@@ -38,12 +38,16 @@
| 경로 | 역할 |
|---|---|
| `catalog/image-map/<image>.env` | 자체 빌드 이미지 → 어느 차트의 어느 필드(`CHART_DIRS`·`TAG_STYLE`·`TAG_BLOCK`). 옛 `images/<image>/catalog.env` 의 카탈로그 레이아웃 정보만 뗀 것 |
| `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` | 주간, 배포 중인 이미지 스캔 → 재빌드 필요하면 `hardened-containers``build-image.yml``workflow_dispatch` 로 트리거만 |
| `.github/workflows/catalog-tag-update.yml` | 매일, `published.json` 조회 → 카탈로그 values 패치 → 브랜치 push |
> 드리프트 탐지 전용 스크립트(`scripts/build/check-rebuild-needed.py`)와 그것을 주간으로
> 돌리던 `.github/workflows/self-build-drift-check.yml`은 이후 제거됐다 — `sbom.yml`
> 자체 빌드 이미지도 다른 카탈로그 이미지와 동일하게 스캔·보고하고, 재빌드는
> `hardened-containers``rescan.yml`이 전적으로 자율 수행하므로 별도 경로가 불필요했다.
> 근거는 ADR [0005](../decisions/0005-self-build-drift-check-removed.md).
## 계약 — `published.json` 하나뿐
`hardened-containers`**이 카탈로그를 모른다.** 게이트 PASS + 레지스트리 push 가 실제로
@@ -81,13 +85,6 @@
## 아직 안 된 것
- **`SECURITY_IMAGES_DISPATCH_TOKEN` 시크릿 미등록.** `self-build-drift-check.yml`
`hardened-containers``build-image.yml``workflow_dispatch` 로 부르려면
그 레포에 `workflow_dispatch` 권한이 있는 PAT 이 필요하다. 등록 전까지 트리거 스텝은
명시적으로 실패한다.
- **`hardened-containers` 레포 자체가 아직 GitHub 에 없다.** 로컬 레포만 있는 상태에서
이 이관을 진행했다 — GitHub 레포 생성·push, `DOCKERHUB_USER`/`DOCKERHUB_TOKEN` 시크릿
등록이 선행돼야 위 두 워크플로가 실제로 동작한다.
- **`published.json``digest` 필드 대부분 공란.** `hardened-containers` 가 아직 실제로
이미지를 재빌드·push 하지 않아서다. 다음 빌드가 채운다 — MEMORY.md 의 "카탈로그 태그가
클러스터보다 앞서 있다" 항목이 이 필드를 쓸 계획이다.
+13 -7
View File
@@ -12,8 +12,8 @@ GitHub Actions([.github/workflows/sbom.yml](../.github/workflows/sbom.yml))로
|---|---|---|
| 질문 | 우리가 배포하는 이미지에 무엇이 있는가 | 그 이미지를 어떻게 만드는가 |
| 실행 위치 | 이 레포 | 별도 레포 `hardened-containers` |
| 워크플로 | `sbom.yml` · `cve-edge-post.yml` | `hardened-containers``build-image.yml`(이 레포에서는 `self-build-drift-check.yml` 필요할 때 그것을 부른다) |
| 단일 출처 | **이 문서** | `hardened-containers` 레포의 `docs/image-authoring.md` |
| 워크플로 | `sbom.yml` · `cve-edge-post.yml` | `hardened-containers``build-image.yml`·`rescan.yml`(레포는 트리거하지 않는다) |
| 단일 출처 | **이 문서** | `hardened-containers` 레포의 `docs/image-authoring/README.md` |
**카탈로그 values 가 자체 빌드 이미지(`docker.io/paasup/*`)를 가리키므로 그 이미지도 이 문서의
스캔 대상이다** — 축이 갈린 것은 "누가 만들고 결정하는가" 이고 "누가 스캔되는가" 가 아니다.
@@ -137,6 +137,12 @@ docker buildx build --platform linux/amd64 \
> python 으로 갖고 있어 **승인 예외(`cve-exceptions.json`)도 실효 등급(`max(벤더,NVD)`)도
> 적용하지 않는다.** 같은 스캔 데이터에서 다른 숫자가 나올 수 있다.
두 워크플로 모두 날짜 단위 키(`trivy-db-<YYYYMMDD>`)로 trivy 취약점 DB를 `actions/cache`
캐싱한다 — 실패해도 워크플로를 막지 않는다(`continue-on-error`, 캐시는 최적화일 뿐 정확성
요건이 아니다). `concurrency` 그룹도 있다 — `sbom.yml``sbom-${{ github.ref }}`(PR은
새 커밋이 오면 이전 실행 취소, 스케줄은 취소 안 함), `cve-edge-post.yml``cve-edge-post`
(항상 직렬화, 외부 POST 잡이라 취소 대신 대기).
**PR 을 실제로 막는 게이트는 이 축에 없다** — `sbom.yml` 은 warn-only 다. 강제 게이트는
자체 빌드 축(`hardened-containers` 레포의 `images/**` PR)에만 있다.
@@ -208,11 +214,11 @@ Repo Secret `CVE_API_KEY`).
## 자체 빌드 축은 이 문서가 다루지 않는다
게이트가 상위 태그 교체·베이스 OS 교체로 해소되지 않는 차단 CVE 를 찾으면 자체 빌드로 간다.
그 축은 **별도 레포 `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-hardened-containers.md).
그 축은 **별도 레포 `hardened-containers`** 가 갖는다 — 빌드·검증·게이트·push·드리프트 재빌드
전부 그 레포 안에서 이루어지고(`rescan.yml`이 매일 자율 재스캔·재빌드), 단일 출처는 그
레포의 `docs/image-authoring/README.md` 다. 이 카탈로그에는 "어느 차트가 그 이미지를
가리키는가"(`catalog/image-map/`)만 남아 있다 — 이관 배경은
[doc/migrations/](migrations/self-build-images-to-hardened-containers.md).
이 문서가 알아야 할 것은 하나뿐이다: **자체 빌드 이미지도 카탈로그가 가리키는 한 위 스캔·게이트
대상이다.** 실제로 `docker.io/paasup/*` 가 게이트 리포트에 등장한다.