Argo CD 보안 업그레이드: repo-server 취약점과 삭제 훅 점검

Argo CD 보안 릴리스의 영향 조건을 확인하고 임시 완화책의 한계와 업그레이드 후 호환성 변경을 점검한다.

2026-10-12

Git 쓰기 권한만으로 노출 범위를 판단할 수 없다

Argo CD의 Application get 권한만 가진 사용자도 파일을 업로드해 repo-server에서 매니페스트를 생성하게 할 수 있다. 이번 Kustomize·Jsonnet 취약점에서는 Application 수정이나 sync 권한, Git 저장소 쓰기 권한이 없어도 이 경로를 사용할 수 있다. 운영 점검을 “누가 배포 저장소에 커밋할 수 있는가”에서 끝내면 노출 경로가 남는다.

Argo CD v3.5.4 릴리스 노트는 2026-10-06 공개된 보안 수정에 CRITICAL 4건, HIGH 2건, MODERATE 2건을 포함한다. 패치 대상 버전과 영향 범위의 기준은 다음과 같다.

Argo CD v3.5.4, v3.4.10, v3.3.15, v3.6.0-rc2 (2026-10-06). 영향 범위는 어드바이저리별로 상이(v0.9.0·v2.1.0·v2.7.0·v2.10.0 이후)

먼저 Application이 사용하는 렌더링 도구와 서버 측 파일 업로드 권한을 함께 확인한다. Kustomize는 Git의 Kustomization과 업로드된 Kustomization을, Jsonnet은 Git의 .jsonnet 파일과 업로드된 파일을 통해 취약한 처리 경로에 도달한다. Jsonnet 업로드 경로에서는 기존 directory Application의 get 권한이 필요하다.

아래 그림은 입력 경로가 달라도 처리가 repo-server로 모이며, 사용하는 렌더링 도구에 따라 피해가 갈라지는 구조를 보여 준다.

⤢✕Git 저장소 파일KustomizeJsonnet파일 업로드: Application get권한repo-serverKustomize 경로: 명령 실행Jsonnet 경로: 파일 읽기저장소 자격 증명 노출

자료에 제시된 서버 측 렌더링 명령은 다음과 같다. 이 명령이 사용하는 API는 POST /api/v1/applications/manifestsWithFiles다.

argocd app diff --local --server-side-generate

이 API를 차단하거나 Application get 권한을 제한하면 업로드 경로를 닫을 수 있다. 그러나 Git에 커밋된 파일은 여전히 빌드된다. 따라서 API 접근 제한을 전체 취약점의 해결로 취급해서는 안 된다.

영향 조건을 렌더링 설정과 삭제 동작에 연결한다

CRITICAL 항목을 같은 repo-server 문제로 묶으면 대응이 어긋난다. Kustomize의 두 경로와 Jsonnet은 repo-server에서 발생하지만, 삭제 훅 문제는 application-controller가 대상 클러스터에 리소스를 생성하는 경로에서 발생한다.

취약점 영향이 시작된 버전 확인할 조건 가능한 영향
Kustomize remote ref v2.7.0 remote resource·base·component URL의 ref 또는 version 처리 repo-server 사용자 권한으로 명령 실행
Kustomize Helm config home v2.1.0 kustomize build에 --enable-helm 전달 저장소가 제어하는 Helm 플러그인 프로그램 실행
Jsonnet import v0.9.0 .jsonnet 평가와 저장소 밖 경로 import repo-server가 읽을 수 있는 파일 노출, 장치 파일 읽기에 따른 메모리 소진
AppProject 삭제 훅 우회 v2.10.0 Application 삭제 시 PreDelete·PostDelete 훅 생성 AppProject가 금지한 리소스를 controller 권한 범위에서 생성

표의 시작 버전은 취약한 동작이 처음 포함된 버전이다. 패치된 릴리스까지 계속 취약하다는 뜻은 아니다. 이 항목들은 v3.3.15, v3.4.10, v3.5.4, v3.6.0-rc2에서 수정됐다.

운영자가 확인할 대상도 다르다. Kustomize는 실제 실행 바이너리와 빌드 옵션을 확인하고, Jsonnet은 directory Application이 평가하는 파일과 import 경로를 확인한다. 삭제 훅은 Git에 선언된 훅, Application 삭제 권한, application-controller의 대상 클러스터 접근 범위를 함께 확인해야 한다.

Kustomize remote ref는 외부 통신 차단으로 막히지 않는다

Kustomize remote ref 어드바이저리에 따르면 Kustomize는 remote URL의 ref 또는 version 값을 -- 구분자 없이 git fetch에 전달했다. 값이 -로 시작하면 Git 옵션으로 해석될 수 있다.

문제가 되는 옵션은 --upload-pack=<command>다. remote가 file:// 같은 로컬 전송이면 지정된 명령이 repo-server 안에서 실행된다. https:// remote에서는 Git이 그 명령을 실행하지 않는다. 명령은 repo-server 사용자 권한으로 실행되며, 출력은 매니페스트 생성 오류를 통해 호출자에게 반환될 수 있다.

file:// 요청은 Pod 밖으로 나가지 않는다. repo-server egress를 차단했더라도 이 실행 경로가 막혔다고 판단할 수 없다. 또한 자료는 최신 Kustomize로 바이너리를 교체하는 방법도 해결책이 아니라고 명시한다. Kustomize가 여전히 ref를 -- 없이 전달하기 때문이다.

패치는 Kustomize 빌드의 PATH에서 실제 Git보다 먼저 실행되는 git wrapper를 두어 remote URL과 ref가 Git 옵션으로 해석되지 않게 한다. remote base 기능 자체는 유지된다. remote base를 사용하는 Application이라면 업그레이드 후 기존 참조가 정상적으로 렌더링되는지 확인한다.

업그레이드 전 완화책으로는 Kustomize를 실행하는 모든 Application을 5.0 이전 버전, 예를 들어 4.5.7에 고정하는 방법이 제시된다. argocd-cm의 kustomize.path.<version>으로 바이너리를 등록하고 각 Application의 spec.source.kustomize.version을 지정해야 한다.

설정에 버전 이름이 들어 있는지만 확인해서는 부족하다. 실제 실행 바이너리가 이전 버전이어야 하며, 5.x가 기본값이나 다른 등록 버전으로 남아 있으면 해당 경로가 열려 있다. Kustomize 5 기능에 의존하는 빌드는 4.5.7에서 실패하거나 동작이 빠질 수 있다. 어드바이저리는 업그레이드 외에 완전한 우회책은 없다고 설명한다.

Helm과 Jsonnet 완화책은 렌더링 결과를 바꾼다

Kustomize Helm config home 어드바이저리의 영향 조건은 운영자가 --enable-helm을 켜 두었는지다. 이 옵션을 전달하지 않는 배포는 해당 취약점의 영향을 받지 않는다.

옵션이 켜져 있으면 Kustomization의 helmGlobals.configHome이 Helm의 설정·데이터 디렉터리에 반영된다. Helm은 <configHome>/.data/plugins에서 downloader plugin을 읽고, helmCharts 저장소 URL이 그 플러그인의 프로토콜을 사용하면 프로그램을 실행한다. 별도의 플러그인 설치 단계는 필요하지 않았다.

kustomize.buildOptions에서 --enable-helm을 제거하는 것은 이 취약점에 대한 완전한 우회책이다. 다만 Kustomize를 통해 Helm 차트를 렌더링하는 Application에는 그대로 적용할 수 없다. 해당 Application이 사용하는 렌더링 기능을 먼저 확인해야 한다.

Jsonnet import 어드바이저리는 import, importstr, importbin이 repo-server 프로세스가 읽을 수 있는 경로를 열 수 있었다고 설명한다. 절대 경로, ../ 경로 이동, 저장소 밖을 가리키는 심볼릭 링크가 포함된다. 노출 가능한 데이터에는 프로세스 환경, 마운트된 개인 키, 저장소 자격 증명, projected service-account token이 들어간다.

생성된 Secret을 가린다고 이 파일 읽기 문제가 해소되지는 않는다. 읽은 내용을 ConfigMap에 넣으면 호출자에게 반환할 수 있기 때문이다. /dev/zero 같은 장치를 importstr로 읽으면 메모리가 소진될 때까지 읽는 문제도 있다.

Jsonnet의 임시 완화책은 argocd-cm에서 다음 값을 설정하는 것이다.

jsonnet.enable: "false"

이 설정은 Git에 있는 파일과 API로 업로드한 파일 모두에 적용된다. 하지만 .jsonnet 파일을 오류로 거부하는 대신 건너뛴다. directory Application의 YAML과 JSON은 계속 적용되며, Jsonnet 파일만 있는 디렉터리는 그 매니페스트 없이 sync된다. 설정 후에는 sync 성공 여부와 함께 렌더링 결과에 필요한 리소스가 남아 있는지 확인해야 한다.

다음 비교는 완화책을 선택할 때 차단 범위와 기능 손실을 함께 판단하도록 정리한 것이다.

⤢✕ 렌더링 취약점의 임시 완화책과 기능 손실 비교 축: 대상 취약점 · 적용 조치 · 차단 범위 · 기능 영향 대상 취약점 Kustomize remote ref Kustomize Helm config home Jsonnet import 적용 조치 차단 범위 기능 영향 모든 Kustomize Application의 실제 실행 바이너리를 5.0 이전 버전인 4.5.7 등으로 고정 kustomize.buildOptions에서 --enable-helm 제거 argocd-cm의 jsonnet.enable을 "false"로 설정 5.x가 기본값이나 다른 등록 버전으로 남으면 해당 경로가 열려 있음 업그레이드 외에 완전한 우회책은 없음 해당 취약점에 대한 완전한 우회책 Git 파일과 API 업로드 파일 모두 건너뜀 Kustomize 5 기능 의존 빌드는 실패하거나 동작이 빠질 수 있음 Kustomize를 통한 Helm 차트 렌더링을 사용할 수 없음 Jsonnet 매니페스트는 생략됨 directory Application의 YAML과 JSON은 계속 적용됨 Argo CD 보안 업그레이드: repo-server 취약점과 삭제 훅 점검 | security

패치 후 Jsonnet은 심볼릭 링크를 포함해 import 경로를 해석하고, 저장소 루트 밖의 경로와 일반 파일이 아닌 대상을 거부한다. 저장소 안에 머무는 import는 계속 동작한다. 정상적인 Jsonnet Application도 저장소 밖 파일에 의존했다면 그 의존성을 점검해야 한다.

Application 삭제도 AppProject 검증 대상이어야 한다

AppProject 삭제 훅 어드바이저리, CVE-2026-77459의 핵심은 sync와 삭제가 서로 다른 리소스 생성 경로를 사용했다는 점이다.

일반 sync에서는 AppProject의 대상 namespace·server, 클러스터 범위 리소스와 namespaced 리소스의 허용·거부 목록, 리소스 이름을 검사한다. PreDelete와 PostDelete 훅은 이 검사를 건너뛰고 Application 삭제 시 application-controller의 자격 증명으로 생성됐다. PreSync, Sync, PostSync, SyncFail 훅은 계속 프로젝트 검사를 거친다.

Git 저장소에 쓰기 권한이 있는 사용자는 AppProject가 sync에서 거부할 namespace·group·kind·name의 삭제 훅을 커밋할 수 있다. controller는 훅을 확인하면 삭제 finalizer를 추가하며, Application은 누군가 삭제할 때까지 Synced 상태로 남을 수 있다. 따라서 Synced라는 상태만으로 삭제 시 동작이 프로젝트 제한 안에 있다고 판단할 수 없다.

패치는 삭제 훅을 만들기 전에 sync와 같은 AppProject 대상·리소스 검사를 적용한다. 아래 그림은 동일한 삭제 요청에서 패치 전후의 검증 경계가 어떻게 달라지는지 보여 준다.

⤢✕Application 삭제패치 전: AppProject 검사생략패치 후: AppProject대상·리소스 검사controller 자격 증명으로 훅생성AppProject 허용AppProject 거부훅 생성 차단

피해 범위는 application-controller가 대상 클러스터에서 할 수 있는 작업으로 제한된다. 어드바이저리는 일반적인 설치에서 이 접근 범위가 넓어 ClusterRoleBinding 같은 클러스터 범위 객체나 프로젝트가 허용하지 않는 namespace의 객체를 만들 수 있다고 설명한다.

생성된 훅이 사라지는지와 그 훅이 만든 객체가 사라지는지도 구분해야 한다. 삭제 정책이 없는 훅은 성공 후 제거되지만, 성공 시 제거하지 않는 정책이면 객체가 남는다. Job 같은 훅이 추가로 만든 객체는 Argo CD가 정리하지 않을 수 있다.

패치 전에는 다음 권한 경계를 조정한다.

  • 제한적인 AppProject의 Application이 사용하는 Git 저장소에 신뢰할 수 없는 사용자의 쓰기 권한을 부여하지 않는다.
  • Application 삭제 권한을 제한한다. 삭제가 PreDelete와 PostDelete 실행을 촉발한다.
  • application-controller의 Kubernetes 접근 범위를 줄여 민감한 namespace와 API에 도달하지 못하게 한다.

삭제 훅을 계속 사용하면서 프로젝트 검사를 적용하는 설정은 없다. Argo CD 2.x와 3.0부터 3.2까지는 이 취약점의 패치를 받지 않으므로, 패치된 3.3 이상 릴리스로 업그레이드해야 한다.

업그레이드 검증에는 Helm 플러그인과 OCI 크기를 넣는다

이번 패치는 정상 빌드에도 영향을 줄 수 있는 변경을 포함한다. 변경 창을 잡기 전에 기존 Application이 해당 동작에 의존하는지 확인한다.

점검 대상 패치 후 동작 확인할 내용
Kustomize remote base remote base 기능 유지 기존 remote 참조의 렌더링 성공 여부
Kustomize Helm 설정 helmGlobals.configHome을 더 이상 반영하지 않음 해당 디렉터리에서 등록한 Helm 플러그인 의존성
일반 Helm chart 저장소 http, https, oci 저장소 렌더링 유지 사용하는 chart 저장소와 렌더링 결과
Jsonnet import 저장소 밖 경로와 일반 파일이 아닌 대상 거부 절대 경로·경로 이동·심볼릭 링크 의존성
OCI 매니페스트 4 MiB보다 크면 거부 사용하는 OCI 매니페스트의 크기

Helm 패치는 Kustomize 빌드마다 빈 설정·데이터 디렉터리를 사용해 helmGlobals.configHome으로 플러그인을 등록할 수 없게 한다. http, https, oci chart 저장소는 계속 동작하지만, 그 디렉터리에서 로드한 플러그인에 의존하는 빌드는 동작하지 않는다. Helm chart 렌더링 전체를 교체할 문제인지, 특정 플러그인 의존성 문제인지 구분해 검증 대상을 잡는다.

OCI 변경의 기준은 매니페스트 크기다. 자료는 4 MiB보다 큰 OCI 매니페스트를 거부한다고 명시한다. 이미지 전체 크기나 chart 패키지 크기를 같은 기준으로 판단해서는 안 된다.

저장소 프록시와 나머지 보안 수정도 확인한다

repo-server 명령 실행 경로는 Kustomize에만 있지 않다. SSH Git 저장소 프록시 어드바이저리, CVE-2026-55797은 SSH 저장소의 프록시 host와 port를 ProxyCommand에 넣는 과정에서 셸 명령 주입이 가능했다고 설명한다.

영향은 v2.11.0부터 시작됐다. 저장소가 SSH를 사용하면 http, https, socks5 프록시 URL이 모두 이 경로를 탄다. HTTPS Git 저장소는 프록시를 HTTP 프록시로 사용하며 이 명령을 만들지 않는다. 따라서 프록시 URL의 스킴만 보고 영향 여부를 판단하지 말고 Git 저장소의 전송 방식까지 확인한다.

저장소나 credential template을 생성·수정할 수 있는 사용자는 프록시를 지정할 수 있다. project-scoped 저장소 권한과 repository Secret도 포함된다. credential template의 프록시는 자체 자격 증명이 없는, 일치하는 SSH 저장소에 적용된다. 명령은 저장소 연결 테스트와 이후 fetch에서 실행되므로 sync만 검증 대상으로 삼으면 부족하다.

기존 repository·credential-template Secret에서 예상하지 못한 proxy 값을 확인하고, 신뢰할 수 없는 사용자의 저장소 생성·수정 권한을 제한한다. SSH 저장소나 이에 일치하는 credential template에 프록시를 설정하지 않는 것이 제시된 우회책이다. SSH 프록시를 유지하면서 셸 명령만 제거하는 설정은 없으며, 패치는 host와 port를 셸에 전달하지 않도록 바꾼다.

릴리스에 포함된 나머지 수정도 해당 기능의 사용 여부와 연결해 확인한다.

수정 항목 심각도 자료에서 확인되는 문제 운영 점검 대상
로그인 실패 속도 제한 HIGH 동시 요청으로 제한 우회 로그인 실패 속도 제한을 사용하는 환경
Extension proxy RBAC MODERATE applications-in-any-namespace 사용 시 Application namespace 무시 해당 기능을 켠 환경의 extension proxy 권한
OCI 매니페스트 MODERATE 과도하게 큰 매니페스트로 repo-server 메모리 소진 OCI 사용과 패치 후 크기 제한

업그레이드 완료 판단은 버전 확인과 실제 Application 렌더링을 함께 근거로 삼는다. 기존 Kustomize remote base, Helm chart 렌더링, 저장소 내부 Jsonnet import가 동작하는지 확인하고, 삭제 훅은 AppProject가 허용·거부하는 대상에 대해 새 검증 경계가 적용되는지 확인한다. 임시로 끈 렌더링 기능이 있다면 생성 결과에서 빠진 매니페스트까지 검토해야 보안 완화와 배포 누락을 구분할 수 있다.

Argo CDGitOpsKustomizeJsonnet보안Kubernetes