33e91c0536
업스트림 quay.io/argoproj/argocd 의 차단 CVE 는 대부분 OS 패키지가 아니라 바이너리에
정적 링크된 Go 모듈이라, 상위 태그 교체(이미 최신)로도 베이스 OS 교체로도 잡히지 않는다.
부분 조치는 효과가 없었다 — 같은 stdlib·x/crypto 취약점이 다섯 바이너리(argocd·helm·
kustomize·git-lfs·pebble)에 각각 들어있어 본체만 고치면 거의 줄지 않는다. 업스트림
Dockerfile 이 helm/kustomize/git-lfs 를 hack/install.sh 로 **미리 빌드된 릴리스 바이너리**
로 내려받기 때문에 그 바이너리의 Go 버전을 우리가 통제할 수도 없다. 그래서 넷 다 소스에서
다시 컴파일한다.
- images/argocd/ 신규 (Go 4개 + C 2개 + Node UI — 기존 자체 빌드 중 가장 크다)
- 최종 베이스 ubuntu → SUSE BCI(원칙 2). ubuntu 가 딸려오던 pebble 도 함께 사라진다
- tini·connect-proxy 는 SLE_BCI 에 없어 소스 빌드 — 기능을 빼지 않기 위해
- UI 는 업스트림과 동일하게 node 로 빌드(Go embed 라 생략 불가)
- manifests/helm/argo-cd/10.4.0/custom-values.yaml 이 이 이미지를 가리킨다
global.image 하나로 argocd 바이너리를 쓰는 5개 컴포넌트가 공유한다
재사용 설계 — Dockerfile 에 버전을 박지 않는다
---------------------------------------------
이 이미지는 앞으로도 CVE 조치를 반복해서 받는다. 모듈 목록·패키지 목록을 전부 값으로 빼서
다음 조치에서 바뀌는 것이 source.build.env 한 곳뿐이게 했다. 이전 방식이라면 모듈 하나
추가에 build.env·ARG 선언·go get 목록·BUILD_ARGS 네 곳을 고쳐야 했다.
- scripts/build/suggest-go-upgrades.py 신규 — 스캔 리포트의 FixedVersion 에서
GO_MODULE_UPGRADES / GO_BUILDER_TAG 를 산출한다. 사람이 CVE 를 훑어 최대값을 고르지
않는다. 빌드 시점에 최신을 당기는 방식(go get -u)은 재현성을 버리므로 택하지 않았다 —
값은 제안만 하고 채택은 사람이 커밋한다.
- images/argocd/go-mod-upgrade.sh 신규 — 목록 하나를 네 프로젝트에 재사용한다.
go get 은 의존성에 없는 모듈도 go.mod 에 추가하므로 그래프에 있는 것만 골라 적용한다.
실측 함정 — verify.sh 에 반영했다
--------------------------------
차트의 repo-server init 컨테이너가 `cp --update=none` 을 쓴다. 이 형식은 GNU coreutils
9.3+ 에서만 되는데, BCI 15.7 로 빌드한 이미지가 게이트도 verify.sh 도 통과하고 **배포
시점에** Init:CrashLoopBackOff 로 죽었다. BCI 16.0(coreutils 9.6)으로 올려 해소했고,
verify.sh 가 이 명령과 copyutil 흐름을 직접 재현하도록 해서 다음엔 빌드 단계에서 걸린다.
16.0 의 패키지 가용성과 trivy 커버리지(CoverageProbe=ok, EOSL 아님)도 확인했다.
검증
----
게이트 PASS — 실효 CRITICAL/HIGH 0건, CoverageProbe ok
기능 VERIFY-OK (심볼릭 링크 9개 · 번들 도구 3종 · LFS 필터 · copyutil 흐름)
배포 docker.io/paasup 에 push 후 운영 argocd 교체 — 파드 8종 Running,
admin 로그인, Application 2건 Synced/Healthy, repo-server 렌더링 확인
(서버가 go1.26.6 · helm v4.2.4 로 보고 — 우리 빌드가 맞다)
카탈로그 차단은 574 → 239 건이 됐고(7.7.0 삭제 · dex 비활성 · 이 커밋), 남은 239 건은
전부 동결된 7.8.11 몫이다. 운영에 쓰는 10.4.0 은 0 건이다. 경과·미결은 MEMORY.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
264 lines
13 KiB
Docker
264 lines
13 KiB
Docker
# argocd — 업스트림 소스를 pinned commit 으로 직접 컴파일하고, 번들되는 Go 도구
|
|
# (helm/kustomize/git-lfs)까지 같은 툴체인으로 다시 만든다.
|
|
#
|
|
# 왜 자체 빌드인가
|
|
# ----------------
|
|
# 이 이미지의 차단 CVE 는 대부분 OS 패키지가 아니라 **바이너리에 정적 링크된 Go 모듈**이다.
|
|
# 상위 태그 교체(이미 최신)로도, 베이스 OS 교체(모듈은 바이너리 안에 있다)로도 잡히지 않아
|
|
# 자체 빌드만 남는다.
|
|
#
|
|
# 왜 번들 도구까지 다시 만드는가
|
|
# ------------------------------
|
|
# 업스트림 Dockerfile 은 helm/kustomize/git-lfs 를 hack/install.sh 로 **미리 빌드된 릴리스
|
|
# 바이너리를 내려받아** 넣는다. 그 바이너리의 Go 버전은 각 프로젝트가 정한 것이라 우리가
|
|
# 태그를 올려도 바뀌지 않는다.
|
|
#
|
|
# 그리고 부분 조치는 효과가 없다 — 같은 stdlib·x/crypto 취약점이 다섯 바이너리에 각각
|
|
# 들어있어서, 한 바이너리만 고치면 나머지에 그대로 남는다. 고유 CVE 는 소수이고 대부분이
|
|
# 공유분이다. 전부 다시 만들어야 게이트가 0 이 된다(실측 확인).
|
|
#
|
|
# 재사용 설계 — Dockerfile 에 버전을 박지 않는다
|
|
# ---------------------------------------------
|
|
# 이 이미지는 앞으로도 CVE 조치를 반복해서 받는다. 다음 조치에서 바뀌는 것은 전부
|
|
# source.build.env 값이다:
|
|
#
|
|
# GO_MODULE_UPGRADES 강제 업그레이드할 Go 모듈 목록 (네 프로젝트에 공통 적용)
|
|
# GO_BUILDER_TAG Go 툴체인 (stdlib CVE 해소)
|
|
# *_VERSION 각 구성요소 버전
|
|
# RUNTIME_PACKAGES 최종 이미지에 설치할 SLE 패키지
|
|
#
|
|
# 값은 scripts/build/suggest-go-upgrades.py 가 스캔 리포트에서 산출한다 — 사람이 CVE 를
|
|
# 훑어 최대값을 고르지 않는다. 모듈 목록은 go-mod-upgrade.sh 가 프로젝트마다 "그 프로젝트가
|
|
# 실제로 쓰는 것만" 골라 적용하므로 목록 하나를 네 프로젝트에 그대로 재사용한다.
|
|
#
|
|
# 업스트림과 다르게 하는 부분
|
|
# ---------------------------
|
|
# ubuntu → SUSE BCI(bci-base). 카탈로그는 SUSE BCI 하나만 쓴다
|
|
# (.claude/image-authoring.md 원칙 2). argocd 는 git/gpg/ssh 런타임이 필요해 패키지
|
|
# 매니저가 있는 base 를 쓴다(micro 씨앗 방식의 복잡도를 감수할 이점이 없었다).
|
|
# ubuntu 베이스가 딸려오던 pebble 바이너리도 함께 사라진다.
|
|
# tini·connect-proxy 는 SLE_BCI 에 패키지가 없어 소스에서 빌드한다.
|
|
# helm/kustomize/git-lfs 를 릴리스 바이너리 다운로드 → 소스 컴파일로 교체.
|
|
# UI 는 업스트림과 동일하게 node 로 빌드한다 — Go embed 로 바이너리에 들어가 생략 불가.
|
|
|
|
# syntax=docker/dockerfile:1
|
|
|
|
# FROM 에서 쓰는 ARG 는 반드시 첫 FROM 이전(전역 스코프)에 선언해야 한다 —
|
|
# 스테이지 안에 두면 지역 변수가 되어 이후 FROM 의 이미지명이 빈 값이 된다
|
|
# (.claude/image-authoring.md).
|
|
ARG GO_BUILDER_TAG=1.26.6-trixie
|
|
ARG NODE_BUILDER_TAG=24.14.1
|
|
ARG RUNTIME_BASE=registry.suse.com/bci/bci-base:15.7
|
|
|
|
####################################################################################################
|
|
# UI — 업스트림 argocd-ui 스테이지를 그대로 재현한다. 산출물이 Go embed 로 바이너리에 들어간다.
|
|
####################################################################################################
|
|
FROM --platform=$BUILDPLATFORM docker.io/library/node:${NODE_BUILDER_TAG} AS argocd-ui
|
|
|
|
ARG SOURCE_COMMIT
|
|
ARG APP_VERSION
|
|
ADD https://github.com/argoproj/argo-cd.git#${SOURCE_COMMIT} /src
|
|
|
|
# 업스트림과 동일: corepack 으로 pnpm 을 켜고 lockfile 그대로 설치한다.
|
|
WORKDIR /src/ui
|
|
RUN npm install -g corepack@0.34.6 && corepack enable && pnpm install --frozen-lockfile
|
|
|
|
# UI 번들은 아키텍처 무관이다 — 업스트림 주석대로 TARGETARCH 를 쓰지 않는다.
|
|
ENV ARGO_VERSION=$APP_VERSION
|
|
RUN NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OPTIONS=--max_old_space_size=8192 pnpm build
|
|
|
|
####################################################################################################
|
|
# 번들 Go 도구 — 업스트림이 릴리스 바이너리로 받아오던 것을 소스에서 다시 컴파일한다.
|
|
#
|
|
# 새 툴체인으로 다시 컴파일하면 stdlib 은 해소되지만 각 프로젝트가 go.mod 에 pin 한 모듈은
|
|
# 그대로 낡아 있다 — 도구 쪽 모듈을 빠뜨려 게이트가 실패한 적이 있다(실측). 그래서 argocd
|
|
# 본체와 같은 GO_MODULE_UPGRADES 를 세 도구에도 적용한다.
|
|
####################################################################################################
|
|
FROM --platform=$BUILDPLATFORM golang:${GO_BUILDER_TAG} AS go-tools
|
|
|
|
ARG TARGETARCH
|
|
ARG HELM_VERSION
|
|
ARG KUSTOMIZE_VERSION
|
|
ARG GIT_LFS_VERSION
|
|
ARG GO_MODULE_UPGRADES
|
|
ENV CGO_ENABLED=0 GOOS=linux GO_MODULE_UPGRADES=${GO_MODULE_UPGRADES}
|
|
COPY go-mod-upgrade.sh /usr/local/bin/go-mod-upgrade
|
|
RUN chmod 0755 /usr/local/bin/go-mod-upgrade
|
|
WORKDIR /out
|
|
|
|
# helm — 업스트림 Makefile 의 ldflags(version/gitTreeState)를 재현한다.
|
|
ADD https://github.com/helm/helm.git#v${HELM_VERSION} /src/helm
|
|
RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
|
|
cd /src/helm && go-mod-upgrade && \
|
|
P=helm.sh/helm/v4/internal/version && \
|
|
GOARCH=${TARGETARCH} go build -trimpath \
|
|
-ldflags "-X ${P}.version=v${HELM_VERSION} -X ${P}.gitTreeState=clean" \
|
|
-o /out/helm ./cmd/helm
|
|
|
|
# kustomize — 리포 안의 kustomize/ 서브모듈이 CLI 다.
|
|
ADD https://github.com/kubernetes-sigs/kustomize.git#kustomize/v${KUSTOMIZE_VERSION} /src/kustomize
|
|
RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
|
|
cd /src/kustomize/kustomize && go-mod-upgrade && \
|
|
P=sigs.k8s.io/kustomize/api/provenance && \
|
|
GOARCH=${TARGETARCH} go build -trimpath \
|
|
-ldflags "-X ${P}.version=v${KUSTOMIZE_VERSION}" \
|
|
-o /out/kustomize .
|
|
|
|
# git-lfs — Makefile 이 ldflags 로 버전을 심는다.
|
|
ADD https://github.com/git-lfs/git-lfs.git#v${GIT_LFS_VERSION} /src/git-lfs
|
|
RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
|
|
cd /src/git-lfs && go-mod-upgrade && \
|
|
GOARCH=${TARGETARCH} go build -trimpath \
|
|
-ldflags "-X github.com/git-lfs/git-lfs/v3/config.GitCommit=v${GIT_LFS_VERSION}" \
|
|
-o /out/git-lfs .
|
|
|
|
####################################################################################################
|
|
# argocd 본체 — 업스트림 argocd-build 스테이지 + 취약 모듈 강제 업그레이드
|
|
####################################################################################################
|
|
FROM --platform=$BUILDPLATFORM golang:${GO_BUILDER_TAG} AS argocd-build
|
|
|
|
ARG TARGETOS
|
|
ARG TARGETARCH
|
|
ARG SOURCE_COMMIT
|
|
ARG APP_VERSION
|
|
ARG GO_MODULE_UPGRADES
|
|
ENV GO_MODULE_UPGRADES=${GO_MODULE_UPGRADES}
|
|
COPY go-mod-upgrade.sh /usr/local/bin/go-mod-upgrade
|
|
RUN chmod 0755 /usr/local/bin/go-mod-upgrade
|
|
|
|
WORKDIR /go/src/github.com/argoproj/argo-cd
|
|
ADD https://github.com/argoproj/argo-cd.git#${SOURCE_COMMIT} .
|
|
|
|
RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
|
|
go-mod-upgrade
|
|
|
|
COPY --from=argocd-ui /src/ui/dist/app ./ui/dist/app
|
|
|
|
# 업스트림 Dockerfile 과 동일하게 Makefile 의 argocd-all 타겟을 부른다
|
|
# (단일 `go build -o dist/argocd ./cmd`). 버전 심볼은 Makefile 이 ldflags 로 심는다.
|
|
# BUILD_DATE 를 고정해 빌드 재현성을 확보한다(업스트림은 date 를 그때그때 넣는다).
|
|
RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
|
|
GIT_TAG=v${APP_VERSION} \
|
|
GIT_COMMIT=${SOURCE_COMMIT} \
|
|
GIT_TREE_STATE=clean \
|
|
BUILD_DATE=1970-01-01T00:00:00Z \
|
|
GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
|
make argocd-all
|
|
|
|
####################################################################################################
|
|
# C 도구 — SLE_BCI 에 패키지가 없어 소스에서 빌드한다(2026-08-19 zypper 실측)
|
|
####################################################################################################
|
|
FROM ${RUNTIME_BASE} AS c-builder
|
|
|
|
ARG TINI_VERSION
|
|
ARG SSH_CONNECT_VERSION
|
|
ARG BUILDER_PACKAGES
|
|
# SLE_BCI 미러가 간헐적으로 끊긴다(실측: curl error 56 / SSL unexpected eof). 재시도한다.
|
|
RUN for i in 1 2 3 4 5; do \
|
|
zypper --non-interactive --gpg-auto-import-keys refresh && break; \
|
|
echo "zypper refresh 실패 — 재시도 $i"; sleep 10; \
|
|
done && \
|
|
zypper --non-interactive install -y --no-recommends ${BUILDER_PACKAGES} && \
|
|
zypper --non-interactive clean --all
|
|
|
|
# tini — 업스트림 argocd 의 ENTRYPOINT(["/usr/bin/tini", "--"]).
|
|
# 동적 링크로 빌드한다. 이 스테이지와 final 이 같은 BCI 베이스라 glibc ABI 가 동일하고,
|
|
# 업스트림이 쓰는 apt tini 도 동적 링크다. 정적 링크는 SLE_BCI 에 정적 glibc 가 없어
|
|
# 애초에 불가능하다(2026-08-19 실측: "cannot find -lc ... static version of the c library").
|
|
ADD https://github.com/krallin/tini.git#v${TINI_VERSION} /src/tini
|
|
RUN cd /src/tini && \
|
|
cmake -DCMAKE_BUILD_TYPE=Release . && \
|
|
make tini && \
|
|
install -m 0755 tini /out-tini
|
|
|
|
# connect-proxy — Debian 의 connect-proxy 패키지 원본(gotoh/ssh-connect 의 connect.c).
|
|
# SSH-over-proxy 환경에서 git 이 쓴다. 기능을 빼지 않기 위해 빌드한다.
|
|
ADD https://github.com/gotoh/ssh-connect.git#${SSH_CONNECT_VERSION} /src/connect
|
|
RUN cd /src/connect && \
|
|
gcc -O2 -o /out-connect connect.c
|
|
|
|
####################################################################################################
|
|
# 최종 이미지 — 업스트림 argocd-base + final 을 SUSE BCI 위에서 재현한다
|
|
####################################################################################################
|
|
FROM ${RUNTIME_BASE} AS final
|
|
|
|
LABEL org.opencontainers.image.source="https://github.com/argoproj/argo-cd"
|
|
|
|
ARG RUNTIME_PACKAGES
|
|
ENV ARGOCD_USER_ID=999
|
|
|
|
# RUNTIME_PACKAGES 는 업스트림 apt 목록을 SLE 이름으로 옮긴 것이다. 이름이 다른 것들:
|
|
# git → git-core tzdata → timezone gpg/gpg-agent → gpg2 openssh-client → openssh-clients
|
|
# tini·connect-proxy 는 SLE_BCI 에 없어 c-builder 에서 빌드해 COPY 한다.
|
|
# SLE_BCI 미러가 간헐적으로 끊긴다(실측). 재시도한다.
|
|
RUN for i in 1 2 3 4 5; do \
|
|
zypper --non-interactive --gpg-auto-import-keys refresh && break; \
|
|
echo "zypper refresh 실패 — 재시도 $i"; sleep 10; \
|
|
done && \
|
|
zypper --non-interactive update -y && \
|
|
zypper --non-interactive install -y --no-recommends ${RUNTIME_PACKAGES} && \
|
|
zypper --non-interactive clean --all && \
|
|
rm -rf /var/log/zypp /usr/share/doc/packages/*
|
|
|
|
RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
|
useradd -r -u $ARGOCD_USER_ID -g argocd argocd && \
|
|
mkdir -p /home/argocd && \
|
|
chown argocd:0 /home/argocd && \
|
|
chmod g=u /home/argocd
|
|
|
|
COPY --from=c-builder /out-tini /usr/bin/tini
|
|
COPY --from=c-builder /out-connect /usr/bin/connect-proxy
|
|
COPY --from=go-tools /out/helm /usr/local/bin/helm
|
|
COPY --from=go-tools /out/kustomize /usr/local/bin/kustomize
|
|
COPY --from=go-tools /out/git-lfs /usr/local/bin/git-lfs
|
|
|
|
# 업스트림이 소스에서 넣는 래퍼 스크립트들 — argocd 가 gpg 검증·entrypoint 에 쓴다.
|
|
COPY --from=argocd-build \
|
|
/go/src/github.com/argoproj/argo-cd/hack/gpg-wrapper.sh \
|
|
/go/src/github.com/argoproj/argo-cd/hack/git-verify-wrapper.sh \
|
|
/go/src/github.com/argoproj/argo-cd/entrypoint.sh \
|
|
/usr/local/bin/
|
|
RUN chmod 0755 /usr/local/bin/gpg-wrapper.sh /usr/local/bin/git-verify-wrapper.sh /usr/local/bin/entrypoint.sh
|
|
|
|
# git-lfs 시스템 설정(/etc/gitconfig) 을 만들어 LFS 필터를 활성화한다 — 업스트림과 동일.
|
|
RUN git lfs install --system
|
|
|
|
# 하위 호환용 심볼릭 링크 — 업스트림과 동일.
|
|
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
|
|
|
# configmap 마운트 지원 — 업스트림과 동일한 경로/권한.
|
|
WORKDIR /app/config/ssh
|
|
RUN touch ssh_known_hosts && \
|
|
ln -s /app/config/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts
|
|
|
|
WORKDIR /app/config
|
|
RUN mkdir -p tls && \
|
|
mkdir -p gpg/source && \
|
|
mkdir -p gpg/keys && \
|
|
chown argocd gpg/keys && \
|
|
chmod 0700 gpg/keys
|
|
|
|
ENV USER=argocd
|
|
|
|
# dual-stack 환경에서 _grpc_config DNS TXT 조회가 타임아웃을 유발하는 문제 회피 — 업스트림과 동일.
|
|
# argocd-cmd-params-cm 으로 덮어쓸 수 있다.
|
|
ENV GRPC_ENABLE_TXT_SERVICE_CONFIG=false
|
|
|
|
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd /usr/local/bin/argocd
|
|
|
|
# 업스트림과 동일한 심볼릭 링크 9개 — 차트가 이 이름들로 컨테이너 command 를 지정한다.
|
|
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-cmp-server && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-notifications && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-applicationset-controller && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-k8s-auth && \
|
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
|
|
|
ENTRYPOINT ["/usr/bin/tini", "--"]
|
|
|
|
USER $ARGOCD_USER_ID
|
|
WORKDIR /home/argocd
|