在Kubernetes 1.32及以上版本中,ServiceAccount(SA)的令牌生成机制发生了重要变化。传统通过直接读取/var/run/secrets/kubernetes.io/serviceaccount/token的方式在新版本中不再适用,这给需要从集群外部使用SA凭证的场景带来了新的挑战。
这个问题的本质在于:从v1.32开始,Kubernetes默认启用了BoundServiceAccountTokenVolume特性,使得自动挂载的SA token变为有时间限制的投影卷(Projected Volume)。这种token的有效期通常较短(默认1小时),且无法直接作为长期凭证使用。
实际场景中,我们经常需要:
这些场景都需要稳定的长期凭证,而新版Kubernetes的默认机制无法满足这个需求。因此,我们需要一套可靠的手动生成长期有效SA token的方案。
在Kubernetes 1.32之前,SA token是作为不透明的静态字符串存储在Secret中的。从安全角度看,这种设计存在明显缺陷:
新版本引入了ServiceAccountTokenVolumeProjection特性,将token改为动态生成的JWT格式,包含标准声明:
json复制{
"iss": "kubernetes/serviceaccount",
"sub": "system:serviceaccount:namespace:sa-name",
"exp": 1735689600,
"iat": 1600000000
}
要生成长期有效的SA token,我们需要:
关键点在于理解Kubernetes的TokenRequest API,这是新版集群中生成token的标准方式。与旧版直接创建Secret不同,我们需要通过API Server的/api/v1/namespaces/{namespace}/serviceaccounts/{name}/token端点来获取凭证。
确保你的环境满足:
验证集群版本:
bash复制kubectl version --short | grep Server
首先创建专用的SA(示例中使用monitoring作为namespace):
yaml复制# external-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-access
namespace: monitoring
应用配置:
bash复制kubectl apply -f external-sa.yaml
根据实际需求创建Role和RoleBinding。以下是允许读取所有pod的示例:
yaml复制# external-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: monitoring
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: monitoring
subjects:
- kind: ServiceAccount
name: external-access
namespace: monitoring
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
应用RBAC配置:
bash复制kubectl apply -f external-role.yaml
现在通过TokenRequest API生成token。有两种实现方式:
方法一:使用kubectl创建TokenRequest
bash复制kubectl create token external-access \
--namespace monitoring \
--duration 8760h > external-sa-token.txt
方法二:通过API直接请求
bash复制kubectl proxy --port=8080 &
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"spec": {
"audiences": ["api"],
"expirationSeconds": 31536000
}
}' \
http://localhost:8080/api/v1/namespaces/monitoring/serviceaccounts/external-access/token | jq -r '.status.token' > external-sa-token.txt
关键参数说明:
--duration:token有效期(8760h=1年)audiences:指定token的目标受众expirationSeconds:过期时间(秒)注意:生产环境建议设置更短的过期时间(如30天),并建立自动轮换机制
获取集群CA证书:
bash复制kubectl get secret \
$(kubectl get sa external-access -n monitoring -o jsonpath='{.secrets[0].name}') \
-n monitoring -o jsonpath='{.data.ca\.crt}' | base64 --decode > ca.crt
使用token访问API:
bash复制curl --cacert ca.crt \
-H "Authorization: Bearer $(cat external-sa-token.txt)" \
"https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/monitoring/pods"
某些场景下需要限制token的使用范围:
bash复制kubectl create token external-access \
--namespace monitoring \
--audience "custom-api" \
--duration 720h
对应的API Server需要配置--api-audiences参数包含这个值。
对于生产环境,建议实现token自动轮换。基本思路:
示例CronJob:
yaml复制apiVersion: batch/v1
kind: CronJob
metadata:
name: token-rotator
namespace: monitoring
spec:
schedule: "0 0 * * 0" # 每周执行
jobTemplate:
spec:
template:
spec:
serviceAccountName: token-rotator
containers:
- name: kubectl
image: bitnami/kubectl
command:
- /bin/sh
- -c
- |
kubectl create token external-access \
--namespace monitoring \
--duration 168h > /tmp/token && \
kubectl create secret generic external-token \
--namespace monitoring \
--from-file=token=/tmp/token \
--dry-run=client -o yaml | \
kubectl apply -f -
restartPolicy: OnFailure
对于更复杂的场景,可以集成Cert-Manager:
始终遵循:
建议配置:
bash复制# 查看SA的token使用情况
kubectl get --raw /metrics | grep serviceaccount
# 审计日志过滤
kubectl logs -n kube-system kube-apiserver-xxx | grep TokenReview
通过NetworkPolicy限制token的使用来源:
yaml复制apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-external-access
namespace: monitoring
spec:
podSelector:
matchLabels:
app: external-system
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 443
典型错误:
code复制Unauthorized: Invalid credentials
检查步骤:
bash复制echo $(cat external-sa-token.txt) | cut -d'.' -f2 | base64 -d | jq .exp
bash复制kubectl get sa -n monitoring external-access
bash复制kubectl get rolebinding -n monitoring read-pods -o yaml
错误示例:
code复制Forbidden: User "system:serviceaccount:monitoring:external-access" cannot list resource "pods" in API group "" in the namespace "default"
解决方案:
bash复制kubectl auth can-i list pods \
--as system:serviceaccount:monitoring:external-access \
-n monitoring
如果遇到:
code复制the server could not find the requested resource
需要确认:
bash复制ps aux | grep kube-apiserver | grep service-account
应包含:code复制--service-account-issuer=kubernetes.default.svc
--service-account-signing-key-file=/etc/kubernetes/pki/sa.key
--service-account-api-audiences=api
| 特性 | 静态Token(1.31-) | 动态Token(1.32+) |
|---|---|---|
| 有效期 | 无限 | 可配置 |
| 自动轮换 | 不支持 | 支持 |
| 安全性 | 较低 | 较高 |
| 使用复杂度 | 简单 | 中等 |
| 集群外使用 | 直接可用 | 需手动生成 |
| 方案 | 适用场景 | 维护成本 | 安全性 |
|---|---|---|---|
| SA Token | 简单脚本 | 低 | 中 |
| Kubeconfig | 开发者本地 | 中 | 中 |
| OIDC集成 | 企业SSO | 高 | 高 |
| 代理Sidecar | 集群内服务 | 中 | 高 |
在实际项目中,我通常会根据安全要求和维护成本做权衡。对于临时性的外部访问,手动生成的SA token是最快捷的方案;而对于生产级系统,建议考虑OIDC或专门的认证代理方案。