Files
service-catalog/images/apisix
wbsong111 18044210f6 apisix 카탈로그 CVE 게이트 완전 해소 (차단 165건 → 0건)
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>
2026-08-12 11:37:32 +09:00
..

apisix — 자체 빌드

apisix(APISIX-Runtime + APISIX 애플리케이션 + keycloak-authz 커스텀 플러그인) 전체를 업스트림 소스에서 SUSE BCI 위에 직접 컴파일한다. manifests/helm/apisix/2.16.0/custom-values.yamlimage.repository/tag가 이 산출물을 가리킨다.

자체 빌드는 대응 우선순위 3번이다. 상위 태그 교체·베이스 OS 교체로 목표를 만족할 수 있으면 그 쪽을 쓴다. 자체 빌드는 업스트림 서명·provenance·SBOM attestation 을 잃고 재빌드 책임을 지는 선택이다.

왜 자체 빌드하나

apisix 카탈로그 CVE 조치(2026-08-11~12) 중 발견 — 기존엔 별도 프로젝트 (paasup/dataup 레포 experiment/apisix/apisix-plugin)에서 apache/apisix:3.17.0-debian 위에 keycloak-authz 커스텀 플러그인만 얹어 빌드했다. 이 Debian 베이스 자체가 게이트 차단 26건(2026-08-11 실측) — apache/apisix:3.17.0-debian은 확인 시점 기준 이미 최신 업스트림 태그라 상위 태그 교체로는 해소가 안 되고, 전부 Debian 베이스 OS 패키지(libc, perl, pcre 등) CVE라 베이스 OS를 통째로 바꿔야 했다.

업스트림과 다르게 하는 부분 — SUSE 에 이 이미지가 없는 이유

업스트림(apache/apisix:3.17.0-debian)은 vanilla OpenResty 가 아니라 "APISIX-Runtime" 이라는 커스텀 컴파일 nginx다 — OpenSSL 3.4.1·zlib·PCRE 를 직접 빌드해 넣고, apisix-nginx-module·wasm-nginx-module(WASM, wasmtime)·lua-var-nginx-module· lua-resty-events·mod_dubbo·ngx_multi_upstream_module--add-module컴파일 타임에 정적으로 얹는다(2026-08-11 openresty -V 로 실측 — 아래 "소스·버전 관리" 표에 정확한 버전 나열). 이 조합을 배포하는 apiseven 의 빌드 파이프라인 (api7/apisix-build-tools)은 Debian/RHEL(UBI9) 용 사전 빌드 패키지만 만든다 — SUSE 용은 없다.

  • vanilla OpenResty로 대체 불가: openresty.org 는 SLES 15.x 를 공식 지원해 zypper 로 설치할 수 있지만, 그건 위 커스텀 모듈(WASM·mod_dubbo 등)이 하나도 없는 순정 빌드다. --add-module은 컴파일 타임에만 바이너리에 정적으로 박히는 옵션이라 이미 컴파일된 vanilla 바이너리에 나중에 모듈을 추가할 수 없다 — nginx의 "동적 모듈" 메커니즘도 이 모듈들이 그 형태로 배포되지 않아 적용 안 된다. 우리 apisix 배포가 WASM 플러그인과 http-dubbo 관련 기능을 공식적으로 지원해야 한다는 요구사항이 있어 (custom-values.yamlapisix.pluginshttp-dubbo 포함), 모듈을 뺀 축소판으로 타협하지 않고 업스트림과 동일한 모듈 구성을 그대로 재현했다.
  • Debian/RHEL 사전 빌드 RPM/DEB 를 SUSE 에 강제 설치도 불가: 같은 이유로 시도하지 않았다 — glibc/openssl 심볼 버전이 안 맞아 ABI 가 깨질 위험(cnpg-postgresql 때와 달리 이번엔 SUSE 자체 저장소에 대응 패키지가 없어 "같은 배포판 계열 재설치"가 성립 하지 않는다).
  • 그래서 apiseven 의 공식 빌드 스크립트(api7/apisix-build-tools, 태그 apisix-runtime/1.3.6openresty -VAPISIX_RUNTIME_VER=1.3.6과 실측 일치)를 그대로 재현해 SUSE BCI 위에서 소스 컴파일했다. 애플리케이션 코드·모듈 버전은 업스트림과 100% 동일하고, 베이스 OS만 바뀐다(diff 최소화 원칙).

3단계 구성

단계 목표
runtime OpenSSL 3.4.1·zlib·PCRE 를 $OR_PREFIX/{openssl3,zlib,pcre}에 직접 빌드하고, OpenResty 소스에 위 커스텀 모듈 6종을 --add-module로 붙여 컴파일한다. openresty.org 의 SLES 전용 -devel 패키지가 없어 이 경로들을 소스 빌드로 채운다.
apisix-app runtime이 만든 OpenResty/LuaJIT 위에 APISIX 애플리케이션(Lua 코드 + 일부 C 확장 rock)을 luarocks make로 설치한다.
final 위 두 스테이지 산출물만 깨끗한 SUSE BCI 이미지로 옮기고, 런타임에 필요한 공유 라이브러리(libxml2·libxslt·libyaml·pcre·pcre2)만 추가, non-root(apisix, uid 636) 설정, keycloak-authz 플러그인 오버레이까지 마친다.

실측 함정 (2026-08-11/12)

  • 빌드 순서 — zlib 을 OpenSSL 보다 먼저 빌드한다. OpenSSL 의 zlib config 옵션이 컴파일 타임에 zlib.h 를 요구한다 — 반대 순서로 뒀다가 zlib.h: No such file 로 실패했다.
  • lua-resty-saml(luarocks 가 자동으로 받는 의존 rock)이 SUSE 표준 libxml2-devel (2.12.10)의 xmlSetStructuredErrorFunc 시그니처(콜백 인자에 const 추가됨)와 안 맞아 -Werror=incompatible-pointer-types 로 빌드 실패한다. rock 자체의 Makefile 이 -Werror 를 하드코딩해 환경변수 CFLAGS 로는 못 끈다 — apisix-app 스테이지 안에서만 쓰는 gcc 래퍼(-Wno-error=incompatible-pointer-types 추가)로 그 진단 하나만 경고로 낮추고, luarocks make 끝나면 즉시 원복한다(다른 빌드에 영향 안 줌). source.Dockerfile 의 해당 RUN 스텝 주석 참고.
  • ui/(Admin 대시보드 프론트엔드)는 apache/apisix 저장소 자체엔 없다. apiseven 의 패키징 파이프라인이 별도로(Node.js/yarn 빌드) 끼워 넣는 단계라 git clone 만으로는 재현 안 된다 — 우리 배포는 이 UI 를 쓰지 않아(custom-values.yaml 에 관련 설정 없음) 없으면 빈 디렉터리로 대체하고 건너뛴다.
  • /usr/bin/apisix 래퍼 스크립트가 내부적으로 awk 를 쓴다. OpenResty 버전 파싱에 쓰는데, final 스테이지에 gawk 를 안 깔면 "awk: command not found" 로 apisix version부터 실패한다.
  • SUSE 공유 라이브러리 패키지명은 soname 이 붙는다. libxml2/libxslt/pcre 같은 이름 그대로는 없고 libxml2-2·libxslt1·libpcre1·libpcre2-8-0·libyaml-0-2 다 — libxml2-2/libpcre2-8-0/libldap-2_4-2bci-base 에 기본 포함이라 명시 안 해도 되지만, 명확성을 위해 실측한 이름 그대로 적었다.
  • docker build --pull 은 베이스 이미지 digest 가 바뀌면 캐시를 전부 무효화한다. build-hardened-image.sh가 항상 --pull 을 쓰므로(스케줄 재빌드가 최신 베이스를 전제하기 때문), Dockerfile 을 안 고쳤어도 SUSE BCI 태그가 그 사이 갱신되면 전체 재컴파일(약 35분)이 발생할 수 있다 — Dockerfile 반복 수정·테스트는 --pull 없는 순수 docker build 로 하고, 최종 검증에서만 build-hardened-image.sh 를 쓴다.

소스·버전 관리

항목
APISIX 소스 https://github.com/apache/apisix.git (태그 3.17.0)
OpenResty 1.29.2.4
OpenSSL 3.4.1
zlib / PCRE 1.3.1 / 8.45
커스텀 모듈 apisix-nginx-module 1.19.5, wasm-nginx-module 0.7.0, lua-var-nginx-module v0.5.3, lua-resty-events 0.2.0, ngx_multi_upstream_module 1.3.3, mod_dubbo 1.0.2
APISIX_RUNTIME_VER 1.3.6 (apiseven 빌드 스크립트 버전 태그)
최종 베이스 registry.suse.com/bci/bci-base:15.7

전부 source.build.env 에서 관리하며 자동 추적하지 않는다. 다른 자체 빌드 이미지와 동일하게, 사람이 업스트림 새 릴리스(또는 위 CVE 세트를 이미 해소한 버전)를 보고 source.build.env 를 고쳐 PR 을 여는 것 자체가 갱신 트리거다.

권장 점검 주기: 카탈로그 게이트가 이 이미지의 차단 CVE 를 다시 보고할 때, 또는 apache/apisix3.17.1(혹은 그 이상)을 내놓았을 때 — 릴리스가 나오면 그쪽으로 갈아타는 것(대응 우선순위 a)이 이 자체 빌드를 유지하는 것보다 항상 우선이다.

빌드

# 로컬 빌드 (push 없음)
IMAGE=apisix BASE_OS=source bash scripts/build/build-hardened-image.sh /tmp/out

# 레지스트리에 push 까지
IMAGE=apisix BASE_OS=source REGISTRY=docker.io/paasup \
  bash scripts/build/build-hardened-image.sh /tmp/out

수행 순서: 빌드 → 기능 검증(verify.sh) → SBOM → 전 심각도 스캔 → 게이트 판정. verify.shapisix version 출력·nginx 설정 문법(nginx -t)뿐 아니라, standalone YAML 설정으로 실제 nginx worker 를 기동해 APISIX 라우터가 HTTP 요청에 응답하는지 (라우트 없음 → 404)까지 확인한다 — 15개 커스텀 모듈 + ~90개 Lua 플러그인(lua-resty-saml 같은 C 확장 포함) 로딩까지 실제로 거치는 유일한 방법이다. keycloak-authz 커스텀 플러그인은 실제 Keycloak 연동이 필요해 이 스모크 범위 밖이다 — dev 클러스터 배포 검증(.claude/deploy-test-procedure.md)이 담당한다.

결과(2026-08-12 실측): 게이트 PASS, 커버리지 ok, 실효 CRITICAL/HIGH 0/0 (업스트림 26건에서 완전 해소). docker.io/paasup/apisix:3.17.0-security-hardened-20260811 로 push 완료.

파일 구성

파일 역할
source.Dockerfile 빌드 정의 — 3단계(runtime/apisix-app/final)
source.build.env pinned 버전 전체(BUILD_ARGS에 나열한 이름만 --build-arg 로 전달됨)
verify.sh 기능 검증 — 실제 nginx 기동 + HTTP 요청까지
keycloak-authz.lua paasup/dataup 레포(experiment/apisix/apisix-plugin/keycloak-authz/)와 동일한 커스텀 플러그인 — 원본 레포는 그대로 두고 이 카탈로그가 더 이상 참조만 안 한다
docker-entrypoint.sh 업스트림 apache/apisix-docker(utils/docker-entrypoint.sh)와 동일

베이스 변종이 하나뿐이라 파일명이 source.* 로 고정돼 있다.

태그

docker.io/paasup/apisix:3.17.0-security-hardened-20260811
                        └ app  ┘└ 슬러그 ┘└ 하드닝 ┘└ 빌드일 ┘

아직 안 한 것 — 배포 검증

게이트 PASS·기능 스모크테스트(nginx 기동+HTTP 응답)까지만 확인했다. 실제 Keycloak 연동(keycloak-authz 플러그인 동작), etcd 연동(externalEtcd), ingress-controller 와의 연동을 포함한 실제 클러스터 배포 검증은 아직 하지 않았다.claude/deploy-test-procedure.md 절차로 카탈로그 반영 전 반드시 수행할 것.