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>
78 lines
5.0 KiB
Bash
78 lines
5.0 KiB
Bash
# argocd — 소스 컴파일형 자체 빌드 (유일한 변종)
|
|
#
|
|
# build-hardened-image.sh 가 source 한다. BUILD_ARGS 에 나열한 이름만 --build-arg 로 넘어간다.
|
|
# 소스를 직접 컴파일하므로 "베이스 OS" 선택지가 없다 — BASE_OS=source 로 호출한다:
|
|
# IMAGE=argocd BASE_OS=source bash scripts/build/build-hardened-image.sh <OUT_DIR>
|
|
#
|
|
# 왜 자체 빌드하는가 → source.Dockerfile 상단 주석 / README.md.
|
|
# argo-cd 10.4.0 CVE 조치(2026-08-19)로 도입 — 근거·경과는 MEMORY.md.
|
|
#
|
|
# ── 다음 CVE 조치에서 바뀌는 것은 이 파일뿐이다 ────────────────────────────────
|
|
# Dockerfile 에는 버전도 모듈 목록도 박혀 있지 않다. 새 차단 CVE 가 나오면:
|
|
# Go stdlib CVE → GO_BUILDER_TAG 를 올린다
|
|
# Go 모듈 CVE → GO_MODULE_UPGRADES 에 `<module>@<version>` 을 추가한다
|
|
# OS 패키지 CVE → RUNTIME_BASE 를 올리거나 RUNTIME_PACKAGES 를 조정한다
|
|
# 구성요소 새 릴리스 → 해당 *_VERSION 을 올린다
|
|
|
|
DOCKERFILE=source.Dockerfile
|
|
TARGET=final
|
|
TAG_SLUG=security
|
|
|
|
# build-hardened-image.sh 가 태그·verify.sh 전달용으로 요구하는 범용 필수값.
|
|
# 스톡 v3.5.1 과는 태그 슬러그(TAG_SLUG=security)로 구분되므로 버전 문자열은 그대로 둔다.
|
|
APP_VERSION=3.5.1
|
|
|
|
# v3.5.1 태그가 가리키는 커밋(lightweight 태그라 태그 객체 없이 커밋 직접 참조), 2026-08-19 확인.
|
|
SOURCE_COMMIT=109ca7ca71139e514114499d294a492e7910a965
|
|
|
|
# stdlib 차단 CVE 를 해소하는 최소 Go 버전. suggest-go-upgrades.py 가 산출한다.
|
|
# 빌더 스테이지에만 쓰이고 최종 이미지에는 남지 않는다.
|
|
GO_BUILDER_TAG=1.26.6-trixie
|
|
|
|
# UI 빌드용. 업스트림 Dockerfile 의 node pin 과 동일하게 맞춘다 — UI 번들은 스캔에서
|
|
# 취약점이 잡히지 않으므로 올릴 이유가 없고, lockfile 과의 정합을 지키는 쪽이 안전하다.
|
|
NODE_BUILDER_TAG=24.14.1
|
|
|
|
# 최종 런타임 베이스 — 카탈로그는 SUSE BCI 하나만 쓴다(.claude/image-authoring.md 원칙 2).
|
|
# argocd 는 git/gpg/ssh 런타임이 필요해 패키지 매니저가 있는 bci-base 를 쓴다.
|
|
# base 와 micro 를 스캔해 비교했고 차이가 없어, micro 씨앗 방식(.claude/image-authoring.md)의
|
|
# 복잡도를 감수할 이점이 없었다.
|
|
#
|
|
# **16.0 을 쓰는 이유 — coreutils 버전이 차트 요구사항을 가른다.**
|
|
# argo-cd 차트의 repo-server init 컨테이너(copyutil)가 `cp --update=none` 을 쓴다. 이
|
|
# 형식은 GNU coreutils 9.3+ 에서만 되는데 15.7 은 그보다 낮아
|
|
# "option '--update' doesn't allow an argument" 로 죽는다(실측 — 15.7 로 빌드해 배포했다가
|
|
# repo-server 가 Init:CrashLoopBackOff 로 실패했다). 16.0 은 coreutils 9.6 이라 동작한다.
|
|
# 16.0 의 패키지 가용성과 trivy 커버리지(CoverageProbe=ok, EOSL 아님)도 확인했다.
|
|
# verify.sh 가 이 명령을 직접 검사하므로 다음에 베이스를 바꿔도 빌드 단계에서 걸린다.
|
|
RUNTIME_BASE=registry.suse.com/bci/bci-base:16.0
|
|
|
|
# 번들 Go 도구 — 업스트림은 hack/install.sh 로 릴리스 바이너리를 내려받지만, 그 바이너리의
|
|
# Go 버전이 낡아(git-lfs 1.25.3 · kustomize 1.24.0) CVE 의 주범이었다. 소스에서 다시 만든다.
|
|
# helm 만 업스트림 pin(4.2.1) 대신 최신을 쓴다 — 나머지 둘은 pin 이 이미 최신이다.
|
|
HELM_VERSION=4.2.4
|
|
KUSTOMIZE_VERSION=5.8.1
|
|
GIT_LFS_VERSION=3.7.1
|
|
|
|
# SLE_BCI 에 패키지가 없어 소스 빌드한다(2026-08-19 zypper 실측).
|
|
# tini 는 업스트림 ENTRYPOINT, connect-proxy 는 SSH-over-proxy 용이다.
|
|
TINI_VERSION=0.19.0
|
|
SSH_CONNECT_VERSION=1.106
|
|
|
|
# 강제 업그레이드할 Go 모듈 — argocd·helm·kustomize·git-lfs 네 프로젝트에 **공통 적용**된다.
|
|
# go-mod-upgrade.sh 가 각 프로젝트의 의존성 그래프에 있는 것만 골라 적용하므로 하나의
|
|
# 목록을 그대로 재사용할 수 있다.
|
|
#
|
|
# 값은 suggest-go-upgrades.py 로 산출한다. 단, 제안값은 CVE 요건의 **최소치**라 모듈 간
|
|
# 제약으로 더 올려야 할 수 있다 — 빌드가 "requires <module>@vX, not @vY" 로 실패하면 그
|
|
# 버전으로 올린다(x/crypto 가 이 경우였다).
|
|
GO_MODULE_UPGRADES="golang.org/x/crypto@v0.53.0 golang.org/x/net@v0.56.0 golang.org/x/text@v0.39.0 google.golang.org/grpc@v1.82.1 github.com/go-git/go-git/v5@v5.19.2 oras.land/oras-go/v2@v2.6.2"
|
|
|
|
# c-builder 가 tini(cmake)·connect(gcc) 를 컴파일하는 데 필요한 것들.
|
|
BUILDER_PACKAGES="gcc make cmake git-core tar gzip"
|
|
|
|
# 최종 이미지 런타임 패키지 — 업스트림 apt 목록의 SLE 대응(README.md 매핑표 참고).
|
|
RUNTIME_PACKAGES="git-core ca-certificates gpg2 timezone openssh-clients"
|
|
|
|
BUILD_ARGS="SOURCE_COMMIT APP_VERSION GO_BUILDER_TAG NODE_BUILDER_TAG RUNTIME_BASE HELM_VERSION KUSTOMIZE_VERSION GIT_LFS_VERSION TINI_VERSION SSH_CONNECT_VERSION GO_MODULE_UPGRADES BUILDER_PACKAGES RUNTIME_PACKAGES"
|