3、ServiceAccount 与 Token:SA 创建、Token 自动挂载、TokenRequest API
在 Kubernetes 鉴权体系中,ServiceAccount(SA) 是 Pod 内进程与 API Server 交互的身份凭证。与 User Account 不同,SA 属于命名空间资源,专为工作负载设计。本章深入剖析 SA 的创建机制、传统 Secret 自动挂载的演进,以及现代 TokenRequest API 如何解决安全痛点。
3.1 ServiceAccount 核心概念与创建
ServiceAccount 是 Kubernetes 内置的“机器人账号”,用于为 Pod 中的进程提供身份标识。每个命名空间在创建时都会自动生成一个 default SA。
| 特性 | User Account | ServiceAccount |
|---|---|---|
| 作用域 | 集群级别(全局) | 命名空间级别 |
| 使用者 | 人类管理员/开发者 | Pod 内的进程 |
| 认证方式 | X.509 证书 / OIDC | Bearer Token(JWT) |
| 生命周期 | 长期存在 | 与工作负载绑定 |
| 自动挂载 | 不适用 | 默认挂载 Token 到 Pod |
default SA 运行生产工作负载。始终为每个应用或微服务创建专用的 ServiceAccount,并绑定最小权限的 RBAC 角色。
创建 ServiceAccount 的 YAML 示例:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: production
automountServiceAccountToken: false # 控制是否自动挂载 Token
使用 kubectl 创建并查看:
# 创建 SA
kubectl create serviceaccount my-app-sa -n production
# 查看 SA 详情(注意 Secrets 字段)
kubectl get serviceaccount my-app-sa -n production -o yaml
3.2 Token 自动挂载机制与演进
Kubernetes 早期版本(v1.20 之前)通过 Secret 自动生成并挂载长期有效的 ServiceAccount Token。这种机制存在严重安全风险:Token 永不过期,一旦泄露可被无限期滥用。
kubernetes.io/service-account-token)不具备过期时间,且存储在 etcd 中。如果 etcd 备份泄露或节点被攻破,攻击者可提取所有 SA Token。
从 v1.22 开始,Kubernetes 默认使用 TokenRequest API 生成绑定 Pod 生命周期的、有到期时间的 Token。自动挂载的 Token 不再是 Secret,而是由 kubelet 通过投射卷(Projected Volume)注入的 JWT。
3.3 TokenRequest API 深度解析
TokenRequest API 是 Kubernetes 认证子系统的核心扩展,位于 v1/authentication.k8s.io API 组。它允许请求者获取一个绑定到特定 ServiceAccount 和 Pod 的、有时间限制的 Token。
3.3.1 API 调用方式
直接通过 REST API 请求 Token:
POST /api/v1/namespaces/{namespace}/serviceaccounts/{name}/token
# 请求体示例
{
"apiVersion": "authentication.k8s.io/v1",
"kind": "TokenRequest",
"spec": {
"audiences": ["my-api-server"],
"expirationSeconds": 3600
}
}
3.3.2 关键参数说明
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
audiences |
[]string | Token 的目标受众(API Server 验证时会检查) | [kubernetes.default.svc] |
expirationSeconds |
int64 | Token 有效时长(秒) | 86400(1 天) |
boundObjectRef |
ObjectReference | 将 Token 绑定到特定 Pod 或 Secret | 无(不绑定) |
audiences 字段。例如,调用外部 Istio 服务时,设置 audiences: ["istio-ca"] 可防止 Token 被重放攻击。
3.3.3 手动请求 Token 示例
使用 kubectl 创建 Token:
# 创建绑定到 Pod 的 Token(有效期 1 小时)
kubectl create token my-app-sa -n production \
--duration=3600s \
--bound-object-kind=Pod \
--bound-object-name=my-pod-xyz
返回的 Token 结构:
{
"status": {
"token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9...",
"expirationTimestamp": "2025-04-10T12:00:00Z"
}
}
3.4 Pod 内 Token 挂载与使用
当 Pod 启动时,kubelet 自动执行以下操作:
- 检测 SA:读取 Pod 的
spec.serviceAccountName字段。 - 调用 TokenRequest:向 API Server 请求一个绑定到该 Pod 的 Token。
- 投射卷挂载:将 Token 写入
/var/run/secrets/kubernetes.io/serviceaccount/token。 - 注入 CA 证书:同时挂载集群 CA 证书,用于验证 API Server 的 TLS 连接。
Pod 内文件系统结构:
/var/run/secrets/kubernetes.io/serviceaccount/
├── ca.crt # 集群 CA 证书
├── namespace # 当前命名空间
└── token # JWT Token(自动刷新)
在应用代码中读取 Token:
# Python 示例
import os
token_path = "/var/run/secrets/kubernetes.io/serviceaccount/token"
with open(token_path, "r") as f:
token = f.read().strip()
# 使用 Token 调用 API Server
headers = {"Authorization": f"Bearer {token}"}
3.5 安全最佳实践与避坑指南
| 场景 | 推荐做法 | 避免 |
|---|---|---|
| Token 过期 | 使用 TokenRequest API 自动刷新 | 手动创建长期 Secret Token |
| 最小权限 | 为每个微服务创建独立 SA | 多个服务共享同一个 SA |
| Token 泄露 | 设置 audiences 限制使用范围 |
使用默认 audiences |
| 自动挂载 | 不需要 API 访问的 Pod 关闭自动挂载 | 所有 Pod 都挂载 Token |
| 审计 | 启用 API Server 审计日志记录 TokenRequest | 忽略 Token 使用记录 |
kubernetes.io/service-account.name: my-sa。但强烈建议迁移到 TokenRequest API。
3.6 故障排查与调试
常见问题及排查命令:
# 检查 SA 是否存在
kubectl get serviceaccount my-app-sa -n production
# 查看 Pod 使用的 SA
kubectl get pod my-pod -n production -o jsonpath='{.spec.serviceAccountName}'
# 验证 Token 是否挂载
kubectl exec my-pod -n production -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
# 检查 Token 过期时间(解码 JWT)
kubectl exec my-pod -n production -- \
python3 -c "import jwt; print(jwt.decode(open('/var/run/secrets/kubernetes.io/serviceaccount/token').read(), options={'verify_signature': False}))"
通过以上实践,你可以安全、高效地管理 ServiceAccount 与 Token,为后端鉴权拦截打下坚实基础。