在 Kubernetes 的默认行为里,只要创建一个 Pod,kubelet 就会把该 Pod 所属 ServiceAccount 的令牌以卷的形式挂载到容器内的 /var/run/secrets/kubernetes.io/serviceaccount/ 目录下。这个设计原本是为了方便应用通过客户端 SDK 访问 API Server,但在安全要求较高的环境里,它反而成了一枚定时炸弹:大量根本不需要访问 API 的应用,胸前却挂着一把能通过认证的钥匙。本文就来聊聊如何通过 automountServiceAccountToken 这项配置,关闭和管控服务账户令牌的自动挂载。

一、令牌自动挂载的工作原理与风险
先说清楚令牌是怎么进到容器里的。当 Pod 被调度到某个节点后,kubelet 会通过 TokenRequest API 向 API Server 申请一个绑定该 Pod 的令牌,然后以投射卷的形式挂载给容器。容器内读取目录下的 token 文件,配合 ca.crt 和 namespace 文件,就能完成对 API Server 的认证与通信。这是三大文件各司其职:token 是 JWT 格式的访问凭证,ca.crt 用于校验 API Server 的证书,namespace 则标明当前命名空间。
问题的核心在于默认行为过于慷慨。如果 Pod 声明时没有显式指定 ServiceAccount,Kubernetes 会把 default 这个服务账户分配给它,而 default 账户默认开启了自动挂载。于是你会看到一个奇怪的现象:一个纯粹的 Web 前端应用、一个日志收集 Agent,容器里都躺着一份有效的 API 访问凭证。攻击者一旦通过任意方式拿到了容器内的执行权限,只需要读取这个文件,就能用令牌查询集群内的资源信息,如果 RBAC 配置也偏宽松,甚至可以直接创建特权 Pod 完成横向移动。
从 1.24 版本开始,社区已经移除了基于 Secret 的静态令牌机制,改用带生命周期的动态令牌,安全性有所提升,但自动挂载本身带来的攻击面并没有消失。令牌存在与否,和应用是否需要它是两回事,最小权限原则要求我们把不需要的令牌彻底摘掉。
二、在 ServiceAccount 与 Pod 两个层面关闭自动挂载
Kubernetes 提供了两个控制入口,一个在 ServiceAccount 上,一个在 Pod 上,对应字段都是 automountServiceAccountToken,类型是布尔值。在 ServiceAccount 层面关闭后,所有使用该账户的 Pod 默认都不会挂载令牌:
apiVersion: v1 kind: ServiceAccount metadata: name: build-robot namespace: prod automountServiceAccountToken: false
如果你只想对个别 Pod 做例外处理,或者不想改动整个命名空间的账户策略,可以在 Pod 的 spec 层面单独设置。注意 Pod 层面关闭时,即使 ServiceAccount 层面是 true,也不会挂载令牌:
apiVersion: v1
kind: Pod
metadata:
name: web-static
spec:
automountServiceAccountToken: false
containers:
- name: nginx
image: nginx:1.25
两个开关同时存在时,优先级规则很简单:Pod 层面的设置永远胜出。也就是说,Pod 显式写了 automountServiceAccountToken: false,无论账户怎么配置都不挂载;Pod 没写或者写为 true,则看 ServiceAccount 的配置;两边都没写,默认为 true。在实际落地时,推荐的做法是把命名空间里的 default 账户统一设置为 false,这样绝大部分普通应用天然就没有令牌,只有确实需要访问 API 的应用才在 Pod 层面显式打开,或者绑定专门创建的服务账户。这种白名单式的收敛思路,比挨个排查 Pod 要省力得多。
设置完成后可以进入容器验证:执行 ls /var/run/secrets/kubernetes.io/serviceaccount/ 目录不存在或为空,说明关闭生效。如果应用运行中报 401 或者找不到凭证的错误,先确认它是否真的需要访问 Kubernetes API,而不是简单地把开关改回去。
三、进阶管控:投射卷、令牌有效期与准入策略
关闭挂载解决了不需要令牌的场景,而确实需要令牌的应用也有更精细的管控手段。现代做法是使用投射卷手动声明令牌,并且可以限定 audience(令牌的受众)和过期时间,避免默认的一年期长令牌:
apiVersion: v1
kind: Pod
metadata:
name: api-consumer
spec:
containers:
- name: app
image: registry.ipipp.com/demo/api-consumer:v2.1
volumeMounts:
- name: token-vol
mountPath: /var/run/secrets/tokens
volumes:
- name: token-vol
projected:
sources:
- serviceAccountToken:
path: my-token
expirationSeconds: 3600
audience: vault
上面这个例子把令牌的有效期压缩到一小时,并且指定了 audience 为 vault,意味着这个令牌只能用于访问 Vault 这类外部服务,拿去请求 Kubernetes API Server 会被直接拒绝。kubelet 会在过期前主动刷新令牌,应用侧通过 inotify 监听文件变更即可拿到新令牌,客户端 SDK 对此都有良好支持。这种按需、短周期、限定受众的令牌,即使泄露,攻击者能利用的窗口也非常有限。
在集群治理层面,还可以借助准入控制做强制约束。自建集群可以用 ValidatingAdmissionPolicy 拒绝所有未显式声明 automountServiceAccountToken 字段的 Pod 创建请求;使用云厂商托管集群的,可以开启类似的策略引擎(如 OPA Gatekeeper、Kyverno)编写规则,比如禁止在 kube-system 之外的命名空间使用 default 账户、强制要求所有 Pod 的令牌有效期不超过四小时。配合 RBAC 收紧 default 账户的权限,两条防线一起构筑,就算某个环节失守,攻击者也难以拿到可用的凭证。
四、审计存量工作负载的实用方法
管控策略再好,也要先摸清存量现状。排查哪些 Pod 挂载了令牌,可以直接查看 Pod 的 spec 字段:
# 列出当前命名空间所有未关闭自动挂载的 Pod
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.automountServiceAccountToken}{"\t"}{.spec.serviceAccountName}{"\n"}{end}'
# 检查某个容器内是否真的存在令牌文件
kubectl exec -it web-static -- ls /var/run/secrets/kubernetes.io/serviceaccount/
jsonpath 输出为空或者显示 null 的,说明 Pod 没有显式设置字段,走的是默认挂载逻辑,这类就是需要优先处理的对象。建议把这类检查纳入 CI 流水线:所有经由 GitOps 或 CI 部署的 YAML,在合并前自动校验是否声明了 automountServiceAccountToken 或使用了投射卷令牌,从源头堵住配置漂移。对于已经在运行的工作负载,逐批修改后滚动重启即可,因为令牌卷的变更需要重建 Pod 才能生效。
最后提醒一点,关闭自动挂载只是手段,不是目的。真正的目标是让每个工作负载只持有完成任务所必需的最小凭证。把 default 账户的令牌挂载关掉、给需要访问 API 的应用发放短周期限定受众的令牌、再用准入策略守住底线,这三步走完,你的集群在凭证管理这一块的安全性会有一个质的提升。
Kubernetes服务账户令牌automountServiceAccountToken修改时间:2026-09-07 22:14:39