跳到正文
复制成功
请遵守本站许可
报告
章节_文章 // 现场报告

文章编号: RL-SERVICEA

2026.10.15

ServiceAccount:Pod 身份、Token 投射与轮换

Chavy
Chavy
#Kubernetes#ServiceAccount#Token#安全
档案

摘要:为 Pod 配置 ServiceAccount 身份,理解 TokenRequest、投射卷、Token 轮换与 Audience,并通过最小权限和关闭自动挂载控制访问范围。

1. 为什么需要 ServiceAccount?#

先看一个实际场景。

假设我们部署了一个应用程序,它运行在 Kubernetes 的 Pod 中。

这个应用程序需要完成以下工作:

  1. 获取当前 Namespace 中所有 Pod。
  2. 监听 Pod 状态变化。
  3. 根据 Pod 状态执行一些自动化操作。

例如,一个自定义控制器需要持续监听 Deployment 和 Pod 的变化。

但是,Pod 本质上只是一个容器运行环境。容器本身并不会天然拥有访问 Kubernetes API 的身份。

因此,需要给 Pod 分配一个身份。

这个身份就是 ServiceAccount。

flowchart TB
    P["Kubernetes Pod"] -->|"运行"| A["业务程序 / Controller"]
    P -->|"serviceAccountName"| S["ServiceAccount<br/>dev-sa"]
    A -->|"携带 ServiceAccount Token"| K["Kubernetes API Server<br/>验证身份并检查 RBAC 权限"]

注意:

ServiceAccount 只提供身份,并不意味着它自动拥有访问所有 Kubernetes 资源的权限。

身份和权限仍然是两个独立的概念。

2. ServiceAccount 的基本结构#

2.1 ServiceAccount 是什么?#

ServiceAccount 是 Kubernetes 中的一种 API 资源。

它属于某个 Namespace。

查看当前集群中的 ServiceAccount:

PRTCL // BASH
Terminal window
kubectl get serviceaccount -A

也可以简写:

PRTCL // BASH
Terminal window
kubectl get sa -A

通常你会发现,每个 Namespace 默认都有一个名为 default 的 ServiceAccount。

例如:

PRTCL // BASH
Terminal window
kubectl get sa -n default

输出类似:

PRTCL // TEXT
NAME SECRETS AGE
default 0 10d

这里的 SECRETS 为 0 并不表示没有 Token。

较新版本 Kubernetes 默认采用 TokenRequest API 和投射卷机制为 Pod 提供短期 Token,而不是自动创建长期有效的 Secret。

2.2 每个 Namespace 为什么都有 default?#

当你创建一个 Namespace 时,Kubernetes 通常会自动创建一个名为 default 的 ServiceAccount。

如果创建 Pod 时没有指定 ServiceAccount:

PRTCL // YAML
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
spec:
containers:
- name: nginx
image: nginx

Kubernetes 会默认使用:

PRTCL // YAML
spec:
serviceAccountName: default

也就是说:

没有指定 ServiceAccount 的 Pod,默认使用所在 Namespace 的 default ServiceAccount。

可以通过以下命令查看 Pod 使用的身份:

PRTCL // BASH
Terminal window
kubectl get pod nginx \
-o jsonpath='{.spec.serviceAccountName}'

也可以查看完整配置:

PRTCL // BASH
Terminal window
kubectl describe pod nginx

关注:

PRTCL // TEXT
Service Account: default

2.3 创建自己的 ServiceAccount#

实际开发中,不建议所有应用程序都使用 default ServiceAccount。

应该根据应用程序的职责创建独立身份。

例如:

PRTCL // BASH
Terminal window
kubectl create serviceaccount dev-sa \
-n dev

也可以使用 YAML:

PRTCL // YAML
apiVersion: v1
kind: ServiceAccount
metadata:
name: dev-sa
namespace: dev

3. Pod 如何使用 ServiceAccount?#

创建 ServiceAccount 后,需要将它关联到 Pod。

3.1 指定 ServiceAccount#

PRTCL // YAML
apiVersion: v1
kind: Pod
metadata:
name: app
namespace: dev
spec:
serviceAccountName: dev-sa
containers:
- name: app
image: nginx

关键配置:

PRTCL // YAML
spec:
serviceAccountName: dev-sa

它告诉 Kubernetes:

这个 Pod 使用 dev Namespace 中的 dev-sa 作为自己的身份。

注意:ServiceAccount 和 Pod 必须位于同一个 Namespace。

3.2 Kubernetes 如何向 Pod 提供 Token?#

当 Pod 使用 ServiceAccount 时,Kubernetes 通常会自动将身份凭据挂载到容器内部。

默认挂载路径:

PRTCL // TEXT
/var/run/secrets/kubernetes.io/serviceaccount/

通常可以看到:

PRTCL // TEXT
ca.crt
namespace
token

三个文件的作用:

文件作用
tokenServiceAccount 身份认证凭据
ca.crt验证 API Server 的 TLS 证书
namespace当前 Pod 所属 Namespace

这里需要区分两个概念:

  • Token:证明自己是谁。
  • CA 证书:验证自己连接的 API Server 是否可信。

它们分别解决客户端身份认证和服务端身份验证问题。

4. ServiceAccount Token 的工作原理#

这个 Token 是谁生成的?如何进入 Pod?什么时候失效?

这涉及 Kubernetes 的 Token 投射机制。

4.1 传统方式:Secret Token#

在较早版本的 Kubernetes 中,创建 ServiceAccount 后,通常会自动创建一个长期有效的 Token Secret。

结构类似:

PRTCL // YAML
apiVersion: v1
kind: Secret
metadata:
name: dev-sa-token
namespace: dev
annotations:
kubernetes.io/service-account.name: dev-sa
type: kubernetes.io/service-account-token

Secret 中包含:

PRTCL // YAML
data:
ca.crt: ...
namespace: ...
token: ...

这种方式存在一些问题:

  • Token 生命周期较长。
  • Token 泄露后可能长期被滥用。
  • 需要额外管理 Token 的创建、存储和轮换。

因此,现代 Kubernetes 已经不再默认自动为每个 ServiceAccount 创建长期有效的 Token Secret。

4.2 现代方式:TokenRequest API#

从 Kubernetes 1.22 开始,Bound ServiceAccount Token 成为默认机制。

它通过 TokenRequest API 申请短期有效的 Token。

整个过程可以理解为:

flowchart TB
    P["Pod 创建<br/>serviceAccountName: dev-sa"] --> K["Kubelet"]
    K -->|"TokenRequest API 申请 Token"| S["API Server"]
    S -->|"签发绑定 ServiceAccount、Pod 的 Token"| K
    K -->|"写入并管理凭据"| V["Projected Volume"]
    V -->|"投射到容器文件系统"| A["应用程序读取 Token"]
    A -->|"携带 Token 访问 Kubernetes API"| S

这里有一个重要细节:

Token 并不是简单地由容器自己生成,而是由 Kubernetes API Server 签发,Kubelet 负责请求和管理投射到 Pod 中的凭据。

4.3 Token 的主要特点#

现代 ServiceAccount Token 通常具有以下特点:

  1. 有时效性:Token 有过期时间。
  2. 身份绑定:可以绑定到特定 Pod。
  3. 自动轮换:Kubelet 会在 Token 到期前更新投射的凭据。
  4. Audience 限制:Token 可以限定适用的接收方。

Token 的 JWT Payload 示例:

PRTCL // JSON
{
"iss": "https://kubernetes.default.svc",
"sub": "system:serviceaccount:dev:dev-sa",
"aud": [
"https://kubernetes.default.svc"
],
"exp": 1790000000,
"kubernetes.io": {
"namespace": "dev",
"serviceaccount": {
"name": "dev-sa",
"uid": "..."
},
"pod": {
"name": "app",
"uid": "..."
}
}
}

重点认识三个字段:

字段含义
subToken 对应的身份
audToken 的目标受众
expToken 过期时间

例如:

PRTCL // TEXT
system:serviceaccount:dev:dev-sa

表示:

  • system:serviceaccount:ServiceAccount 身份类型。
  • dev:Namespace。
  • dev-sa:ServiceAccount 名称。

注意:JWT Payload 解码不等于验证 Token 的真实性。真正的签名、有效期、受众及身份绑定校验由 API Server 等验证方完成。

4.4 Token 自动轮换#

假设一个 Pod 运行了 30 天,而 Token 的有效期远短于 30 天。

如果 Token 不会自动更新,应用程序就可能因为 Token 过期而无法访问 API Server。

Kubernetes 通过 Kubelet 的 Token 投射与轮换机制解决这个问题。

需要注意两点:

  • Kubelet 会更新投射卷中的 Token 文件。
  • 应用程序应当重新读取 Token 文件,不能一直使用启动时读取并缓存的旧 Token。

使用 Kubernetes 官方客户端库时,通常可以利用客户端库提供的凭据刷新能力。

如果是自己编写的程序,就需要正确处理文件更新和重新读取。

5. Projected Volume:理解 Token 投射#

前面提到了 Projected Volume,它是 Kubernetes 将多个数据源组合到同一个目录中的一种机制。

ServiceAccount Token 就是其中一种典型应用。

5.1 查看 Pod 的 Token 挂载#

PRTCL // BASH
Terminal window
kubectl describe pod app -n dev

关注:

PRTCL // TEXT
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-xxxxx (ro)
Volumes:
kube-api-access-xxxxx:
Type: Projected

这说明 Kubernetes 使用 Projected Volume 为 Pod 提供访问 API Server 所需的凭据。

它可能组合以下数据源:

  • ServiceAccount Token。
  • API Server CA 证书。
  • Pod 所属 Namespace 信息。

5.2 自定义 Projected Volume#

除了默认挂载方式,我们还可以自己定义 Token 投射。

例如:

PRTCL // YAML
apiVersion: v1
kind: Pod
metadata:
name: projected-token
namespace: dev
spec:
serviceAccountName: dev-sa
containers:
- name: app
image: nginx
volumeMounts:
- name: token-volume
mountPath: /var/run/custom-token
readOnly: true
volumes:
- name: token-volume
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: https://kubernetes.default.svc
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt

这里:

配置含义
serviceAccountToken请求投射 ServiceAccount Token
pathToken 在挂载目录中的文件名
expirationSeconds请求的 Token 有效期
audienceToken 的目标受众
configMap投射 CA 证书

注意:

expirationSeconds 是请求的有效期,实际签发期限由 API Server 配置和策略决定。通常不能设置低于 600 秒的请求期限。

自定义 Token 投射与默认 Token 挂载可以并存,但如果自定义 Token 已经满足应用需求,应该避免无意义地挂载多份凭据。

6. 如何禁止 Pod 自动挂载 Token?#

这是一个非常重要的安全知识点。

不是所有 Pod 都需要访问 Kubernetes API。

例如:

  • Nginx Web 服务。
  • 普通 Java 微服务。
  • 静态资源服务。
  • 不需要访问集群 API 的任务容器。

这些应用如果没有 API 访问需求,就不应该默认获得 ServiceAccount Token。

6.1 在 Pod 中关闭#

PRTCL // YAML
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
automountServiceAccountToken: false
containers:
- name: nginx
image: nginx

6.2 在 ServiceAccount 中关闭#

也可以在 ServiceAccount 层面设置:

PRTCL // YAML
apiVersion: v1
kind: ServiceAccount
metadata:
name: dev-sa
namespace: dev
automountServiceAccountToken: false

这样,使用这个 ServiceAccount 的 Pod 默认不会自动挂载 Token。

如果 Pod 和 ServiceAccount 都设置了该字段,Pod 级别的设置优先。

生产环境建议

遵循最小权限原则:

  • 不需要访问 API 的 Pod:关闭自动挂载。
  • 需要访问 API 的 Pod:使用专属 ServiceAccount。
  • 只授予完成业务所需的最小 RBAC 权限。

7. Pod 如何真正访问 Kubernetes API Server?#

假设有一个应用程序运行在 Pod 中,它需要获取 dev Namespace 中的 Pod 列表。

完整过程是:

flowchart TB
    P["Pod 使用 dev-sa ServiceAccount"] --> K["Kubelet 将 Token 投射并挂载到 Pod"]
    K --> A["应用程序读取 Token"]
    A -->|"HTTPS 请求携带 Bearer Token"| S["API Server 验证 Token"]
    S --> R["RBAC 检查权限"]
    R -->|"授权成功"| D["返回 dev Namespace 的 Pod 列表"]

7.1 Kubernetes API Server 地址#

在 Pod 内部,通常可以通过 Kubernetes Service 访问 API Server:

PRTCL // TEXT
https://kubernetes.default.svc

查看 Kubernetes Service:

PRTCL // BASH
Terminal window
kubectl get svc kubernetes

输出类似:

PRTCL // TEXT
NAME TYPE CLUSTER-IP
kubernetes ClusterIP 10.96.0.1

其中:

  • kubernetes 是默认 Service 名称。
  • default 是 Namespace。
  • svc 是 Kubernetes Service DNS 域名的一部分。

7.2 给 ServiceAccount 授予权限#

先创建一个 Role:

PRTCL // YAML
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: dev
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]

再创建 RoleBinding:

PRTCL // YAML
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-sa-pod-reader
namespace: dev
subjects:
- kind: ServiceAccount
name: dev-sa
namespace: dev
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

7.3 使用 Token 调用 API Server#

Pod 内部使用 Token 调用 API 的命令示例:

PRTCL // BASH
Terminal window
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

访问 API Server:

PRTCL // BASH
Terminal window
curl --cacert "$CACERT" \
-H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/dev/pods

如果权限配置正确,API Server 会返回 JSON 格式的 Pod 列表。

8. ServiceAccount Token 的手动生成与管理#

8.1 手动生成 Token#

PRTCL // BASH
Terminal window
kubectl create token dev-sa -n dev

指定有效期:

PRTCL // BASH
Terminal window
kubectl create token dev-sa \
-n dev \
--duration=1h

指定 Audience:

PRTCL // BASH
Terminal window
kubectl create token dev-sa \
-n dev \
--audience=https://kubernetes.default.svc

这个命令通过 TokenRequest API 申请 Token。

注意:手动生成的 Token 默认不是绑定到某个 Pod 的 Token,不能简单地认为它与 Pod 自动挂载的 Token 完全相同。

8.2 查看 ServiceAccount#

PRTCL // BASH
Terminal window
kubectl get sa -n dev
PRTCL // BASH
Terminal window
kubectl describe sa dev-sa -n dev

8.3 查看 Pod 使用的 ServiceAccount#

PRTCL // BASH
Terminal window
kubectl get pod app -n dev \
-o jsonpath='{.spec.serviceAccountName}'

8.4 查看 ServiceAccount 对应的权限#

PRTCL // BASH
Terminal window
kubectl auth can-i --list \
--as=system:serviceaccount:dev:dev-sa \
-n dev

8.5 查看 Token 是否自动挂载#

PRTCL // BASH
Terminal window
kubectl get pod app -n dev \
-o jsonpath='{.spec.automountServiceAccountToken}'

注意:如果输出为空,不代表关闭了自动挂载,而是表示 Pod 没有显式设置该字段,需要继续检查 ServiceAccount 配置及默认行为。

9. 生产环境中的 ServiceAccount 设计#

假设有一个实际的微服务集群:

应用需要的权限
Nginx不需要访问 API
日志采集器根据需求读取 Pod 元数据
Prometheus根据采集方式读取监控相关资源
自定义 Controller读取和更新指定资源
CI/CD Agent根据发布流程创建或更新资源

合理的设计方式:

9.1 最小权限设计#

flowchart LR
    N["Nginx"] --> N1["automountServiceAccountToken: false<br/>不挂载 Token"]
    P["Prometheus"] --> P1["独立 ServiceAccount + 只读 RBAC<br/>只读权限"]
    C["Controller"] --> C1["独立 ServiceAccount + 精确资源授权<br/>按需授权"]

生产环境尤其需要注意:

  1. 不要让所有 Pod 共用具有高权限的 ServiceAccount。
  2. 不要随意创建长期有效的 ServiceAccount Token Secret。
  3. 不需要访问 API Server 的应用应关闭 Token 自动挂载。
  4. 尽量限制 Token 的 Audience 和有效期。
  5. 不要将 Token 写入镜像、代码仓库或日志。
  6. 对 Controller 等程序,精确限制其可操作的资源和 Namespace。
归档于 2026.10.15
// 档案结束
Classified
章节_06
协议编号: CC-BY-NC-SA-4.0

ServiceAccount:Pod 身份、Token 投射与轮换

作者:Chavy发布日期:2026.10.15

基于 CC BY-NC-SA 4.0

© 2026 Chavy
Powered by Astro