Kubernetes ServiceAccount 详解

1. ServiceAccount 基本概念

ServiceAccount(服务账户)是 Kubernetes 中用于为工作负载(如 Pod)提供身份认证的机制,它代表了应用程序或服务的身份,而不是个人用户的身份。

1.1 ServiceAccount 的主要作用

  • 为 Pod 中运行的进程提供身份标识
  • 控制对 Kubernetes API 的访问权限
  • 管理与外部系统集成时的身份认证
  • 实现 Pod 间的安全通信

1.2 与 UserAccount 的区别

特性 ServiceAccount UserAccount
用途 集群内部工作负载身份 集群外部用户身份
创建位置 集群内部 集群外部(由外部IDP管理)
命名空间 属于特定命名空间 集群全局
前缀 自动添加 system:serviceaccount: 无特定前缀
典型使用者 Pod中的应用程序 人类用户或外部服务

2. ServiceAccount 的 YAML 结构

基本 ServiceAccount 定义示例:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-serviceaccount
  namespace: default
  annotations:
    example.com/description: "Service account for XYZ service"
automountServiceAccountToken: true
secrets:
- name: my-serviceaccount-token-xyz12
imagePullSecrets:
- name: my-registry-key

2.1 关键字段说明

  1. metadata:

    • name: ServiceAccount 名称
    • namespace: 所属命名空间
    • annotations: 可添加自定义注解
  2. automountServiceAccountToken:

    • 布尔值,控制是否自动挂载 API 访问令牌
    • 默认值在 kube-apiserver 的 --service-account-default-token 参数中设置
  3. secrets:

    • 关联的 Secret 对象列表
    • 通常包含访问令牌
  4. imagePullSecrets:

    • 用于拉取私有镜像的 Secret 引用

3. ServiceAccount 与相关资源的联系

3.1 与 Pod 的关系

  • 每个 Pod 都与一个 ServiceAccount 关联
  • 如果没有显式指定,Pod 使用所在命名空间的默认 ServiceAccount (default)
  • Pod 规格中指定 ServiceAccount:
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: my-serviceaccount
  containers:
  - name: main
    image: my-image

3.2 与 Secret 的关系

  • 每个 ServiceAccount 通常有一个关联的 Secret
  • Secret 包含以下关键信息:
    • ca.crt: 集群的 CA 证书
    • namespace: Pod 所在的命名空间
    • token: 用于认证的 Bearer token
  • 自动生成的 Secret 命名格式: <serviceaccount-name>-token-<random-suffix>

3.3 与 RBAC 的关系

  • 通过 RoleBinding/ClusterRoleBinding 将权限授予 ServiceAccount
  • 示例 RoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-reader-binding
  namespace: default
subjects:
- kind: ServiceAccount
  name: my-serviceaccount
  namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

3.4 与 API Server 的关系

  • API Server 验证 ServiceAccount token
  • 认证流程:
    1. Pod 使用挂载的 token 向 API Server 发起请求
    2. API Server 验证 token 的有效性
    3. API Server 检查关联的 RBAC 规则

3.5 与 Admission Controllers 的关系

  • ServiceAccount 准入控制器负责:
    • 为新建的 Pod 设置默认 ServiceAccount
    • 确保引用的 ServiceAccount 存在
    • 自动挂载 ServiceAccount token
  • TokenRequest 准入控制器处理 token 的创建和更新

4. ServiceAccount 的生命周期

4.1 创建

kubectl create serviceaccount my-sa
# 或通过 YAML 文件
kubectl apply -f sa.yaml

4.2 令牌管理

  • 自动创建: Kubernetes 1.24+ 使用 TokenRequest API 动态生成令牌
  • 手动创建: 可以创建 Secret 并关联到 ServiceAccount

4.3 更新

kubectl patch serviceaccount my-sa -p '{"imagePullSecrets": [{"name": "new-secret"}]}'

4.4 删除

kubectl delete serviceaccount my-sa

5. ServiceAccount 的使用场景

5.1 Pod 访问 Kubernetes API

apiVersion: v1
kind: Pod
metadata:
  name: api-client-pod
spec:
  serviceAccountName: api-client
  containers:
  - name: main
    image: api-client-image
    volumeMounts:
    - name: token-volume
      mountPath: "/var/run/secrets/kubernetes.io/serviceaccount"
      readOnly: true
  volumes:
  - name: token-volume
    projected:
      sources:
      - serviceAccountToken:
          path: token
          expirationSeconds: 3600
          audience: api

5.2 跨命名空间访问

# 在命名空间A中创建ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cross-ns-sa
  namespace: namespace-a

# 在命名空间B中创建RoleBinding引用该SA
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: allow-cross-ns-access
  namespace: namespace-b
subjects:
- kind: ServiceAccount
  name: cross-ns-sa
  namespace: namespace-a
roleRef:
  kind: Role
  name: namespace-b-reader
  apiGroup: rbac.authorization.k8s.io

5.3 私有镜像仓库认证

apiVersion: v1
kind: ServiceAccount
metadata:
  name: private-registry-user
imagePullSecrets:
- name: registry-credentials

6. 高级主题

6.1 TokenRequest API (Kubernetes 1.20+)

  • 更安全的动态令牌机制
  • 令牌具有有限的生命周期
  • 可以绑定到特定 Pod 或特定用途
apiVersion: v1
kind: Pod
metadata:
  name: token-request-pod
spec:
  containers:
  - name: main
    image: my-image
    volumeMounts:
    - name: token-volume
      mountPath: "/var/run/secrets/tokens"
  volumes:
  - name: token-volume
    projected:
      sources:
      - serviceAccountToken:
          path: token
          expirationSeconds: 600
          audience: "vault"

6.2 BoundServiceAccountTokenVolume

  • Kubernetes 1.21+ 默认启用
  • 提供更安全的令牌卷挂载方式
  • 令牌自动轮换且与 Pod 生命周期绑定

6.3 ServiceAccount Issuer Discovery

  • 允许外部系统验证 ServiceAccount token
  • 配置 API Server 参数:
    --service-account-issuer=https://kubernetes.default.svc
    --service-account-jwks-uri=https://kubernetes.default.svc/openid/v1/jwks
    --service-account-signing-key-file=/path/to/key
    

7. 安全最佳实践

  1. 最小权限原则:

    • 只授予必要的 RBAC 权限
    • 定期审计 ServiceAccount 的权限
  2. 令牌管理:

    • 使用 TokenRequest API 而非静态令牌
    • 设置适当的 expirationSeconds
    • 限制令牌的 audience
  3. Pod 配置:

    • 避免使用默认 ServiceAccount
    • 设置 automountServiceAccountToken: false 当不需要 API 访问时
  4. 网络策略:

    • 结合 NetworkPolicy 限制 Pod 的网络访问
    • 限制对 API Server 的访问
  5. 监控与审计:

    • 监控异常 API 访问
    • 定期轮换凭证

8. 常见问题排查

8.1 权限不足错误

# 检查 ServiceAccount 的权限
kubectl auth can-i <verb> <resource> --as=system:serviceaccount:<namespace>:<sa-name>

# 检查关联的 Role/RoleBinding
kubectl get rolebindings,clusterrolebindings --all-namespaces | grep <sa-name>

8.2 令牌挂载问题

# 检查 Pod 是否挂载了 token
kubectl exec <pod-name> -- ls /var/run/secrets/kubernetes.io/serviceaccount

# 检查 ServiceAccount 配置
kubectl get serviceaccount <sa-name> -o yaml

8.3 镜像拉取失败

# 检查 imagePullSecrets 是否正确关联
kubectl get serviceaccount <sa-name> -o jsonpath='{.imagePullSecrets}'

# 检查 Secret 是否存在且有效
kubectl get secret <secret-name> -o yaml

9. 实际示例

9.1 创建受限的 ServiceAccount

  1. 创建 ServiceAccount:
kubectl create serviceaccount restricted-sa
  1. 创建 Role:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-viewer
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
  1. 创建 RoleBinding:
kubectl create rolebinding restricted-sa-binding \
  --role=pod-viewer \
  --serviceaccount=default:restricted-sa \
  --namespace=default
  1. 测试 Pod:
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  serviceAccountName: restricted-sa
  containers:
  - name: test
    image: alpine
    command: ["sh", "-c", "sleep 3600"]

9.2 使用 ServiceAccount 访问 API

from kubernetes import client, config

# 自动加载 Pod 内的 ServiceAccount 凭证
config.load_incluster_config()

v1 = client.CoreV1Api()
print("Listing pods:")
ret = v1.list_namespaced_pod(namespace="default")
for i in ret.items:
    print(f"{i.metadata.name}")

10. 总结

ServiceAccount 是 Kubernetes 安全模型的核心组件,它:

  1. 为工作负载提供身份标识
  2. 通过 RBAC 实现精细的访问控制
  3. 支持安全的服务间通信
  4. 管理外部资源访问凭证

合理使用 ServiceAccount 可以:

  • 实现最小权限原则
  • 隔离不同工作负载
  • 提供可审计的服务身份
  • 增强集群整体安全性

理解 ServiceAccount 与 Pod、Secret、RBAC 等组件的关系,对于设计安全的 Kubernetes 架构至关重要。随着 Kubernetes 的发展,ServiceAccount 机制也在不断改进,如 TokenRequest API 的引入,使得身份管理更加安全和灵活。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐