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>
CI(31148639764)의 trivy 취약점 DB 가 로컬보다 최신이라 로컬 스캔에는 없던
CVE-2026-40983·CVE-2026-40984(io.micrometer:micrometer-core)가 차단으로 잡혔다.
netty·jackson 과 동일한 성격의 번들 jar CVE 라 같은 오버레이 메커니즘으로 처리한다.
micrometer 1.16.3 → 1.16.6. netty 와 같은 이유로 CVE 가 붙은 micrometer-core 만이
아니라 패밀리 전체 4종(commons·core·observation·registry-prometheus-simpleclient)을
함께 올린다 — 아티팩트 간 버전 혼용이 비지원이다.
Dockerfile 의 ARG 선언을 처음에 빠뜨렸는데 overlay-jars.sh 의 `: "${VAR:?}"` 가드가
설계대로 빌드를 세웠다("parameter null or not set"). 조용히 통과하지 않는다.
재빌드 결과(로컬 trivy DB 캐시를 지우고 CI 와 같은 최신 DB 로 재판정):
차단 0건 — 게이트 PASS
docker.io/paasup/keycloak:26.7.1-bci15.7-hardened-20260807 push 완료
태그는 빌드일 기준이라 같은 날 재빌드가 같은 태그를 덮는다(프레임워크 설계대로).
직전에 push 된 것은 micrometer 취약점이 남아 있던 빌드이고 어디에도 소비되지 않았다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>