d3816a14caefb8ffeb900a3a0ac773e086720fa7
11 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
79555215a0 |
파이프라인을 두 축으로 갈라 소유 문서를 확정한다
"파이프라인이 chart CVE 조치와 커스텀 이미지 빌드 2개로 나뉘어 있는가" 를 확인하다가 실제
구성이 그 모델과 다른 것이 드러났다.
1. 워크플로는 3개다. cve-edge-post.yml 이 CLAUDE.md 에서 디렉토리 트리와 괄호 안에만
등장해 파이프라인으로 읽히지 않았다 — "2개" 인식의 근원이다.
2. doc/sbom-pipeline.md 가 두 축을 한 파일에 담고 있었다(자체 빌드 절 43줄).
커스텀 이미지가 별도 레포로 분리될 예정인데 이 상태로는 분리 때 파일을 찢어야 한다.
3. 그 문서가 이미 뒤집힌 결정을 담고 있었다 — "schedule 트리거는 없다. 블라인드 정기
재빌드는 제거했다" 인데 PR #40 이 schedule 을 추가했다.
축을 이렇게 갈랐다
-----------------
차트 카탈로그 축 sbom.yml · cve-edge-post.yml → doc/sbom-pipeline.md
자체 빌드 축 build-image.yml → .claude/image-authoring.md
doc/sbom-pipeline.md — 차트 축만 남긴다
- 상단에 "이 문서가 다루는 축" 을 두고 자체 빌드는 링크로 넘긴다
- 자체 빌드 절(43줄)을 image-authoring.md 로 이관
- sbom.yml ↔ cve-edge-post.yml 비교 표 신설. **판정기가 두 벌**이라는 사실을 명시했다 —
cve-edge-post.yml 은 집계를 워크플로 YAML 안의 인라인 python 으로 갖고 있어 승인 예외도
실효 등급도 적용하지 않는다. 같은 스캔 데이터에서 다른 숫자가 나올 수 있다
- PR 을 실제로 막는 게이트는 images/** PR 뿐이고 manifests/applicationset/** 는 아무
워크플로도 보지 않는다는 사각지대를 적었다
.claude/image-authoring.md — 자체 빌드 축의 단일 출처가 된다
- 이관받은 워크플로 서술 + "이 워크플로의 게이트는 강제다"(차트 축 warn-only 와 다르다는
사실이 지금까지 한 곳에만 있었다)
- schedule 결정 정정 — 지금 것은 블라인드가 아니라 CVE 트리거다. 수정 버전이 있는 차단
CVE 가 있을 때만 빌드하고 없으면 아무것도 하지 않는다. 거부된 것과 조건이 다르다
- "레포 분리 후 무엇이 끊기는가" 결합점 7개 표. 3번(탐지가 카탈로그를 읽는다)이 가장 크고,
게이트 공유는 workflow_call 이 아니라 composite action 이어야 한다는 것도 적었다
(workflow_call 은 별도 job 이라 $OUT_DIR 를 공유하지 못한다)
- 파일 상단에 "레포 분리 시 images/·scripts/build/·build-image.yml 과 함께 이동한다"
- #35 에서 실측한 매핑 함정 추가 — CHART_DIRS 에 없는 파일은 patch-catalog-tag.py 가
검사조차 하지 않아 cnpg-cluster/1.1.0 이 조용히 빠졌다
CLAUDE.md — 지도만 남긴다
두 축 비교 표(질문·워크플로·게이트 강도·소유 문서)로 바꾸고 메커니즘 서술을 걷어냈다.
cve-edge-post.yml 을 파이프라인으로 처음 등재했다.
검증
----
표의 사실 대조 각 워크플로의 cve-gate 호출·warn-only·활성 schedule 을 파일에서 확인
축 분리 sbom-pipeline.md 에 남은 build-image.yml 언급은 전부 링크·대조·시크릿
공유 문장(의도된 것)
뒤집힌 결정 "schedule 트리거는 없다" 잔존 0건
링크 3개 문서의 로컬 링크 전부 실재
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
981d36daa4 |
차단 CVE 가 나오면 자체 빌드 이미지를 스스로 재빌드한다 + Dockerfile 작성 규칙 (#40)
* 차단 CVE 가 나오면 자체 빌드 이미지를 스스로 재빌드한다 + Dockerfile 작성 규칙
두 가지다. (1) 이전 이미지에서 CVE 가 발견되면 재빌드가 자동으로 일어나게 한다.
(2) Dockerfile 을 OS·언어 축으로 일관되게 쓰도록 규칙을 정한다.
(1) 재빌드 자동화 — 트리거를 로컬 실측으로 고쳤다
------------------------------------------------
처음에는 트리거를 "핀이 뒤처졌는가" 로 잡았다. **배포 중인 이미지 8개를 실제로 스캔해 보니
그게 틀렸다** — 차단 CVE 가 있는 4개 중 핀 변경이 필요한 것은 하나도 없었고, 그 판정은
"0건" 을 냈다. 재빌드로 고쳐지는 경우가 셋이기 때문이다.
핀이 뒤처졌다 → 핀 올려서 빌드
핀은 맞는데 그 핀으로 아직 안 빌드됐다 → 그냥 빌드 ← 핀만 고친 PR 직후 상태
베이스 OS 패키지가 뒤처졌다 → 그냥 빌드 ← 재빌드하면 zypper 가 최신을 깐다
그래서 트리거는 **"수정 버전이 있는 차단 CVE 가 있는가"** 다. 핀 변경은 필요하면 함께 하는
부수 작업이고 트리거가 아니다. 수정 버전 없는 차단(affected·will_not_fix)은 재빌드해도
그대로이므로 no-fix 로 따로 보고해 사람이 다른 레버를 판단하게 한다.
scripts/build/check-rebuild-needed.py 신규
카탈로그가 실제로 가리키는 이미지를 기준으로 잰다 — 재빌드해 보지 않고도 "지금 배포 중인
것이 규정을 벗어났는가" 를 답한다.
catalog.env → 카탈로그 values 의 ref → 그 ref 의 스캔 리포트 ↕ build.env 의 핀
판정 규칙은 게이트와 같은 출처를 쓴다(max(벤더,NVD)·승인 예외·만료 예외 무효) —
cve-gate.py 의 함수를 그대로 가져다 쓴다. 핀 산출은 suggest-go-upgrades.py 재사용.
--list-refs 로 "무엇을 스캔해야 하나" 도 스크립트가 낸다 — ref 해석 규칙이 워크플로로
새면 두 곳이 어긋난다.
suggest-go-upgrades.py 는 산출 로직이 main() 안에 있어 재사용할 수 없었다. collect_modules/
collect_stdlib/builder_candidates/effective_severity 로 빼고 main() 은 호출해 출력만 한다 —
CLI 출력은 리팩터 전후 동일하다(실측 대조).
build-image.yml 에 schedule(월요일 02:00 UTC)과 mode=drift 를 추가했다. 판정 → (핀이
뒤처졌으면) 브랜치 push → 빌드·verify.sh·게이트까지 자동이고 **레지스트리 push 와 카탈로그
반영은 하지 않는다.** 게시 여부 로직에서 catch-all 을 true → false 로 바꿨다 — 원래는
트리거를 추가하면 자동으로 push 가 켜지는 구조였고 schedule 을 넣는 순간 사고가 났다.
이제 push 는 workflow_dispatch + mode=image 에서만 켜진다.
(2) Dockerfile 작성 규칙 — 실제 8개에서 추출했다
----------------------------------------------
image-authoring.md 가 규칙 본문의 단일 출처이고 skill 은 "어느 절을 읽어야 하는가" 선택표만
갖는다(CLAUDE.md 의 "Skill 은 절차 본문을 복제하지 않는다").
두 축은 독립이다 — 같은 bci-micro 최종 위에 Go 빌더도 Node 빌더도 온다.
축 1 최종 베이스 bci-base / bci-micro / scratch+micro rootfs — 셋뿐이다.
네 번째를 만들기 전에 왜 셋으로 안 되는지 먼저 적는다.
micro·scratch 는 nonroot 계정을 직접 만들어야 한다(실측 형태 첨부).
축 2 빌더 Go(BUILDPLATFORM/TARGETARCH, ldflags 에 SOURCE_COMMIT) /
Node(런타임은 OS 패키지) / JVM(재컴파일 없이 jar 만 OLD/NEW 쌍으로
교체 — OLD 를 받는 이유는 못 찾으면 실패시키기 위함) /
C·Lua(정적 링크 금지 — SLE_BCI 에 static glibc 없음)
"업스트림 런타임 계약은 보존한다" 절을 새로 넣었다(USER·ENTRYPOINT·파일 레이아웃).
cloudnative-pg 가 멀티아치 심볼릭 링크를 부수 장치로 오판해 지웠다가 리컨실이 실패한 것이
근거다. 스캐너는 이걸 전혀 보지 못한다.
로컬 검증 — 판정만이 아니라 빌드까지 돌렸다
-----------------------------------------
판정이 지목한 4건을 실제로 빌드해 전제를 확인했다. 문서에 단정만 해두고 넘어가지 않았다.
이미지 배포 중 재빌드 후 핀 변경
apisix-ingress-controller 차단 8 0/0 PASS · VERIFY-OK · cov=ok 없음
cloudnative-pg 차단 8 0/0 PASS · VERIFY-OK · cov=ok 없음
etcd 차단 8 0/0 PASS · VERIFY-OK · cov=ok 없음
cnpg-postgresql 차단 2 0/0 PASS · VERIFY-OK · cov=ok 없음
- cnpg-postgresql 이 "베이스 OS 패키지 갱신으로 해소" 사례다(perl·rpm-ndb). 핀을 하나도
안 바꾸고 재빌드만으로 0건이 됐다 — 검증 없이 단정했던 부분이라 이게 핵심 확인이다.
- 빌드된 etcd 바이너리가 go1.26.6 + pinned commit 을 보고한다 — 핀이 실제로 반영됐다.
- 4건 다 CoverageProbe=ok 이므로 0건이 "측정되지 않음" 이 아니다.
- 빌드 후에도 git status images/ 가 깨끗하다 = 트리거를 핀 기준으로 뒀다면 4건 전부
놓쳤을 것이라는 확인.
그 외: 예외 적용(keycloak CVE-2025-59250 → 0건), --fail-on-drift rc=1, 워크플로 셸 재현
(GITHUB_OUTPUT 4개 JSON·fromJson OK·핀 변경 0 → 브랜치 미생성 → 기본 ref 로 빌드),
push 정책 전 조합 확인.
CI 에서 미검증인 것은 환경 특성뿐이다 — trivy 설치 스텝, GITHUB_STEP_SUMMARY 출력,
matrix 팬아웃, contents:write 로 브랜치 push. 빌드 자체는 로컬과 같은 스크립트를 쓴다.
Refs #35
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* self-build-image SKILL 의 자동 재빌드 절을 실행 계약만 남긴다
같은 커밋에서 image-authoring.md 와 SKILL 양쪽에 자동 재빌드 절차를 거의 동일하게 썼다.
SKILL 이 "작성 규칙 본문은 image-authoring.md 가 단일 출처다 — 여기 복제하지 않는다" 고
두 번 선언한 파일에서 내가 그 규칙을 어겼다.
SKILL 에는 실행 계약(수동 실행 명령 · 로컬 판정 명령 · push 안 한다는 사실)만 남기고
트리거 조건과 그 근거는 image-authoring.md 를 가리킨다.
frontmatter 도 함께 고친다 — "7종" 에 argocd 가 빠져 있어 실제 images/ 8개와 어긋났다.
description 은 Skill 로딩 판단에 쓰이는 텍스트이므로 "목록은 images/ 가 단일 출처" 라는
회피 조항으로 덮을 수 없다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* 레포 분리 시 갈라지는 이음선을 코드와 문서에 명시한다
커스텀 이미지가 별도 레포로 분리될 예정인데, check-rebuild-needed.py 는 카탈로그(무엇을
배포 중인가)와 build.env(어떻게 만드는가)를 **양쪽 다** 읽는다. 분리 시 이 파일이 갈라진다.
절단면은 이미 함수 경계에 있다. 6개월 뒤에 "이 함수가 왜 카탈로그를 읽지" 를 다시 파지
않도록 어느 함수가 어느 쪽인지 docstring 에 적었다.
A (카탈로그 레포) resolve_current_ref + blocking_cves → "차단됐는가"
B (이미지 레포) pin_changes + apply_changes → "핀을 올려야 하는가"
check_image 둘을 엮는 오케스트레이션 — 여기가 갈라진다
image-authoring.md 의 드리프트 절에도 같은 사실을 한 문단으로 적어 분리 작업 때 이 주석을
먼저 읽게 했다. 결합점 전체 목록은 경계 문서화 작업에서 표로 정리한다.
코드 동작은 바뀌지 않았다 — 주석과 문서만 추가했다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
1a747f61a8 |
자체 빌드 이미지 문서가 이 레포에 없는 경로를 인용하던 것을 없앤다 (#33) (#34)
images/·manifests/helm/·.claude/ 의 20개 파일이 doc/decisions·doc/analysis 등 **이 레포에 존재한 적 없는 경로 15종을 48곳에서** 인용하고 있었다. security-catalog 에서 포팅할 때 따라온 것인데, 그 레포는 개인 레포(github.com/wbsong111/security-catalog)라 팀 구성원은 접근조차 못 한다 — "security-catalog 에 있으나 이관되지 않았다" 는 안내가 아무 역할을 하지 못했다. 원문을 통째로 복사하지 않았다 ---------------------------- 원본 문서들이 서로를 근거로 인용한다. decisions/0001 하나만 봐도 analysis/cnpg-image-baseline.md · analysis/vendor-unassessed-data-sources.md 처럼 **인용 목록에 없던 또 다른 미이관 문서**를 가리킨다. 복사는 문제를 옮기는 것이지 없애는 게 아니다. 그리고 대부분은 애초에 dip-catalog 가 더 나은 것을 갖고 있다. 7곳에서 인용되던 analysis/sles-oval-measurement.md 는 원문 스스로 "이 문서는 결정하지 않는다. 재측정하면 갱신된다" 고 밝히는 스냅샷인데, dip-catalog 는 같은 측정을 CoverageProbe 로 매 스캔마다 자동으로 한다. 문서를 복사하는 것보다 게이트를 가리키는 것이 정확하다. 그래서 성격별로 나눴다 --------------------- 재측정으로 복원 안 되는 것 → doc/decisions/ 에 자립적 ADR 로 다시 씀 (4건) 이미 단일 출처가 있는 것 → 그쪽으로 인용 교체 (11종 경로) ADR 4건은 security-catalog 0001·0005·0006·0007 이 원본이고, 결론과 근거만 추려 dip-catalog 맥락으로 새로 썼다 — **레포 밖을 가리키는 링크가 0이다.** 번호는 이 레포에서 0001~0004 로 다시 붙였고 원본 대응은 각 문서와 README 에 적었다. 왜 안 가져온 것은 안 가져왔는지도 README 표에 남겼다. 인용 교체는 카테고리별로: analysis/*-cve.md, cnpg-image-vuln-comparison.md → 해당 ADR · images/<image>/README.md analysis/sles-oval-measurement.md → 게이트 CoverageProbe (doc/sbom-pipeline.md) cve-zero-pipeline.md, architecture/build-pipeline.md → doc/sbom-pipeline.md image-selection.md → .claude/image-authoring.md charts/*/deploy-test.md → scripts/deploy-test/*.sh + 절차 문서 찾은 오류 2건 ------------- - images/cloudnative-pg/source.build.env 가 인용한 decisions/0004-cloudnative-pg-operator-self-build.md 는 **번호 오기**다. 원본 0004 는 postgresql-chart-selection 이고 이 결정은 0005 다. - cnpg-cluster values.yaml·templates/database.yaml 이 인용한 doc/deploy-test-cnpg.md 는 **원본 레포에도 없다.** CREATE EXTENSION 함정 설명은 주석 자체에 이미 있어 인용만 뺐다. 검증 ---- 우리 파일의 깨진 doc/ 인용 0건 (전수 스캔) 새 문서·수정 문서의 로컬 링크 전부 실재 확인 helm template cnpg-cluster · etcd · cloudnative-pg 정상 렌더 남은 doc/health-checking.md(144곳)·doc/integration/*(2곳)은 업스트림 CRD·차트 안의 문자열로 우리가 쓴 인용이 아니다 — 건드리지 않았다. Closes #33 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
63d25221df |
MEMORY.md 를 상태 파일로 되돌린다 (264줄 → 64줄)
MEMORY.md 는 3번째 줄에서 "지금 시점의 상태와 다음에 할 일만 담는다" 고 스스로 선언하는데
실제로는 완료 기록이 쌓여 264줄이 됐고, 이슈 참조가 한 건도 없었다. 그 결과:
- SBOM_PIPELINE_IMAGE 재빌드가 파일 안에서 3번 반복된다 (215·234·262줄)
- 게이트 강제력 전환이 2번 반복되고, 그 내용은 이미 이슈 #7 체크리스트와 #19 에 있다
- argo-cd 절 55줄은 커밋
|
||
|
|
33e91c0536 |
argocd: 자체 빌드 하드닝 이미지 추가, 게이트 차단 0건
업스트림 quay.io/argoproj/argocd 의 차단 CVE 는 대부분 OS 패키지가 아니라 바이너리에
정적 링크된 Go 모듈이라, 상위 태그 교체(이미 최신)로도 베이스 OS 교체로도 잡히지 않는다.
부분 조치는 효과가 없었다 — 같은 stdlib·x/crypto 취약점이 다섯 바이너리(argocd·helm·
kustomize·git-lfs·pebble)에 각각 들어있어 본체만 고치면 거의 줄지 않는다. 업스트림
Dockerfile 이 helm/kustomize/git-lfs 를 hack/install.sh 로 **미리 빌드된 릴리스 바이너리**
로 내려받기 때문에 그 바이너리의 Go 버전을 우리가 통제할 수도 없다. 그래서 넷 다 소스에서
다시 컴파일한다.
- images/argocd/ 신규 (Go 4개 + C 2개 + Node UI — 기존 자체 빌드 중 가장 크다)
- 최종 베이스 ubuntu → SUSE BCI(원칙 2). ubuntu 가 딸려오던 pebble 도 함께 사라진다
- tini·connect-proxy 는 SLE_BCI 에 없어 소스 빌드 — 기능을 빼지 않기 위해
- UI 는 업스트림과 동일하게 node 로 빌드(Go embed 라 생략 불가)
- manifests/helm/argo-cd/10.4.0/custom-values.yaml 이 이 이미지를 가리킨다
global.image 하나로 argocd 바이너리를 쓰는 5개 컴포넌트가 공유한다
재사용 설계 — Dockerfile 에 버전을 박지 않는다
---------------------------------------------
이 이미지는 앞으로도 CVE 조치를 반복해서 받는다. 모듈 목록·패키지 목록을 전부 값으로 빼서
다음 조치에서 바뀌는 것이 source.build.env 한 곳뿐이게 했다. 이전 방식이라면 모듈 하나
추가에 build.env·ARG 선언·go get 목록·BUILD_ARGS 네 곳을 고쳐야 했다.
- scripts/build/suggest-go-upgrades.py 신규 — 스캔 리포트의 FixedVersion 에서
GO_MODULE_UPGRADES / GO_BUILDER_TAG 를 산출한다. 사람이 CVE 를 훑어 최대값을 고르지
않는다. 빌드 시점에 최신을 당기는 방식(go get -u)은 재현성을 버리므로 택하지 않았다 —
값은 제안만 하고 채택은 사람이 커밋한다.
- images/argocd/go-mod-upgrade.sh 신규 — 목록 하나를 네 프로젝트에 재사용한다.
go get 은 의존성에 없는 모듈도 go.mod 에 추가하므로 그래프에 있는 것만 골라 적용한다.
실측 함정 — verify.sh 에 반영했다
--------------------------------
차트의 repo-server init 컨테이너가 `cp --update=none` 을 쓴다. 이 형식은 GNU coreutils
9.3+ 에서만 되는데, BCI 15.7 로 빌드한 이미지가 게이트도 verify.sh 도 통과하고 **배포
시점에** Init:CrashLoopBackOff 로 죽었다. BCI 16.0(coreutils 9.6)으로 올려 해소했고,
verify.sh 가 이 명령과 copyutil 흐름을 직접 재현하도록 해서 다음엔 빌드 단계에서 걸린다.
16.0 의 패키지 가용성과 trivy 커버리지(CoverageProbe=ok, EOSL 아님)도 확인했다.
검증
----
게이트 PASS — 실효 CRITICAL/HIGH 0건, CoverageProbe ok
기능 VERIFY-OK (심볼릭 링크 9개 · 번들 도구 3종 · LFS 필터 · copyutil 흐름)
배포 docker.io/paasup 에 push 후 운영 argocd 교체 — 파드 8종 Running,
admin 로그인, Application 2건 Synced/Healthy, repo-server 렌더링 확인
(서버가 go1.26.6 · helm v4.2.4 로 보고 — 우리 빌드가 맞다)
카탈로그 차단은 574 → 239 건이 됐고(7.7.0 삭제 · dex 비활성 · 이 커밋), 남은 239 건은
전부 동결된 7.8.11 몫이다. 운영에 쓰는 10.4.0 은 0 건이다. 경과·미결은 MEMORY.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
4f67f63f69 |
SBOM 파이프라인 문서 정비 + 이미지 목록 열거 제거
## doc/sbom-pipeline.md — 중복·모순 정리 (328 → 286줄) 같은 사실이 여러 절에 흩어져 있었고, 일부는 문서가 아니라 변경 이력이었다. - `SEVERITY` 를 전 심각도로 덮어써야 하는 이유가 환경변수 절·CI 절·요약 절 3곳에 있었다. 스크립트 절의 blockquote 하나로 합쳤다 — "게이트를 돌릴 거라면 전 심각도로 스캔해야 한다"가 핵심이고 나머지는 그 결과다. - Job Summary 1MB 제한이 CI 절과 결과 확인 절에 중복됐다. CI 절 하나로 합쳤다. - `--warn-only` 서술이 mermaid 라벨·CI 절·게이트 절·트리아지 절 4곳에 있었다. 게이트 절 하나로 합치고, `build-image.yml` 쪽은 이미 강제라는 대비를 함께 적었다. - 자체 빌드 트리거 표가 "PR 은 push 안 함"을 말하는데 바로 아래 불릿이 같은 말을 반복했다. 표는 그대로 두고 불릿은 **왜** 그런지(REGISTRY 미전달 → localhost 태그라 push 를 시도할 수조차 없다)만 남겼다. - "오해를 주던 단일 '총 소요'는 제거" 같은 변경 이력 서술을 걷어냈다. 문서는 현재 상태를 적는 곳이다. - "첫 전체 실행 결과(2026-07-08)" 절은 수치를 싣고 바로 아래에서 "현재 수치가 아니다"로 무효화하는 구조였다. 절 자체를 없애고, 거기서 유일하게 쓸모 있던 사실 (SBOM 생성 실패는 대부분 사설/미인증 레지스트리이거나 대용량 timeout)만 스크립트 절로 옮겼다. - "실행 이력(2026-08-04)" 절은 MEMORY.md 와 중복이라 제거했다. 거기서만 알 수 있던 사실(Actions 시크릿의 push 권한 확인)은 GitHub 설정 표에 반영했다. - `CVE_API_KEY` 가 본문에만 있고 GitHub 설정 표에 빠져 있어 추가했다. - 아키텍처 절 불릿이 mermaid 서브그래프 라벨과 같은 말을 하고 있어, "pull 을 ② 한 곳에 몰아둔 것이 핵심"이라는 결론 한 문장으로 줄였다. ## 이미지 목록을 문서에 박아두지 않는다 이미지는 계속 추가되므로 열거하면 추가할 때마다 낡는다. `images/` 디렉토리를 단일 출처로 삼고 CLAUDE.md·image-authoring.md·build-image.yml·sbom-pipeline.md 의 열거를 걷어냈다. keycloak README 의 베이스 OS 결정 근거도 "기존 3종" 대신 "먼저 들어온 이미지들"로 바꿨다 — 근거의 내용은 그대로다. ## 현황 서술 정정 - build-image.yml 주석이 "아직 도입된 자체 빌드 이미지가 없다(images/ 가 비어 있음)" 로 남아 있었다. 이 레포 CI 에서 빌드→검증→게이트→push→카탈로그 브랜치 push 까지 실제로 검증된 상태다. - MEMORY.md: cve-exceptions.json 첫 예외 등록, 베이스 OS 정책 확정, PR 자동 생성이 조직 정책으로 불가하다는 실측(run 30882785612)을 반영했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f9a2d8400f |
add-keycloakx: keycloak 자체 빌드 하드닝 이미지 추가 (차단 CVE 17건 → 0건)
PR #18 이 카탈로그에 넣는 quay.io/keycloak/keycloak:26.6.4 가 게이트에서 차단 17건(실효 HIGH 17 / CRITICAL 0)이었다. sbom.yml 이 warn-only 라 PR 은 통과했지만 실제로는 게이트 실패 상태로 카탈로그에 들어간다. ## 상위 태그·베이스 OS 교체를 먼저 검토한 결과 차단 17건 중 12건이 배포본에 함께 실린 jar 다. keycloak 26.6.4 와 최신 26.7.1 의 quarkus.version 이 둘 다 3.33.2.1 이고 그 BOM 이 netty 4.1.135.Final / jackson-bom 2.21.2 를 고정한다(keycloak pom.xml 두 태그 + quarkus BOM 실측). 필요한 수정 버전은 netty 4.1.136.Final, jackson 2.21.4 라 **상위 태그로도 풀리지 않고**, CVE 가 OS 패키지가 아니라 jar 자체라 **베이스 OS 교체도 통하지 않는다.** jar 를 직접 교체하는 자체 빌드가 유일한 수단이다 — etcd 이미지의 `go.work replace golang.org/x/text` 와 같은 성격의 의존성 override. ## images/keycloak/ 업스트림 quarkus/container/Dockerfile 을 기준으로 하되 셋이 다르다. 1. 런타임 rootfs 가 SUSE BCI. bci-micro 파일시스템을 **씨앗으로 깔고** 그 위에 zypper --installroot 로 설치한다. 업스트림 ubi-null.sh 처럼 별도 installroot 를 micro 위에 덮으면 micro 의 rpmdb 가 가려져 micro 자체 패키지가 SBOM 에서 통째로 사라진다 — CVE 가 주는 게 아니라 스캔 사각지대가 생긴다. 씨앗 방식으로 OS 패키지 65종이 정상적으로 잡히는 것을 SBOM 으로 확인했다. 2. 취약 jar 오버레이(overlay-jars.sh). netty 17종 → 4.1.136.Final, jackson core/databind → 2.21.4, pgjdbc → 42.7.12. Quarkus fast-jar 의 클래스패스가 파일명을 그대로 참조하므로 **파일명은 유지하고 내용만** 바꾸고 sha1 로 검증한다. trivy 는 jar 내부 메타데이터를 읽으므로 SBOM 에 새 버전이 정확히 잡힌다. 3. bin/client 제거. keycloak-admin-cli 가 jackson 을 shade 로 품은 uber-jar 라 교체가 불가능하다. 서버 JVM 이 로드하지 않는 독립 CLI 라 제거했다 — 업스트림 대비 유일한 기능적 차이이며 CUSTOM-README 에 대안을 적었다. 버전은 26.7.1 로 올렸다. 26.6.4 는 26.7.1(및 26.6.5)에서만 패치된 keycloak-services HIGH 5건(CVE-2026-16102/16442/16443/15572/15573)에 취약하다. 차트(keycloakx 7.2.2)는 최신이고 그대로 둔다 — appVersion 26.6.4 는 codecentric 의 릴리스 캐던스 지연이다. ## 베이스 OS 정책 확정 (image-authoring.md 원칙 2 미결 해소) SUSE BCI 로 통일하되 **버전은 이미지마다 실측해서 고른다.** BCI 16.0 이 나와 있지만 SLE_BCI 의 java-21-openjdk-headless 가 15.7 은 21.0.12, 16.0 은 21.0.11 이라 최신 베이스로 가면 CVE-2026-41254·CVE-2026-47063 이 오히려 남는다. bci-micro 에 sed·grep·find 가 셋 다 없다는 것과 SLE 패키지명 차이(tzdata→timezone 등)도 함께 기록했다. ## 실측 결과 로컬 빌드(linux/amd64) → verify.sh → SBOM → 전 심각도 스캔 → 게이트: 차단 17건 → **0건** (커버리지 자가진단 ok, OS=sles 15.7) 남은 1건 CVE-2025-59250 은 예외 등록했다 — 트리비가 같은 mssql-jdbc jar 하나로 컴포넌트를 둘 만들어(pom.properties 의 13.2.1.jre11 / 파일명의 13.2.1) 접미사가 잘린 쪽이 매칭된 파싱 오탐이다. 설치본은 FixedVersion 목록에 있는 13.2.1.jre11 이다. dev 클러스터 격리 네임스페이스(kc-test-build)에 cnpg-cluster + keycloakx 로 실배포 검증: Pod Running, jdbc-postgresql 연결, liquibase 스키마 생성, admin 부트스트랩 (KC-SERVICES0077), apisix ingress 경유 OIDC discovery 200 / admin 토큰 발급 / realm·client 생성(201) 까지 확인. 정리 시 Longhorn Volume 까지 삭제했다. ## custom-values.yaml ingress 수정 path 가 exact "/" 였다. apisix 에서는 루트만 매치되어 /realms/*, /admin/* 이 전부 404 가 난다 — airflow·superset·mlflow·lakekeeper 에서 이미 실측된 문제로 카탈로그가 regex 방식으로 통일돼 있다. path: /.* + k8s.apisix.apache.org/use-regex 로 맞췄고, 배포 검증에서 이 경로들이 실제로 뜨는 것을 확인했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
01ec67ae1f |
파이프라인 실행 절차를 Claude Code Skill 3종으로 등록
세 파이프라인의 실행법이 문서에만 있어 "그 문서를 읽어야만" 알 수 있었다. 관련 작업 시 자동 로드되도록 Skill 로 등록한다. - catalog-update-pipeline: agent/update_catalog 파이프라인. CATALOG_ROOT 가 개인 로컬 경로로 하드코딩돼 있어 오버라이드 필수라는 점, create_pr 단계가 주석 처리돼 PR 이 생성되지 않는다는 점을 명시했다. - sbom-cve-gate: SBOM·스캔·게이트. CoverageProbe(ok/none/n-a) 해석과 게이트가 현재 warn-only 라는 점을 명시했다. - self-build-image: 자체 빌드. 오케스트레이터는 하나뿐이라는 원칙과 전역 ARG 선언, 게이트 PASS 가 동작을 보장하지 않는다는 점을 명시했다. Skill 은 절차 본문을 복제하지 않고 권위 문서를 가리킨다 — 문서가 단일 출처이고 Skill 은 실행 계약과 함정·현재 상태만 담는다. 함께 고친 stale 문서(image-authoring.md): - "images/ 디렉토리 자체가 없다" → 실제로는 이미지 3종이 있고 push 까지 됐다 - "자동화된 배포 테스트 절차는 아직 없다" → deploy-test-procedure.md 와 전용 스크립트가 있다 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6597059a15 |
scan-sbom.sh: CoverageProbe 커버리지 자가진단 이식 + 관련 파이프라인 버그 수정
security-catalog 프로젝트에서 자체 빌드 이미지 3종을 실제로 로컬 빌드·게이트 검증하는 과정에서 발견한 문제들: - extract-helm-images.sh 가 `image:` 필드만 잡고 `imageName:`(CNPG Cluster CRD 관례) 은 놓쳐, cnpg-cluster 차트의 이미지가 SBOM·스캔·게이트 어디에도 안 나타났다. - patch-catalog-tag.py 의 split 포맷 패처가 registry 필드가 따로 없고 repository 에 registry+repo 를 합쳐 쓰는 차트(cloudnative-pg 오퍼레이터, 업스트림 템플릿이 image.registry 를 아예 참조하지 않음)에서 tag 만 조용히 갱신하고 repository 는 그대로 남겨 깨진 참조를 만들 수 있었다. - CoverageProbe(센티널 패키지 주입 재스캔으로 "0건"과 "측정 안 됨"을 구분)가 이식되지 않아, 전 심각도 0건인 자체 빌드 이미지가 실제로는 깨끗한데도 게이트가 "데이터 커버리지 이상"으로 오탐 처리했다 — security-catalog 의 scan-sbom.sh 를 이식해 해소. cve-gate.py 는 이미 이 키를 읽도록 구현돼 있어 소비 쪽 변경은 없다. rpm(SUSE)·deb(Debian)·apk(Alpine) 세 센티널 경로와 병렬 스캔 회귀를 로컬에서 확인했고, 이식 후 cloudnative-pg·cnpg-postgresql 게이트가 실제로 FAIL→PASS 로 바뀌는 것도 확인했다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
a168a3c7e6 |
docs(image-authoring): CoverageProbe cov= 확인 지시 제거 + Dockerfile 경로 주석 정정
scan-sbom.sh 에 커버리지 자가진단이 없는데도 체크리스트가 cov= 확인을 지시해 같은 문서 104-106줄과 모순됐다. 실제 동작(findings 0건 시 보수적 실패)과 판단 방법으로 교체. Dockerfile 헤더 주석의 doc/scripts/ 경로도 scripts/pipeline/ 로 갱신(경로 이전 시 누락됨). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
844c567d0d |
자체 빌드 이미지 프레임워크 도입 (실사용 이미지 없음)
security-catalog 에서 포팅: build-hardened-image.sh, patch-catalog-tag.py, build-image.yml(REGISTRY_HOST=docker.io/paasup). images/ 는 아직 비어있다 — 베이스 OS 정책 미결 등은 .claude/image-authoring.md, MEMORY.md 참고. |