Files
service-catalog/images/apisix/source.Dockerfile
T
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

269 lines
16 KiB
Docker

# apisix — 업스트림 apache/apisix:3.17.0-debian 를 대체하는 자체 빌드.
#
# 업스트림은 vanilla OpenResty 가 아니라 "APISIX-Runtime" 이라는 커스텀 컴파일 nginx다
# (openssl3·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` 로 실측). 업스트림 빌드
# 스크립트(https://github.com/api7/apisix-build-tools, 태그 apisix-runtime/1.3.6 —
# `openresty -V` 의 APISIX_RUNTIME_VER=1.3.6 과 실측 일치)가 배포판 무관하게 소스에서
# 컴파일하는 구조라 SUSE BCI 로 옮길 수 있었다 — Debian/RHEL 전용 사전빌드 RPM/DEB 를
# SUSE 에 강제 설치하는 건 ABI 가 안 맞아 불가능하다(dockerfiles/Dockerfile.apisix.rpm
# 등 참고, 둘 다 UBI9/Ubuntu 전용).
#
# 왜 자체 빌드인가 — apache/apisix:3.17.0-debian(2026-08-06 배포, 확인 시점 최신 태그)이
# 게이트 차단 26건. 이미 최신 태그라 상위 태그 교체 불가, Debian 베이스 OS 패키지
# (libc/perl/pcre 등) CVE 라 이번엔 정적 링크 바이너리가 아니라 진짜 베이스 OS 문제다.
#
# 구성(3단계):
# 1) runtime — OpenSSL 3.4.1·zlib·pcre 를 $OR_PREFIX/{openssl3,zlib,pcre} 에 직접
# 빌드하고, openresty-${OR_VER} 소스에 위 커스텀 모듈들을 --add-module 로 붙여
# 컴파일한다(업스트림 빌드 스크립트 그대로 재현, 버전 전부 pinned).
# 2) apisix — runtime 위에 APISIX 본체(Lua 애플리케이션 + 일부 C 확장 Lua rock)를
# luarocks 로 설치한다. 이 단계에서 필요한 pcre2·openldap·libxml2·libxslt(lua-resty-
# saml 이 xmlsec1 바인딩에 씀)·zlib devel 은 SUSE 표준 패키지로 설치한다(업스트림처럼
# openresty 전용 -devel 패키지가 아니라 시스템 표준 -devel — 실제 배포판 표준
# pcre/pcre2/libxml2/libxslt 헤더로 컴파일해도 무방한 부분이라 문제 없다).
# 3) final — bci-base(런타임 공유 라이브러리 필요: libxml2·libxslt·openldap2 등을
# 정적이 아니라 동적 링크로 쓰는 Lua rock 이 있어 bci-micro 로는 부족하다 — 실측
# 후 bci-micro 로 축소 검토, 1차는 정확성 우선).
#
# 업스트림과 다르게 하는 부분
# - 베이스: Debian → SUSE BCI(.claude/image-authoring.md 원칙 2).
# - Rust 툴체인: rustup 으로 설치(wasm-nginx-module 의 wasmtime-c-api, APISIX 일부
# rock 컴파일에 필요 — 업스트림도 동일하게 rustup 을 쓴다, 배포판 무관 설치라 차이 없음).
# - 그 외 애플리케이션 코드·모듈 버전은 100% 동일(diff 최소화 원칙).
# syntax=docker/dockerfile:1
ARG BUILDER_BASE=registry.suse.com/bci/bci-base:15.7
ARG RUNTIME_BASE=registry.suse.com/bci/bci-base:15.7
# =============================================================================
# 1) runtime — OpenSSL/zlib/pcre + OpenResty(APISIX-Runtime 커스텀 모듈 전체)
# =============================================================================
FROM ${BUILDER_BASE} AS runtime
ARG OPENRESTY_VERSION=1.29.2.4
ARG OPENSSL_VERSION=3.4.1
ARG APISIX_NGINX_MODULE_VER=1.19.5
ARG WASM_NGINX_MODULE_VER=0.7.0
ARG LUA_VAR_NGINX_MODULE_VER=v0.5.3
ARG LUA_RESTY_EVENTS_VER=0.2.0
ARG NGX_MULTI_UPSTREAM_MODULE_VER=1.3.3
ARG MOD_DUBBO_VER=1.0.2
ARG APISIX_RUNTIME_VER=1.3.6
ENV OR_PREFIX=/usr/local/openresty
ENV PATH=/root/.cargo/bin:$PATH
RUN zypper -n refresh && zypper -n install -y \
gcc gcc-c++ make patch git wget curl tar gzip xz which findutils perl unzip gawk
# 업스트림처럼 cpanm 으로 IPC::Cmd 를 보강한다(OpenSSL 3.x Configure 가 요구) — SUSE 는
# perl-App-cpanminus 패키지가 없어 공식 부트스트랩으로 설치.
RUN curl -L https://cpanmin.us | perl - App::cpanminus \
&& cpanm --notest IPC::Cmd
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
WORKDIR /tmp/build
# --- zlib 먼저 (업스트림 openresty-zlib-devel 대신 표준 소스 빌드 — 같은 산출 경로).
# OpenSSL 의 `zlib` config 옵션이 컴파일 타임에 zlib.h 를 요구하므로 OpenSSL 보다
# 먼저 빌드해야 한다 — 실제로 순서를 반대로 뒀다가 `zlib.h: No such file` 로 실패한
# 것을 실측했다(2026-08-11).
ARG ZLIB_VERSION=1.3.1
RUN wget "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz" \
&& tar xzf "zlib-${ZLIB_VERSION}.tar.gz" \
&& cd "zlib-${ZLIB_VERSION}" \
&& ./configure --prefix="${OR_PREFIX}/zlib" \
&& make -j"$(nproc)" \
&& make install
# --- pcre (classic PCRE1, nginx/openresty 기본 요구사항 — 업스트림 openresty-pcre-devel
# 과 같은 산출 경로 $OR_PREFIX/pcre 에 설치) ---
ARG PCRE_VERSION=8.45
RUN wget "https://sourceforge.net/projects/pcre/files/pcre/${PCRE_VERSION}/pcre-${PCRE_VERSION}.tar.gz/download" -O "pcre-${PCRE_VERSION}.tar.gz" \
&& tar xzf "pcre-${PCRE_VERSION}.tar.gz" \
&& cd "pcre-${PCRE_VERSION}" \
&& ./configure --prefix="${OR_PREFIX}/pcre" --enable-jit --enable-utf --enable-unicode-properties \
&& make -j"$(nproc)" \
&& make install
# --- OpenSSL 3.4.1 (업스트림과 동일 버전) — zlib 헤더 경로를 명시로 넘긴다 ---
RUN wget --no-check-certificate "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" \
&& tar xzf "openssl-${OPENSSL_VERSION}.tar.gz" \
&& cd "openssl-${OPENSSL_VERSION}" \
&& CFLAGS="-I${OR_PREFIX}/zlib/include" LDFLAGS="-L${OR_PREFIX}/zlib/lib -Wl,-rpath,${OR_PREFIX}/zlib/lib" \
./config shared zlib enable-camellia enable-seed enable-rfc3779 \
enable-cms enable-md2 enable-rc5 enable-weak-ssl-ciphers \
--prefix="${OR_PREFIX}/openssl3" --libdir=lib \
--with-zlib-lib="${OR_PREFIX}/zlib/lib" --with-zlib-include="${OR_PREFIX}/zlib/include" \
&& make -j"$(nproc)" \
&& make install_sw install_ssldirs
# --- OpenResty 소스 + 커스텀 모듈 (업스트림 build-apisix-runtime.sh 재현) ---
RUN wget --no-check-certificate "https://openresty.org/download/openresty-${OPENRESTY_VERSION}.tar.gz" \
&& tar zxf "openresty-${OPENRESTY_VERSION}.tar.gz"
RUN git clone --depth=1 -b "${LUA_RESTY_EVENTS_VER}" https://github.com/Kong/lua-resty-events.git "lua-resty-events-${LUA_RESTY_EVENTS_VER}" \
&& git clone --depth=1 -b "${NGX_MULTI_UPSTREAM_MODULE_VER}" https://github.com/api7/ngx_multi_upstream_module.git "ngx_multi_upstream_module-${NGX_MULTI_UPSTREAM_MODULE_VER}" \
&& git clone --depth=1 -b "${MOD_DUBBO_VER}" https://github.com/api7/mod_dubbo.git "mod_dubbo-${MOD_DUBBO_VER}" \
&& git clone --depth=1 -b "${APISIX_NGINX_MODULE_VER}" -- https://github.com/api7/apisix-nginx-module.git "apisix-nginx-module-${APISIX_NGINX_MODULE_VER}" \
&& git clone --depth=1 -b "${WASM_NGINX_MODULE_VER}" https://github.com/api7/wasm-nginx-module.git "wasm-nginx-module-${WASM_NGINX_MODULE_VER}" \
&& git clone --depth=1 -b "${LUA_VAR_NGINX_MODULE_VER}" https://github.com/api7/lua-var-nginx-module "lua-var-nginx-module-${LUA_VAR_NGINX_MODULE_VER}"
RUN cd "ngx_multi_upstream_module-${NGX_MULTI_UPSTREAM_MODULE_VER}" && ./patch.sh "../openresty-${OPENRESTY_VERSION}" && cd .. \
&& cd "apisix-nginx-module-${APISIX_NGINX_MODULE_VER}/patch" && ./patch.sh "../../openresty-${OPENRESTY_VERSION}" && cd ../.. \
&& cd "wasm-nginx-module-${WASM_NGINX_MODULE_VER}" && ./install-wasmtime.sh && cd ..
RUN cd "openresty-${OPENRESTY_VERSION}" \
&& or_limit_ver=0.09 \
&& rm -rf "bundle/lua-resty-limit-traffic-${or_limit_ver}" \
&& limit_ver=1.2.0 \
&& wget "https://github.com/api7/lua-resty-limit-traffic/archive/refs/tags/v${limit_ver}.tar.gz" -O "lua-resty-limit-traffic-${limit_ver}.tar.gz" \
&& tar xzf "lua-resty-limit-traffic-${limit_ver}.tar.gz" \
&& mv "lua-resty-limit-traffic-${limit_ver}" "bundle/lua-resty-limit-traffic-${or_limit_ver}"
RUN cd "openresty-${OPENRESTY_VERSION}" \
&& zlib_prefix="${OR_PREFIX}/zlib" pcre_prefix="${OR_PREFIX}/pcre" openssl_prefix="${OR_PREFIX}/openssl3" ; \
./configure --prefix="${OR_PREFIX}" \
--with-cc-opt="-DAPISIX_RUNTIME_VER=${APISIX_RUNTIME_VER} -DNGX_LUA_ABORT_AT_PANIC -I${zlib_prefix}/include -I${pcre_prefix}/include -I${openssl_prefix}/include" \
--with-ld-opt="-Wl,-rpath,${OR_PREFIX}/wasmtime-c-api/lib -L${zlib_prefix}/lib -L${pcre_prefix}/lib -L${openssl_prefix}/lib -Wl,-rpath,${zlib_prefix}/lib:${pcre_prefix}/lib:${openssl_prefix}/lib" \
--add-module="../mod_dubbo-${MOD_DUBBO_VER}" \
--add-module="../ngx_multi_upstream_module-${NGX_MULTI_UPSTREAM_MODULE_VER}" \
--add-module="../apisix-nginx-module-${APISIX_NGINX_MODULE_VER}" \
--add-module="../apisix-nginx-module-${APISIX_NGINX_MODULE_VER}/src/stream" \
--add-module="../apisix-nginx-module-${APISIX_NGINX_MODULE_VER}/src/meta" \
--add-module="../wasm-nginx-module-${WASM_NGINX_MODULE_VER}" \
--add-module="../lua-var-nginx-module-${LUA_VAR_NGINX_MODULE_VER}" \
--add-module="../lua-resty-events-${LUA_RESTY_EVENTS_VER}" \
--with-poll_module --with-pcre-jit \
--without-http_rds_json_module --without-http_rds_csv_module --without-lua_rds_parser \
--with-stream --with-stream_ssl_module --with-stream_ssl_preread_module \
--with-http_v2_module --with-http_v3_module \
--without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module \
--with-http_stub_status_module --with-http_realip_module --with-http_addition_module \
--with-http_auth_request_module --with-http_secure_link_module --with-http_random_index_module \
--with-http_gzip_static_module --with-http_sub_module --with-http_dav_module \
--with-http_flv_module --with-http_mp4_module --with-http_gunzip_module \
--with-threads --with-compat \
--with-luajit-xcflags="-DLUAJIT_NUMMODE=2 -DLUAJIT_ENABLE_LUA52COMPAT" \
-j"$(nproc)" \
&& make -j"$(nproc)" \
&& make install
RUN cd "lua-resty-events-${LUA_RESTY_EVENTS_VER}" \
&& install -d "${OR_PREFIX}/lualib/resty/events/" \
&& install -m 664 lualib/resty/events/*.lua "${OR_PREFIX}/lualib/resty/events/" \
&& install -d "${OR_PREFIX}/lualib/resty/events/compat/" \
&& install -m 644 lualib/resty/events/compat/*.lua "${OR_PREFIX}/lualib/resty/events/compat/"
RUN cd "apisix-nginx-module-${APISIX_NGINX_MODULE_VER}" && OPENRESTY_PREFIX="${OR_PREFIX}" make install \
&& cd "../wasm-nginx-module-${WASM_NGINX_MODULE_VER}" && OPENRESTY_PREFIX="${OR_PREFIX}" make install
# =============================================================================
# 2) apisix — APISIX 본체(Lua 애플리케이션) 설치
# =============================================================================
FROM runtime AS apisix-app
ARG APISIX_VERSION=3.17.0
ENV PATH=$PATH:${OR_PREFIX}/luajit/bin:${OR_PREFIX}/nginx/sbin:${OR_PREFIX}/bin
# lua-resty-saml(xmlsec1 바인딩)·lyaml·기타 rock 컴파일에 필요. 업스트림은 openresty
# 전용 -devel 대신 시스템 표준 pcre/pcre2/openldap/libxml2/libxslt/zlib/yaml -devel 를
# 쓴다(RHEL 기준 utils/install-dependencies.sh 참고) — SUSE 표준 패키지로 대응한다.
# sudo 는 utils/linux-install-luarocks.sh 내부에서 그대로 호출한다(스크립트 수정 없이
# 재사용하려고 패키지로 설치 — 이 스테이지는 어차피 root 로 실행된다).
RUN zypper -n install -y \
pcre-devel pcre2-devel openldap2-devel \
libxml2-devel libxslt-devel zlib-devel libyaml-devel \
diffutils cmake automake autoconf libtool gawk readline-devel sudo
RUN wget https://raw.githubusercontent.com/apache/apisix/${APISIX_VERSION}/utils/linux-install-luarocks.sh \
&& chmod +x linux-install-luarocks.sh \
&& ./linux-install-luarocks.sh
WORKDIR /apisix
RUN git clone --depth=1 -b "${APISIX_VERSION}" https://github.com/apache/apisix.git .
# apisix-master-0.rockspec 는 저장소 루트에 있다(2026-08-11 태그 3.17.0 기준 실측).
# `luarocks make` 는 rockspec 의 source.url(원격 tarball)을 쓰지 않고 현재 디렉토리를
# 그대로 빌드 대상으로 삼는다 — git clone 만으로 충분하고 apiseven 빌드처럼 source.url
# 을 로컬 경로로 sed 패치할 필요가 없다.
#
# lua-resty-saml(luarocks 가 자동으로 받는 의존 rock)의 xmlsec 바인딩(src/saml.c)이
# libxml2 2.12(SUSE 표준 -devel 버전) 의 `xmlSetStructuredErrorFunc` 시그니처(콜백 인자에
# const 추가됨)와 안 맞아 `-Werror=incompatible-pointer-types` 로 빌드 실패한다(2026-08-11
# 실측) — rock 자체의 Makefile 이 `CFLAGS_ALL := ... -Werror ...` 를 하드코딩해 환경변수
# CFLAGS 로는 못 끈다. rock 소스를 포크하지 않고, 이 스테이지 안에서만 쓰는 gcc 래퍼로
# 그 진단 하나만 경고로 낮춘다(뒤에 오는 -Wno-error=가 앞의 -Werror 를 그 진단에 한해 이긴다).
RUN mv /usr/bin/gcc /usr/bin/gcc.real \
&& printf '#!/bin/sh\nexec /usr/bin/gcc.real -Wno-error=incompatible-pointer-types "$@"\n' > /usr/bin/gcc \
&& chmod +x /usr/bin/gcc
RUN luarocks make ./apisix-master-0.rockspec --tree=/usr/local/apisix/deps --local
# 이후 단계에 영향 주지 않도록 원복한다 — 위 gcc 래퍼는 lua-resty-saml 빌드에만 쓴다.
RUN mv /usr/bin/gcc.real /usr/bin/gcc
# apisix-build-tools 의 install_apisix() 를 재현한다(utils/install-common.sh) — luarocks
# 가 rockspec 의 build.install.bin 으로 설치한 bin/apisix 래퍼와, deps 트리 안에 설치된
# apisix Lua 패키지(share/lua/5.1/apisix)를 /usr/local/apisix 구조로 재배치한다.
#
# ui/ 는 apache/apisix 저장소 자체엔 없다(2026-08-11 3.17.0 태그 실측) — apiseven 의
# 패키징 파이프라인이 별도로 admin 대시보드 프론트엔드를 빌드해 끼워 넣는 단계라
# (Node.js/yarn 빌드, Dockerfile.package.apisix), 소스만 git clone 해서는 재현할 수
# 없다. 우리 apisix 카탈로그 배포는 이 내장 UI 를 쓰지 않는다(custom-values.yaml 에
# 관련 설정 없음) — 없으면 빈 디렉터리만 만들고 건너뛴다(있으면 그대로 복사).
RUN mkdir -p /usr/local/apisix \
&& cp -r conf /usr/local/apisix/conf \
&& { cp -r ui /usr/local/apisix/ui 2>/dev/null || mkdir -p /usr/local/apisix/ui; } \
&& install -m 755 bin/apisix /usr/local/apisix/apisix-cli-bin \
&& mv /usr/local/apisix/deps/share/lua/5.1/apisix /usr/local/apisix/apisix \
&& sed -i '1i package.path = "/usr/local/apisix/deps/share/lua/5.1/?/init.lua;" .. package.path' \
/usr/local/apisix/apisix/cli/apisix.lua
# =============================================================================
# 3) final
# =============================================================================
FROM ${RUNTIME_BASE} AS final
# 실제 SUSE 공유 라이브러리 패키지명은 soname 이 붙는다(libxml2/libldap 등은 이미
# bci-base 기본 설치에 있어 명시 안 해도 되지만, 명확성을 위해 실측한 이름 그대로 적는다
# — 2026-08-11 zypper search 로 확인: libxml2-2, libpcre2-8-0, libldap-2_4-2 는 기본
# 포함, libpcre1·libxslt1·libyaml-0-2 만 추가 설치가 필요했다).
# gawk 는 라이브러리가 아니라 /usr/bin/apisix 래퍼 스크립트 자체가 openresty 버전
# 파싱에 awk 를 쓰기 때문에 필요하다(2026-08-11 실측: "awk: command not found").
RUN zypper -n install -y libpcre1 libxslt1 libyaml-0-2 libxml2-2 libpcre2-8-0 gawk \
&& zypper -n clean --all
COPY --from=apisix-app /usr/local/openresty /usr/local/openresty
COPY --from=apisix-app /usr/local/apisix /usr/local/apisix
COPY --from=apisix-app /usr/local/apisix/apisix-cli-bin /usr/bin/apisix
ENV PATH=$PATH:/usr/local/openresty/luajit/bin:/usr/local/openresty/nginx/sbin:/usr/local/openresty/bin
RUN chmod 755 /usr/bin/apisix \
&& rm -f /usr/local/apisix/apisix-cli-bin \
&& mkdir -p /usr/local/apisix/logs /usr/local/apisix/conf/cert \
&& groupadd --system --gid 636 apisix \
&& useradd --system --gid apisix --no-create-home --shell /usr/sbin/nologin --uid 636 apisix \
&& chown -R apisix:0 /usr/local/apisix \
&& chmod -R g=u /usr/local/apisix \
&& ln -sf /dev/stdout /usr/local/apisix/logs/access.log \
&& ln -sf /dev/stderr /usr/local/apisix/logs/error.log
# keycloak-authz 커스텀 플러그인 (paasup/dataup 레포의 dockerfile 과 동일한 오버레이)
COPY keycloak-authz.lua /usr/local/apisix/apisix/plugins/keycloak-authz.lua
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod 755 /docker-entrypoint.sh
WORKDIR /usr/local/apisix
USER apisix
EXPOSE 9080 9443
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["docker-start"]