云原生学习路线导航页(持续更新中)
- kubernetes学习系列快捷链接
- Kubernetes架构原则和对象设计(一)
- Kubernetes架构原则和对象设计(二)
- Kubernetes架构原则和对象设计(三)
- Kubernetes控制平面组件:etcd(一)
- Kubernetes控制平面组件:etcd(二)
- Kubernetes控制平面组件:etcd常用配置参数
- Kubernetes控制平面组件:etcd高可用集群搭建
- Kubernetes控制平面组件:etcd高可用解决方案
- Kubernetes控制平面组件:Kubernetes如何使用etcd
- kubectl 和 kubeconfig 基本原理
- kubeadm 升级 k8s集群 1.17到1.20
- Kubernetes常见问题解答
- 查看云机器的一些常用配置
本文主要对kubernetes API Server 认证机制中的 ServiceAccount 认证进行介绍,包括ServiceAccount的设计理念、实现原理、认证流程,以及 API Server 对 ServiceAccount 的整个处理过程
1.ServiceAccount详解
1.1.ServiceAccount设计理念
ServiceAccount 是 Kubernetes 中为 Pod 内进程设计的身份标识,核心目标是安全、自动化地管理 Pod 与 API Server 的通信权限。其设计遵循以下原则:
-
最小权限原则
每个 ServiceAccount 仅被授予执行任务所需的最小权限,降低安全风险。 -
自动化凭证管理
自动挂载 Token 和 CA 证书到 Pod,无需手动管理敏感信息。 -
身份与访问分离
ServiceAccount 处理身份认证,RBAC 负责授权,职责分离提升安全性。 -
命名空间隔离
ServiceAccount 属于命名空间资源,天然支持多租户环境下的权限隔离。
1.2.ServiceAccount认证流程详解
-
ServiceAccount 创建
用户创建 ServiceAccount 时,Kubernetes 自动生成:- 一个身份标识(如
system:serviceaccount:<ns>:<name>
) - 关联的 Secret(包含 TLS 客户端证书和令牌,新版默认使用 TokenRequest API 动态签发)
- 一个身份标识(如
-
Pod 配置
在 Pod 的spec.serviceAccountName
字段指定 ServiceAccount 名称。若不指定,使用默认的default
。 -
凭证自动注入
Kubelet 在创建 Pod 时自动挂载以下文件到/var/run/secrets/kubernetes.io/serviceaccount
:ca.crt
: API Server 的 CA 证书namespace
: Pod 所属命名空间token
: JWT 令牌(用于身份认证)
-
API 请求认证
Pod 进程访问 API Server 时:- 使用
ca.crt
验证 API Server 证书合法性 - 在 HTTPS 请求头中添加
Authorization: Bearer <token>
- 使用
-
API Server 验证
- 解密 JWT 验证签名有效性
- 检查 Token 的受众(audience)、有效期等声明
- 提取身份信息
system:serviceaccount:<ns>:<sa-name>
-
RBAC 授权
API Server 根据 RBAC 规则验证该身份是否有权限执行操作。
1.3.完整示例:监控组件访问 Metrics API
1.3.1.创建 ServiceAccount
# monitoring-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:name: metrics-readernamespace: monitoring
1.3.2.定义 RBAC 权限
# metrics-reader-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:namespace: monitoringname: metrics-viewer
rules:
- apiGroups: ["metrics.k8s.io"]resources: ["pods", "nodes"]verbs: ["get", "list", "watch"]
1.3.3.绑定权限到 ServiceAccount
# metrics-reader-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: metrics-reader-bindingnamespace: monitoring
subjects:
- kind: ServiceAccountname: metrics-readernamespace: monitoring
roleRef:kind: Rolename: metrics-viewerapiGroup: rbac.authorization.k8s.io
1.3.4.部署使用该 SA 的 Pod
# monitoring-pod.yaml
apiVersion: v1
kind: Pod
metadata:name: metrics-collectornamespace: monitoring
spec:serviceAccountName: metrics-reader # 指定自定义SAcontainers:- name: collectorimage: bitnami/curl:latestcommand: ["sleep", "infinity"]
1.3.5.测试 API 访问
# 进入 Pod
kubectl exec -it metrics-collector -n monitoring -- sh# 获取 Token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)# 访问 Metrics API
curl https://kubernetes.default.svc/api/v1/namespaces/monitoring/pods \--header "Authorization: Bearer $TOKEN" \--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
1.4.关键进阶特性
-
Token 投影(Projected Volumes)
新版 Kubernetes 使用serviceAccountToken
卷类型动态生成令牌,提供:- 精确的受众声明(audience)
- 可配置的令牌有效期(默认 1 小时)
- 自动轮换机制
spec:containers:- name: appvolumeMounts:- name: kube-api-accessmountPath: /var/run/secrets/kubernetes.io/serviceaccountreadOnly: truevolumes:- name: kube-api-accessprojected:sources:- serviceAccountToken:path: tokenexpirationSeconds: 3600audience: api-server- configMap:name: kube-root-ca.crtitems:- key: ca.crtpath: ca.crt- downwardAPI:items:- path: namespacefieldRef:fieldPath: metadata.namespace
-
身份联邦(OIDC 集成)
可将 ServiceAccount 令牌与外部身份系统(如 AWS IAM、Azure AD)集成,实现跨系统身份联合。 -
审计日志关联
API Server 的审计日志会记录每个请求的 ServiceAccount 身份,便于安全审计。
1.5.最佳实践
-
避免使用 default ServiceAccount
始终为工作负载创建专用 ServiceAccount,禁止默认空权限账户被滥用。 -
定期轮换 Secret
对于使用 Secret 存储令牌的情况,定期重建 ServiceAccount 以触发 Secret 更新。 -
使用 Network Policies 限制访问
结合网络策略,只允许特定 Pod 访问 API Server,增加纵深防御。 -
监控异常 API 调用
通过监控系统检测高频失败认证请求,及时发现凭证泄露或滥用行为。
通过以上机制,Kubernetes 实现了安全、灵活的 workload 身份管理,成为云原生安全体系的重要基石。
2.API Server 如何处理 ServiceAccount
2.1.请求处理流程图
2.2.详细处理步骤解析
2.2.1.接收请求阶段
// k8s.io/apiserver/pkg/server/handler.go
func (d *APIServerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {// 1. TLS 握手验证if !d.isValidTLS(req) {return 401, "TLS verification failed"}// 2. 封装请求上下文ctx := req.Context()ctx = request.WithRequest(ctx, req)// 3. 进入认证流程resp, ok := d.AuthenticateRequest(ctx, req)
}
关键检查点:
- 验证 TLS 证书有效性(双向 TLS 场景)
- 检查请求头
Authorization: Bearer <token>
- 解析请求路径和方法(如
/api/v1/namespaces/default/pods
)
2.2.2.认证阶段
// k8s.io/apiserver/pkg/authentication/request/bearertoken/bearertoken.go
func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {// 1. 提取 Bearer Tokentoken := extractBearerToken(req.Header.Get("Authorization"))// 2. 调用 TokenReview 验证机制resp, ok, err := a.auth.AuthenticateToken(req.Context(), token)
}
JWT Token 解析流程:
-
拆分 Token 结构
解析 Header.Payload.Signature 三部分:// Header {"alg": "RS256","kid": "AXSA...VQ" }// Payload {"iss": "kubernetes/serviceaccount","sub": "system:serviceaccount:default:my-sa","aud": ["https://api.mycluster.local"],"exp": 1735689600,"iat": 1623945600 }
-
签名验证
使用 API Server 的私钥验证签名:# 查看公钥位置 ps aux | grep kube-apiserver | grep -- --service-account-key-file # 默认路径:/etc/kubernetes/pki/sa.pub
-
声明(Claims)验证:
iss
(Issuer):必须为kubernetes/serviceaccount
aud
(Audience):必须包含 API Server 的标识exp
(Expiration):检查令牌是否过期sub
(Subject):提取 ServiceAccount 身份
2.2.3.身份转换阶段
// k8s.io/apiserver/pkg/authentication/request/bearertoken/bearertoken.go
func convertTokenToUser(claims *jwt.Claims) (*user.DefaultInfo, error) {// 提取身份信息return &user.DefaultInfo{Name: "system:serviceaccount:"+namespace+":"+saName,Groups: []string{"system:serviceaccounts", "system:serviceaccounts:"+namespace},}, nil
}
生成的身份对象:
{"username": "system:serviceaccount:default:my-sa","groups": ["system:serviceaccounts","system:serviceaccounts:default"]
}
2.2.4.鉴权阶段(RBAC)
// k8s.io/apiserver/pkg/authorization/authorizer.go
func (r *RBACAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {// 1. 获取用户信息user, ok := request.UserFrom(ctx)// 2. 查询 ClusterRoleBinding/RoleBindingpolicies := r.getPoliciesForUser(user)// 3. 匹配访问规则for _, policy := range policies {if matches(policy, a) {return DecisionAllow, "allowed by role", nil}}return DecisionDeny, "no matching policy", nil
}
RBAC 规则匹配逻辑:
- 组合用户身份 + 请求动作(verb) + 资源类型
- 遍历所有相关的 Role/ClusterRole
- 检查是否存在匹配的
apiGroups/resources/verbs
规则
2.2.5.准入控制阶段
// k8s.io/apiserver/pkg/admission/chain.go
func (c *chainAdmissionHandler) Admit(ctx context.Context, a Attributes, o ObjectInterfaces) error {// 依次执行准入控制器for _, handler := range c.Handlers {if handler.Handles(a.GetOperation()) {err := handler.Admit(ctx, a, o)if err != nil {return err}}}return nil
}
关键准入控制器:
ServiceAccount
:自动注入 ServiceAccountSecurityContextDeny
:安全策略检查ResourceQuota
:资源配额验证PodSecurity
:Pod 安全标准检查
2.2.6.持久化操作阶段
// k8s.io/apiserver/pkg/registry/generic/registry/store.go
func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {// 1. 转换对象版本if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {return nil, err}// 2. 写入 etcdresult, err := e.Storage.Create(ctx, key, obj, out, ttl)// 3. 返回结果return out, nil
}
ETCD 操作路径:
# Pod 资源存储路径
/registry/pods/<namespace>/<pod-name># ServiceAccount 存储路径
/registry/serviceaccounts/<namespace>/<sa-name>
2.3.关键日志分析
2.3.1.API Server 日志示例
I0701 09:30:15.123456 1 authentication.go:63] "Authenticating request" path="/api/v1/namespaces/default/pods"
I0701 09:30:15.123789 1 jwt.go:105] "JWT authentication succeeded" user="system:serviceaccount:default:my-sa"
I0701 09:30:15.124123 1 rbac.go:117] "RBAC authorization" verb="list" resource="pods" allowed=true reason="allowed by RoleBinding metrics-reader-binding"
2.3.2.审计日志示例
{"kind": "Event","apiVersion": "audit.k8s.io/v1","level": "Metadata","auditID": "d1e4f5g6-h7i8-j9k0-l1m2n3o4p5q6","stage": "ResponseComplete","requestURI": "/api/v1/namespaces/default/pods","user": {"username": "system:serviceaccount:default:my-sa","groups": ["system:serviceaccounts", "system:serviceaccounts:default"]},"responseStatus": {"code": 200}
}
2.4.安全防护机制
2.4.1.Token 自动轮换
# 查看 projected token 有效期
kubectl get pod <pod-name> -o jsonpath='{.spec.volumes[?(@.name=="kube-api-access")].projected.sources[?(@.serviceAccountToken)].expirationSeconds}'# 手动触发轮换
kubectl delete pod <pod-name>
2.4.2.绑定 ServiceAccount 到特定 Pod
apiVersion: v1
kind: ServiceAccount
metadata:name: restricted-sa
automountServiceAccountToken: false # 禁止自动挂载
2.4.3.增强型 Token 验证
apiVersion: apiserver.config.k8s.io/v1
kind: APIServerConfiguration
authentication:serviceAccounts:issuer: "https://api.mycluster.local" # 严格校验 iss 声明jwksURI: "/openid/v1/jwks" # 自定义 JWKS 端点
2.5.典型错误场景分析
错误码 | 原因 | 排查方法 |
---|---|---|
401 | Token 格式错误 | kubectl get secret <sa-secret> -o jsonpath='{.data.token}' | base64 -d |
403 | RBAC 权限不足 | kubectl auth can-i list pods --as system:serviceaccount:default:my-sa |
500 | API Server 证书配置错误 | 检查 /etc/kubernetes/pki 目录下的证书文件 |
503 | ETCD 连接失败 | 检查 API Server 与 ETCD 的网络连通性 |
通过以上流程解析,可以清晰理解 API Server 处理 ServiceAccount Token 的完整生命周期。实际生产环境中建议结合审计日志和监控系统进行深度追踪。