Argo CD 这次一起公开了三个安全公告,两个是 CVSS 9.9 的 Critical,一个是 8.8 的 High。3.x 里受影响的是 3.0.0 至 3.3.14、3.4.0 至 3.4.9、3.5.0 至 3.5.3 和 3.6.0-rc1,修复版分别是 v3.3.15、v3.4.10、v3.5.4 和 v3.6.0-rc2;2.x 也在受影响范围里,但没有补丁。
四个修复版在北京时间 10 月 7 日凌晨 4:50 至 5:35 之间陆续发出,三个公告在当天 23:58 前后公开。SSH 代理和删除 hook 这两个公告写明,2.x 和 3.0 至 3.2 已经停止支持,不会再出补丁,公告给的出路是升到 3.3 或更新线上的补丁版。
三个洞里两个落在 repo-server,一个落在 AppProject 的权限检查。下面按公告原文逐个说。
Kustomize 开了 --enable-helm,仓库里的东西能在 repo-server 里跑命令
公告编号 GHSA-fw5c-w8rc-j7fx,公告里没有列 CVE 编号。CVSS 3.1 打分 9.9,严重级别 Critical,向量是 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H。
受影响版本从 v2.1.0 开始,v2.0.5 及更早不受影响。公告给出的受影响区间是 3.0.0 至 3.3.14、3.4.0 至 3.4.9、3.5.0 至 3.5.3,以及 3.6.0-rc1。
原理是这样的。Kustomize 会把 kustomization 里的 helmGlobals.configHome 拷进 Helm 的配置和数据目录,Helm 会直接加载 <configHome>/.data/plugins 下的 downloader 插件,不需要单独安装。只要 helmCharts 的仓库地址用了这个插件的协议,插件就会以 repo-server 的身份执行,能读到仓库凭据,以及这个进程能拿到的其他东西。
利用前提是运维已经在 Argo CD 里给 kustomize build 加了 --enable-helm。没加这个参数的部署不受影响。在这个前提下,公告列了两条投递路径。一条是把 kustomization 提交到某个用 Kustomize 构建的 Application 所指向的 Git 仓库。另一条是通过 POST /api/v1/applications/manifestsWithFiles 上传,也就是 argocd app diff --local --server-side-generate。第二条只要对一个已有 Application 有 get 权限就够了,不需要更新或同步权限,也不需要仓库写权限。
缓解办法公告给得很明确。不要在 kustomize.buildOptions 里传 --enable-helm,这是完整的规避方式,代价是靠 Kustomize 渲染 Helm chart 的应用没法用了。只收紧 Application get 权限或者拦掉 manifestsWithFiles 接口,只能堵住上传那条路,Git 里提交的 kustomization 在下次同步时照样会被构建。
修复后,Argo CD 在每次 Kustomize 构建时都让 Helm 指向空的配置和数据目录,helmGlobals.configHome 注册不了插件。普通的 http、https、oci chart 仓库照常能用。
SSH 仓库配了代理,代理地址能注入 shell 命令
公告编号 GHSA-j6cw-g6p4-7hch,CVE-2026-55797。CVSS 3.1 打分 8.8,严重级别 High,向量是 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H。
受影响版本从 v2.11.0 开始,v2.10.20 及更早不受影响。问题来自 v2.11.0 加入的 SSH Git 地址 SOCKS5 代理支持。公告给的受影响区间同样是 3.0.0 至 3.3.14、3.4.0 至 3.4.9、3.5.0 至 3.5.3 和 3.6.0-rc1,v2 模块从 2.11.0 起也列为受影响,但 2.x 不出补丁。
repo-server 克隆或拉取一个带代理地址的 SSH 仓库时,会把代理的 host 和 port 拼进 SSH 的 ProxyCommand,再交给 shell 执行。host 里带 shell 元字符,就能跳出这条命令,在 repo-server 里执行任意内容。这个进程能读到 repo-server 上存的其他 Git、Helm、OCI 凭据。
利用前提有两条。仓库必须走 SSH,代理写成 http、https、socks5 都会走到这条路径;HTTPS 仓库把代理当 HTTP 代理用,不会拼这条命令。另外攻击者要能创建或更新仓库,或者仓库凭据模板。项目级仓库权限、argocd repo add --proxy、直接写仓库 Secret 都算。凭据模板上的代理会作用到所有匹配、且自身没有凭据的 SSH 仓库。测试仓库连接时就会执行一次,之后每次拉取也会执行。
公告说没有哪个配置能保留 SSH 仓库代理同时去掉这条 shell 命令。能做的缓解有这些:不要把仓库创建和更新权限给不受信任的用户,项目级仓库权限也一样;不要在 SSH 仓库上配代理,也不要在会匹配到 SSH 仓库的凭据模板上配代理。公告还建议把现有仓库和凭据模板 Secret 过一遍,看有没有意料之外的 proxy 值。
修复后,代理的 host 和 port 不再经过 shell 传递。
PreDelete 和 PostDelete hook 绕过了 AppProject 检查
公告编号 GHSA-fmxq-cgp8-87wp,CVE-2026-77459。CVSS 3.1 打分 9.9,严重级别 Critical,向量是 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H。
受影响版本从 v2.10.0 开始,v2.9.22 及更早不受影响。PostDelete hook 在 v2.10.0 引入,创建路径从一开始就没做项目检查。PreDelete hook 在 v3.3.0 引入,走的是同一条路径,v3.2.12 及更早没有 PreDelete。公告给的受影响区间是 3.0.0 至 3.3.14、3.4.0 至 3.4.9、3.5.0 至 3.5.3 和 3.6.0-rc1。
正常同步时,Argo CD 会按 AppProject 检查目标命名空间和集群、集群级资源的允许和拒绝列表、命名空间级资源的允许和拒绝列表,包括资源名。PreSync、Sync、PostSync、SyncFail 这几种 hook 都会过这道检查。PreDelete 和 PostDelete 不过。Application 被删除时,Argo CD 用 application-controller 的凭据在目标集群上创建这些 hook。
利用前提是能往 Application 同步的 Git 仓库推代码。攻击者提交一个 hook,它的命名空间、group、kind 或名字是 AppProject 在同步时会拒绝的。controller 看到 hook 后给 Application 加上删除 finalizer,Application 一直显示 Synced,直到有人删除它,hook 才被创建。能碰到什么取决于 application-controller 在目标集群上的权限。公告说典型安装里这个权限很宽,租户可以借此创建 ClusterRoleBinding 这类集群级对象,或者在项目不允许的命名空间里建对象。没有删除策略的 hook 成功后会被删掉;删除策略不在成功时删 hook 的,对象会留在集群里。如果 hook 是 Job,它还能再创建 Argo CD 不会清理的其他对象。
公告说没有配置项能在继续使用删除类 hook 的同时给它们加上项目检查。缓解有三条:不要把受限 AppProject 下 Application 对应 Git 仓库的写权限给不受信任的用户;限制谁能删除 Application,因为删除才会触发这两种 hook;收紧 application-controller 的 Kubernetes 权限,让 hook 碰不到敏感命名空间和 API。
修复后,创建 PreDelete 或 PostDelete hook 之前会先做同步时那套 AppProject 目标和资源检查。
修复版镜像和 Helm chart
四个修复版的镜像都已经在 quay.io 上。我用 Quay 的公开 API 查了一遍,输出如下(时间为 UTC):
[('v3.3.15', 'Tue, 06 Oct 2026 21:08:21 -0000')]
[('v3.4.10', 'Tue, 06 Oct 2026 20:32:34 -0000')]
[('v3.5.4', 'Tue, 06 Oct 2026 20:32:05 -0000')]
[('v3.6.0-rc2', 'Tue, 06 Oct 2026 20:25:56 -0000')]
用社区 Helm chart 装的,要看 chart 版本对应的 appVersion。argo-helm 仓库 index.yaml 里 appVersion 为这几个修复版的 argo/argo-cd chart 只有下面这些:
argo/argo-cd 10.10.2 appVersion v3.5.4
argo/argo-cd 10.10.1 appVersion v3.5.4
argo/argo-cd 10.10.0 appVersion v3.5.4
argo/argo-cd 10.9.7 appVersion v3.5.4
也就是说 chart 只跟进了 3.5 线。3.3 和 3.4 线没有对应的新 chart,chart 的 values 里 global.image.tag 可以覆盖默认取自 appVersion 的镜像 tag,指到 v3.3.15 或 v3.4.10。
自查当前集群
先看版本。下面假设 Argo CD 装在 argocd 命名空间。
kubectl -n argocd get deploy argocd-repo-server \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
kubectl -n argocd get statefulset argocd-application-controller \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
argocd version
再看 argocd-cm 里有没有给 Kustomize 加 --enable-helm。除了 kustomize.buildOptions,按 Kustomize 版本配置的 kustomize.buildOptions.<版本> 也要一起看,所以直接把整个 data 拿出来过滤:
kubectl -n argocd get cm argocd-cm -o yaml | grep -n 'kustomize.buildOptions'
kubectl -n argocd get cm argocd-cm -o yaml | grep -n -- '--enable-helm'
第二条有输出,就满足 GHSA-fw5c-w8rc-j7fx 的利用前提。
SSH 仓库代理配置存在仓库 Secret 和凭据模板 Secret 里,分别带 argocd.argoproj.io/secret-type=repository 和 repo-creds 标签。下面这条把两类 Secret 里设了 proxy 的都列出来,连同 url 一起解码,看 url 是不是 SSH 地址(git@ 开头或 ssh:// 开头):
for t in repository repo-creds; do
kubectl -n argocd get secret -l argocd.argoproj.io/secret-type=$t -o json \
| jq -r '.items[] | select(.data.proxy != null)
| "\(.metadata.name)\t\(.data.url | @base64d)\t\(.data.proxy | @base64d)"'
done
带 PreDelete 或 PostDelete hook 的应用,controller 会在 Application 上加对应的 finalizer,名字在 Argo CD 源码里定义为 pre-delete-finalizer.argocd.argoproj.io 和 post-delete-finalizer.argocd.argoproj.io。按 finalizer 筛一遍,跨所有命名空间:
kubectl get applications.argoproj.io -A -o json \
| jq -r '.items[]
| select(any(.metadata.finalizers[]?; test("^(pre|post)-delete-finalizer\\.argocd\\.argoproj\\.io")))
| "\(.metadata.namespace)/\(.metadata.name)\t\(.spec.project)"'
筛出来的应用,去对应仓库里看 hook 资源的注解 argocd.argoproj.io/hook,确认这些 hook 要建的东西有没有超出所属 AppProject 的范围。
升级
官方清单安装的升级命令按升级文档的写法,<version> 换成对应线的修复版,以 v3.5.4 为例:
kubectl apply -n argocd --server-side --force-conflicts \
-f https://raw.githubusercontent.com/argoproj/argo-cd/v3.5.4/manifests/install.yaml
HA 安装换成 manifests/ha/install.yaml。v3.5.4 这份 install.yaml 里的镜像我核对过:
32094: image: quay.io/argoproj/argocd:v3.5.4
Helm chart 安装的,argo-cd chart README 里的升级写法是 helm upgrade argocd argo/argo-cd --version <chart 版本> --reuse-values,对应 v3.5.4 的是 10.10.2:
helm repo update argo
helm upgrade argocd argo/argo-cd -n argocd --version 10.10.2 --reuse-values
命令里的 release 名 argocd 和命名空间 argocd 是 README 里的默认写法。
GitOps 场景下的另一类工具,之前写过 Kubemend,只会开 GitOps PR 的 K8s 修复 Agent。
参考来源
- GHSA-fw5c-w8rc-j7fx:https://github.com/argoproj/argo-cd/security/advisories/GHSA-fw5c-w8rc-j7fx
- GHSA-j6cw-g6p4-7hch(CVE-2026-55797):https://github.com/argoproj/argo-cd/security/advisories/GHSA-j6cw-g6p4-7hch ,https://github.com/advisories/GHSA-j6cw-g6p4-7hch
- GHSA-fmxq-cgp8-87wp(CVE-2026-77459):https://github.com/argoproj/argo-cd/security/advisories/GHSA-fmxq-cgp8-87wp
- Argo CD v3.3.15 发布说明:https://github.com/argoproj/argo-cd/releases/tag/v3.3.15
- Argo CD v3.4.10 发布说明:https://github.com/argoproj/argo-cd/releases/tag/v3.4.10
- Argo CD v3.5.4 发布说明:https://github.com/argoproj/argo-cd/releases/tag/v3.5.4
- Argo CD v3.6.0-rc2 发布说明:https://github.com/argoproj/argo-cd/releases/tag/v3.6.0-rc2
- Kustomizing Helm charts:https://argo-cd.readthedocs.io/en/stable/user-guide/kustomize/#kustomizing-helm-charts
- Configure repositories with proxy:https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#configure-repositories-with-proxy
- Resource hooks:https://argo-cd.readthedocs.io/en/stable/user-guide/resource_hooks/
- Argo CD 升级文档:https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/
- argo-helm 仓库索引:https://argoproj.github.io/argo-helm/index.yaml
一起交流
分享你的思考,让讨论更进一步。