etcd(bitnamilegacy 동결 미러) → etcd.enabled=false + 카탈로그 자체 etcd 차트를 externalEtcd 기본값으로 연결. adc·apisix-ingress-controller·apisix(paasup/apisix) 세 이미지는 SUSE BCI 자체 빌드로 교체 — 전부 벤더 등급만으로는 안 보이던 벤더 하향 등급 CVE(NVD 재평가 시 드러남)가 원인이었다. - images/apisix-ingress-controller: 정적 링크 Go 모듈 취약 버전만 강제 업그레이드 - images/apisix: APISIX-Runtime(WASM·dubbo 등 커스텀 모듈 포함) 전체를 SUSE BCI 위에서 소스로 재현, keycloak-authz 플러그인 오버레이 - images/adc: 업스트림 빌더 스테이지는 그대로 두고 distroless 최종 베이스만 SUSE BCI+nodejs24 로 교체 scripts/build/patch-catalog-tag.py 의 TAG_BLOCK 이 점 구분 중첩 경로를 지원하도록 확장(apisix 서브차트 alias 때문에 필요). 세 이미지 모두 게이트 PASS(실효 CRITICAL/HIGH 0/0)와 배포 검증(테스트 클러스터)을 마쳤다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
adc — 자체 빌드
adc(APISIX/API7 관리 CLI, apisix-ingress-controller 의 사이드카) 를 업스트림 소스에서
SUSE BCI 위에 직접 빌드한다. manifests/helm/apisix/2.16.0/custom-values.yaml의
ingress-controller.deployment.adcContainer.image.repository/tag가 이 산출물을
가리킨다.
자체 빌드는 대응 우선순위 3번이다. 상위 태그 교체·베이스 OS 교체로 목표를 만족할 수 있으면 그 쪽을 쓴다. 자체 빌드는 업스트림 서명·provenance·SBOM attestation 을 잃고 재빌드 책임을 지는 선택이다.
왜 자체 빌드하나 — "태그 교체로 끝난 줄 알았는데 아니었다"
apisix 카탈로그 CVE 조치 중 ghcr.io/api7/adc를 0.27.1 → 0.29.0(확인 시점 최신 태그)
으로 올렸다. 업스트림 0.29.0 릴리스 노트: "distroless 이미지로 전환해 베이스 이미지
취약점을 0으로 줄였다" — 실제로 trivy image 원시 스캔은 CRITICAL/HIGH 0/0 이 맞다.
하지만 이건 벤더 등급만 본 결과다. 카탈로그 게이트(scripts/pipeline/cve-gate.py)
는 max(벤더, NVD)로 재평가하는데, 이걸로 다시 돌리면 아래 4건이 실효
CRITICAL/HIGH 로 여전히 차단한다(2026-08-12 실측):
| CVE | 실효 등급 | 벤더 | NVD | 패키지 | status |
|---|---|---|---|---|---|
| CVE-2019-1010022 | CRITICAL | LOW | CRITICAL (9.8) | libc6 | affected, 수정 없음 |
| CVE-2019-1010023 | HIGH | LOW | HIGH (8.8) | libc6 | affected, 수정 없음 |
| CVE-2018-20796 | HIGH | LOW | HIGH (7.5) | libc6 | affected, 수정 없음 |
| CVE-2019-9192 | HIGH | LOW | HIGH (7.5) | libc6 | affected, 수정 없음 |
전부 2018~2019년 glibc regex/collating 스택오버플로 버그다. Debian 이 "affected,
수정 버전 없음"으로 영구 고정해뒀다 — cnpg-postgresql 자체 빌드 때 겪은 것과 완전히
같은 패턴("Debian 은 unimportant/no-dsa 로 판단해 안 고치는데 다른 배포판은 이미
백포트했음"). 이미 최신 태그(0.29.0)라 상위 태그 교체 레버는 소진됐고, 남은 건 베이스
OS 교체뿐이다.
SUSE 로 바꾸면 실제로 해소되는지 사전 검증
registry.suse.com/bci/bci-base:15.7 + zypper install nodejs24만으로 최소 이미지를
만들어 SBOM·스캔해봤다 — 위 4건이 전혀 안 잡힌다(2026-08-12 실측). SUSE 글리브C
에는 이미 고쳐져 있다는 뜻이다.
업스트림과 다르게 하는 부분 — 딱 한 줄
업스트림 Dockerfile(libs/tools/src/docker/Dockerfile, 태그 v0.29.0)은 2단계다:
FROM node:lts-bookworm-slim AS builder # pnpm+nx 로 main.cjs 하나로 번들
...
FROM gcr.io/distroless/nodejs24-debian13:nonroot
COPY --from=builder /build/dist/apps/cli/main.cjs .
ENTRYPOINT [ "/nodejs/bin/node", "main.cjs" ]
빌더 스테이지(pnpm/nx 빌드)는 정책 대상이 아니라(.claude/image-authoring.md 원칙 2)
업스트림 그대로 재사용한다. 바뀌는 건 최종 스테이지 베이스 한 줄뿐이다:
gcr.io/distroless/nodejs24-debian13:nonroot→registry.suse.com/bci/bci-base:15.7zypper install nodejs24(같은 Node 24 메이저 유지 — SUSE 표준 패키지라 cnpg-postgresql 때 겪은 openresty 전용 -devel 문제도 없다)
- 엔트리포인트 경로
/nodejs/bin/node→/usr/bin/node(zypper 설치 경로, 실측) - non-root 사용자: distroless
:nonroot태그 대신 명시적으로adc시스템 계정 생성
애플리케이션 코드(main.cjs 번들)는 업스트림과 100% 동일하다 — apisix/
apisix-ingress-controller 자체 빌드보다 훨씬 단순한 유형(cnpg-postgresql 과 같은
"OS 패키지 재설치형"에 가깝다 — 소스를 여러 모듈 컴파일하는 게 아니라 빌더 스테이지를
그대로 두고 런타임 베이스만 바꾼다). 실측 빌드 시간도 2분 이내(대부분 pnpm
install+nx build 시간).
소스·버전 관리
| 항목 | 값 |
|---|---|
| 소스 | https://github.com/api7/adc.git |
| pinned commit | source.build.env 의 SOURCE_COMMIT — v0.29.0 태그(lightweight, peeled 커밋 없음)가 가리키는 실제 커밋 |
| 빌더 | 업스트림과 동일한 node:lts-bookworm-slim — 정책 대상 아님(빌더 스테이지) |
| 최종 베이스 | registry.suse.com/bci/bci-base:15.7 + nodejs24 |
SOURCE_COMMIT은 자동 추적하지 않는다. 다른 자체 빌드 이미지와 동일하게, 사람이
업스트림 새 릴리스(또는 위 4건을 이미 해소한 버전)를 보고 source.build.env를
고쳐 PR을 여는 것 자체가 갱신 트리거다.
권장 점검 주기: 카탈로그 게이트가 이 이미지의 차단 CVE를 다시 보고할 때, 또는
api7/adc가 다음 릴리스를 내놓았을 때 — 릴리스가 나오면 그쪽으로 갈아타는 것(대응
우선순위 a)이 이 자체 빌드를 유지하는 것보다 항상 우선이다.
빌드
# 로컬 빌드 (push 없음)
IMAGE=adc BASE_OS=source bash scripts/build/build-hardened-image.sh /tmp/out
# 레지스트리에 push 까지
IMAGE=adc BASE_OS=source REGISTRY=docker.io/paasup \
bash scripts/build/build-hardened-image.sh /tmp/out
수행 순서: 빌드 → 기능 검증(verify.sh) → SBOM → 전 심각도 스캔 → 게이트 판정.
verify.sh는 non-root 실행 여부, --version 출력, --help의 커맨드 목록(dump/diff/
sync)을 확인한다 — adc 는 실제 APISIX/API7 백엔드 접속이 필요한 명령이 대부분이라
그 연동 자체는 이 스모크 범위 밖이다. dev 클러스터 배포 검증
(.claude/deploy-test-procedure.md)이 담당한다.
결과(2026-08-12 실측): 게이트 PASS, 커버리지 ok, 실효 CRITICAL/HIGH 0/0
(원본 4건에서 완전 해소). docker.io/paasup/adc:0.29.0-security-hardened-20260812
로 push 완료.
파일 구성
| 파일 | 역할 |
|---|---|
source.Dockerfile |
빌드 정의 — 빌더 스테이지(업스트림 그대로) + SUSE BCI 최종 스테이지 |
source.build.env |
pinned commit·버전(BUILD_ARGS에 나열한 이름만 --build-arg 로 전달됨) |
verify.sh |
기능 검증 — non-root·버전·--help 커맨드 확인 |
베이스 변종이 하나뿐이라 파일명이 source.* 로 고정돼 있다.
태그
docker.io/paasup/adc:0.29.0-security-hardened-20260812
└ app ┘└ 슬러그 ┘└ 하드닝 ┘└ 빌드일 ┘
아직 안 한 것 — 배포 검증
게이트 PASS·기능 스모크테스트(버전+help)까지만 확인했다. apisix-ingress-controller
와 함께 실제 APISIX/API7 백엔드에 접속해 dump/diff/sync 가 정상 동작하는지는 아직
확인하지 않았다 — .claude/deploy-test-procedure.md 절차로 카탈로그 반영 전
반드시 수행할 것(apisix-ingress-controller 자체 빌드는 이미 이 절차로 배포 검증
완료됨 — 같은 방식으로 진행).