18044210f6
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>
130 lines
6.9 KiB
Markdown
130 lines
6.9 KiB
Markdown
# 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단계다:
|
|
|
|
```dockerfile
|
|
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.7`
|
|
+ `zypper 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)이 이 자체 빌드를 유지하는 것보다 항상 우선이다.
|
|
|
|
## 빌드
|
|
|
|
```sh
|
|
# 로컬 빌드 (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 자체 빌드는 이미 이 절차로 배포 검증
|
|
완료됨 — 같은 방식으로 진행).
|