资讯动态

K8s 1.32手动生成ServiceAccount Token:短期与长期方案详解

发布时间:2026/10/21 3:00:38 来源:尧图企业网站定制
做K8s运维的朋友应该都遇到过这类需求CI/CD流水线要调用集群API做发布监控系统要采集集群指标或者外部应用需要读集群资源状态。最直接的办法就是给外部系统准备一个ServiceAccount后面统一叫SA的token让它拿着这个token通过API Server认证。如果你的集群还是1.24之前的旧版本事情很简单——创建SA之后系统会自动生成一个对应Secrettoken就在里面躺着复制出来就能用。但从1.24开始这个默认行为被砍掉了到了K8s 1.32及之后的版本你创建完SA之后等半天也看不到那个带token的Secret。我最初也在这个地方卡了很久查文档、翻源码才搞明白。这篇文章就用我实际踩坑的经历把K8s 1.32以上集群手动生成SA token的方法一次讲清楚包括短期token和长期token两条路线怎么选、怎么组装外部kubeconfig、以及那些文档里不会写的坑。无论你是刚接触K8s的新手还是正在帮外部系统对接集群的老人这篇都值得收藏。1. 为什么新版集群不再自动生成SA token1.1 旧机制里到底发生了什么在1.24之前的版本里只要执行kubectl create sa xxx控制平面里的一个控制器就会自动创建一个同名的Secret这个Secret的type是kubernetes.io/service-account-token里面data.token字段就是SA的长期token。这个token没有过期时间任何持有它的人都能以这个SA的身份调用API。更关键的是Pod在创建时如果指定了SA且没有关闭自动挂载kubelet会把对应Secret挂载到Pod里的/var/run/secrets/kubernetes.io/serviceaccount/token容器内应用读这个文件就能访问集群API。这套机制用了很多年但它有两个越来越让安全团队头疼的问题一是token长期有效一旦泄漏很难控制损失只能靠删SA或者改权限来兜底二是token跟Secret绑定没法按需设置有效期和用途边界审计和轮换都麻烦。1.2 TokenRequest API是替代方案为了解决上面这些问题K8s引入了TokenRequest API核心思路是从给一个无限期的静态token变成按需签发一个短期token。在一个大版本周期内这个API迭代成熟后官方就决定从1.24开始默认不再自动创建SA对应的Secret。现在Pod里的serviceaccount挂载方式也变了默认走的是投影卷Projected Volumekubelet会周期性地通过TokenRequest向API Server请求一个短时token写进只读文件里。Pod内的应用拿到的token通常过期时间是一小时左右kubelet会在过期前自动续期旧token直接失效。这样即使某个Pod的token被拿走了风险窗口也被压缩得很小。1.3 1.32里手动创建Secret还灵不灵我一开始以为1.32彻底禁用了老方式后来实测发现并没有。虽然系统不再自动创建一个全新Secret但如果你手动声明一个Secret设置好type字段和对应annotation控制平面里的旧控制器仍然会识别它帮你在Secret里填充token。这意味着现在有两条并行的路可以走一条是走TokenRequest API拿短期token适合单个任务、短生命周期场景另一条是手动建Secret继续拿长期token适合那些没法频繁续期的外部系统。两条路我都验证过1.32版本下都能正常使用。2. 动手前先理清权限和服务身份2.1 SA、Secret、RBAC这三层关系拿到一个token之后它在K8s里的身份映射是这样的token本身只是凭证API Server解出token里的信息后会确认它对应的是哪个SA然后用这个SA绑定的RBAC规则来决定能做什么操作。所以整个链路是token证明你是哪个SA → 该SA在哪个命名空间 → 该命名空间下RBAC绑定了哪些权限。我在实操中见过不少token生成对了但还是调不通的案例最后查下来基本都是RBAC这一层没配置好。比如token对应的SA在prod命名空间但只给这个SA绑定了dev命名空间的Role那调prod的API自然就是403。2.2 给外部系统一个清晰的最小权限不管外部系统是只读巡检还是需要触发Job都建议先画清楚它到底要调哪些API然后按API分组去设计RBAC。最省事的做法当然是直接绑cluster-admin但这是运维事故的高发源头千万别图省事。下面是一个只读权限的示例适合监控巡检类外部系统kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: external-readonly rules: - apiGroups: [] resources: [pods, services, endpoints, nodes, namespaces] verbs: [get, list, watch] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: external-readonly-binding subjects: - kind: ServiceAccount name: ci-reader namespace: ci # 改成你真实SA所在namespace roleRef: kind: ClusterRole name: external-readonly apiGroup: rbac.authorization.k8s.io如果外部系统只需要操作某一个命名空间的资源那就用Role和RoleBinding别用Cluster级别的权限范围越小越安全。2.3 短期token还是长期token按场景选这是我最常被问到的问题。我的建议其实很简单看外部系统能不能自己续期能自己续期的系统比如SDK支持读取kubelet投影token、或者定时任务可以每次调用TokenRequest再重新加载kubeconfig优先用短期token。没法改代码、只能塞一个固定token跑半年的老系统就只能用Secret方式拿长期token。CI/CD单次构建、一次性的数据迁移任务用kubectl create token指定一个像8小时或24小时的有效期就非常合适。下面这张表是我日常选型的参考对比维度kubectl create tokenSecret绑定SAtoken有效期默认1小时可通过--duration指定长期有效不自动过期轮换方式到期前重新生成删除重建Secret换取新token风险敞口小大泄漏后影响时间长适用场景CI/CD单次任务、短时操作无法改代码的外部系统官方推荐度推荐兼容保留不推荐新接入3. 手动生成SA token的完整实操3.1 准备阶段创建SA并绑定权限先建一个SA。我这里以外部监控系统要读取ci命名空间资源为例kubectl create ns ci kubectl create sa ci-reader -n ci创建完验证一下kubectl get sa ci-reader -n ci这时候你去kubectl get secret -n ci看会发现没有任何跟ci-reader相关的Secret这就是新版集群和旧版集群最直观的区别。然后把上面那套RBAC配置保存成rbac.yaml执行kubectl apply -f rbac.yaml这里有个细节容易忽略RBAC里的subjects用的是kind: ServiceAccount不是kind: User。我之前见过有同事把SA写成了User结果怎么弄都是权限不足因为这个API对象的subjects类型是固定的SA必须用ServiceAccount类型。3.2 方式Akubectl create token生成短期token新版kubectl自带了create token子命令这是最推荐的方式。命令如下kubectl create token ci-reader -n ci --duration8h --audiencehttps://kubernetes.default.svc参数解释一下--duration指定过期时间支持Go的时长格式比如6h、24h、720h。默认是1小时。--audience填API Server认可的受众标识。如果集群没做特殊配置填https://kubernetes.default.svc一般都能通过。执行完会直接输出一串JWT格式的token形如eyJhbGciOiJSUzI1NiIsImtpZCI6...很长一串。这个token可以直接用在kubeconfig里也可以放在HTTP请求的Authorization: Bearer头里。我在实际使用中一般是把token写进环境变量然后在脚本里用SA_TOKEN$(kubectl create token ci-reader -n ci --duration24h)这样后续脚本里直接用$SA_TOKEN就行不用复制粘贴也避免token出现在shell历史里。3.3 方式BSecret绑定SA生成长期token如果外部系统确实需要一个不过期的token就手动创建Secret并加上下面这个关键annotation。apiVersion: v1 kind: Secret metadata: name: ci-reader-token namespace: ci annotations: kubernetes.io/service-account.name: ci-reader type: kubernetes.io/service-account-token保存为secret.yaml后执行kubectl apply -f secret.yaml等几秒钟控制器会往这个Secret里写入token和ca.crt。验证一下kubectl get secret ci-reader-token -n ci -o yaml看到的data字段里应该有三项ca.crt、namespace、token。token字段的值是base64编码后的JWT字符串。提取token的命令kubectl get secret ci-reader-token -n ci -o jsonpath{.data.token} | base64 -d如果你在macOS上执行有些老版本的base64命令不认-d参数用--decode或者-D都可以。这里有两个坑要重点说。第一个Secret的type必须是kubernetes.io/service-account-token写错成Opaque的话控制器不会往里面填token。第二个annotation里的key是kubernetes.io/service-account.name不是service-account-name少一个点或者多了个横杠都不行。我有一次手滑把name拼成了names结果token字段空了十分钟排查的时候差点把集群翻了个底朝天。3.4 组装外部系统的kubeconfig外部系统拿到token之后通常会配一个独立的kubeconfig文件来访问集群。我一般这样组装先拿到API Server地址和CA数据kubectl cluster-info拿到类似https://192.168.1.100:6443这样的地址。然后kubectl config view --raw -o jsonpath{.clusters[0].cluster.certificate-authority-data}这一步拿到的也是一段base64编码的CA证书数据。到这里token、API Server地址、CA数据三样东西都齐了把它们拼成一个kubeconfig文件apiVersion: v1 kind: Config clusters: - name: external-cluster cluster: server: https://192.168.1.100:6443 certificate-authority-data: 上面拿到的base64数据 contexts: - name: external-context context: cluster: external-cluster namespace: ci user: external-user current-context: external-context users: - name: external-user user: token: SA token字符串这个文件可以放到外部系统对应的目录里比如/opt/monitor/kubeconfig或者挂在外部容器的某个安全目录。3.5 验证token能否正常访问集群配置文件写好后第一步先做本地验证kubectl --kubeconfigexternal-kubeconfig auth whoami输出里会显示USERNAME类似system:serviceaccount:ci:ci-reader说明token认证是靠SA的身份这个输出没问题的话再试一次资源操作kubectl --kubeconfigexternal-kubeconfig get pods -n ci能正常返回Pod列表说明整个链路已经通了。如果这一步报错大部分情况不是token有问题而是CA数据或者RBAC的问题下面第4部分会有排查清单。4. 常见问题与排查实录4.1 401 Unauthorizedaudience不匹配报错表现是HTTP 401token拿去curl API Server完全认不了。这个问题多见于用TokenRequest生成的短期token因为TokenRequest里会带audience字段而API Server校验时要求audience匹配。排查方式很简单看看你生成token时是不是指定了奇怪的audience。大多数集群用默认的https://kubernetes.default.svc就好。如果你实在不确定集群接受什么audience就用kubectl create token时不带--audience参数让它用默认值。4.2 token怎么7天就过期了有同事反馈用Secret绑定SA生成的token一周后就访问不了了。这里要区分一下如果你用的是kubectl create token方式默认1小时过期非常正常如果你用的是Secret方式但token还是过期了那就要检查是不是集群里开了额外的token老化策略比如某些配置或安全组件强制TTL。另外一个常见原因外部系统的kubeconfig里同时配置了token和client-certificate而证书过期时间更短表现上就像token失效了。建议外部kubeconfig里只保留token认证字段别混着配客户端证书。4.3 403 ForbiddenRBAC没生效401是身份不被认可403是身份认可但权限不足。这两个区别一定要分清。403的排查思路就是看RBACSA在哪个命名空间、绑定了哪个Role、Role里有没有对应资源的操作权限。我遇到过一种很隐蔽的情况用ClusterRoleBinding绑定了SA之后ClusterRole里的apiGroups没写对。比如要读DeploymentapiGroups得写apps即使同名的资源在多个apiGroup里写错组就是没权限。这类问题光看报错信息往往看不出来建议一条一条资源去试缩小范围。4.4 删除与吊销让一个token立刻失效长期token一旦泄漏第一反应就是赶紧让它失效。对Secret方式来说直接删掉Secretkubectl delete secret ci-reader-token -n ci删掉之后所有持有这个token的请求都会变成401。对短期token来说还没到过期时间就等它自然过期或者如果是因为SA被删导致最直接的办法是删掉绑定的SA同命名空间下所有相关token都会一并失效。4.5 base64解码在不同平台上的坑提取Secret里的token要base64解码大多数Linux发行版自带base64 -dmacOS自带的base64命令老版本只认-D有些用GNU coreutils的macOS环境则两个参数都认依具体环境而异。我在文章里统一用base64 -d如果你在macOS上报错改成base64 --decode或者base64 -D即可。别小看这一点我见过有同事直接复制走了base64编码后的原文当token用结果请求一直失败查了半天还怀疑是集群配置问题。5. 安全使用建议5.1 永远别绑cluster-admin给外部系统配权限时最容易犯的错误就是图省事绑个cluster-admin。我见过不止一次外部监控系统拿着cluster-admin token结果某天误操作把集群里重要的资源全删了。回头看这个系统的正常功能只需要读Pod和节点一个只读ClusterRole完全够用当时图方便留下的隐患出事之后整个团队连续加班了一周收拾残局。最小权限原则在这件事上不是一句口号外部系统的权限边界应该就覆盖它的正常业务范围多出一点点都是在放大风险。5.2 token的存放、传输与引用SA token的敏感程度等同于一套API凭据绝对不能直接写死在代码仓库或者镜像里。外部kubeconfig尽量放在专门的配置目录权限收紧到只有运行用户能读。传输的话如果系统支持优先走密钥管理系统下发而不是在聊天工具里转发明文token。生产环境里我还见过一个细节某些系统的日志会把HTTP头打出来Authorization: Bearer后面紧跟的就是完整token一次报错日志就能泄干净。如果外部系统是自己开发维护的建议在日志脱敏处理上把Authorization字段排除掉。5.3 轮换节奏与巡检短期token的轮换机制可以交给kubelet或者定时任务处理长期token就得人工规划轮换周期。我的经验是每90天轮换一次Secret绑定SA的token做法是新建一个新名字的Secret让外部系统切到新token后再删除旧Secret。这个过程可以做成脚本配合监控告警提示用到多少天。巡检方面我把下面几个SQL和维护动作固定在月度运维检查里罗列所有type为kubernetes.io/service-account-token的Secret标记出那些创建超过90天且不在白名单里的。检查ClusterRoleBinding里是否有绑定到外部SA的cluster-admin一有就报警。检查外部kubeconfig文件真实使用的token对应的SA是否仍然存在。这套巡检看起来琐碎但它能在token泄漏或者权限放大问题酿成大祸之前把风险提前暴露出来。我个人在实际运维中的体会是K8s从自动生成长期token走向按需签发短期token这个方向本身就是安全和易用性的一个平衡结果。短期token确实更安全但也逼着我们这些集成方把认证的逻辑理顺把token当成有生命周期的资源来管理。如果你在这篇文章里只记住一件事那就记住外部系统能用短期token就别用长期token能拿到最小权限就别贪图admin权限。真按这个原则走1.32集群里手动生成SA token这件小事就能既满足业务需求又守住安全底线。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑