# 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