16.7.2 ServiceAccount与认证机制

ServiceAccount 是 Kubernetes 中为 Pod 提供身份标识的内置对象,用于在集群内与 API Server 交互完成认证与授权。每个命名空间默认包含一个 default ServiceAccount,未显式指定时 Pod 会使用它。ServiceAccount 与 RBAC 一起决定“是谁”以及“能做什么”,其核心载体为 Token 与 CA 证书,挂载到 Pod 文件系统中供应用调用。

原理草图(认证与授权链路):

文章图片

1. 基础操作与示例#

1.1 创建专用 ServiceAccount#

# 创建命名空间
kubectl create ns dev

# 创建 ServiceAccount
kubectl -n dev create sa app-sa

# 查看 SA 与自动生成的 Token
kubectl -n dev get sa app-sa -o yaml

1.2 最小权限 RBAC 绑定(示例仅允许读取 Pod)#

# 文件: rbac-read-pods.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: dev
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-reader-binding
  namespace: dev
subjects:
- kind: ServiceAccount
  name: app-sa
  namespace: dev
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f rbac-read-pods.yaml

1.3 在 Pod 中指定 ServiceAccount 并测试权限#

# 文件: pod-with-sa.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sa-test
  namespace: dev
spec:
  serviceAccountName: app-sa
  containers:
  - name: curl
    image: curlimages/curl:8.6.0
    command: ["sleep","3600"]
kubectl apply -f pod-with-sa.yaml

# 进入 Pod,读取 Token 并访问 API Server
kubectl -n dev exec -it sa-test -- sh -c '
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
APISERVER=https://kubernetes.default.svc
curl -s --cacert $CACERT -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces/dev/pods | head
'

预期效果:返回 dev 命名空间内 Pod 列表(JSON),证明 RBAC 生效。

1.4 禁用自动挂载 Token(降低暴露面)#

# 文件: pod-no-token.yaml
apiVersion: v1
kind: Pod
metadata:
  name: no-token
  namespace: dev
spec:
  automountServiceAccountToken: false
  containers:
  - name: busybox
    image: busybox:1.36
    command: ["sleep","3600"]
kubectl apply -f pod-no-token.yaml
kubectl -n dev exec -it no-token -- ls /var/run/secrets/kubernetes.io/serviceaccount
# 预期输出:目录不存在或为空

2. Bound ServiceAccount Token(短期令牌)示例#

新版集群推荐使用短期投影 Token,降低长期 Token 泄露风险:

# 文件: pod-projected-token.yaml
apiVersion: v1
kind: Pod
metadata:
  name: projected-token
  namespace: dev
spec:
  serviceAccountName: app-sa
  containers:
  - name: curl
    image: curlimages/curl:8.6.0
    command: ["sleep","3600"]
    volumeMounts:
    - name: sa-token
      mountPath: /var/run/secrets/tokens
  volumes:
  - name: sa-token
    projected:
      sources:
      - serviceAccountToken:
          path: api-token
          expirationSeconds: 3600
          audience: api
kubectl apply -f pod-projected-token.yaml
kubectl -n dev exec -it projected-token -- \
  sh -c 'ls /var/run/secrets/tokens && wc -c /var/run/secrets/tokens/api-token'

预期效果:Token 文件存在且为短期令牌(1小时过期)。

3. 常见问题与排错(含命令解释)#

1) 认证失败(401)

# 检查 Pod 的 SA
kubectl -n dev get pod sa-test -o jsonpath='{.spec.serviceAccountName}'

# 检查 SA 是否存在
kubectl -n dev get sa app-sa

# 检查 Token 是否挂载
kubectl -n dev exec -it sa-test -- ls /var/run/secrets/kubernetes.io/serviceaccount

原因:ServiceAccount 删除/未挂载 Token/Token 过期。

2) 权限不足(403)

# 查看 Role/RoleBinding
kubectl -n dev get role,rolebinding

# 模拟权限判定(解释:验证 SA 是否允许 list pods)
kubectl auth can-i list pods --as system:serviceaccount:dev:app-sa -n dev

原因:Role/RoleBinding 绑定错误或权限不足。

3) Token 挂载异常

# 检查 Pod 是否禁用自动挂载
kubectl -n dev get pod sa-test -o jsonpath='{.spec.automountServiceAccountToken}'

# 检查集群是否启用了相关 Admission 插件
kubectl -n kube-system get cm kube-apiserver -o yaml | grep -i admission

原因:automountServiceAccountToken 被禁用或 Admission 配置影响。

4. 认证机制与外部 OIDC 简述(关键点)#

  • 集群内工作负载最常用 ServiceAccount Token。
  • 集群外用户可通过 OIDC/x509/Webhook 等认证方式。
  • 必须明确区分“集群内 SA 认证链”与“外部用户认证链”。

5. 练习#

1) 在 dev 命名空间创建 log-sa,只允许读取 pods/log
2) 编写 Pod 使用 log-sa,验证 GET /api/v1/namespaces/dev/pods 是否被拒绝,GET /pods/<name>/log 是否被允许。
3) 将 Pod 的 automountServiceAccountToken 设为 false,验证 Token 不再挂载。
4) 使用 kubectl auth can-i 验证最小权限是否正确生效。

以上实践可在保障应用访问能力的同时,显著提升集群安全性与合规性。