ServiceAccount
概述
ServiceAccount是Kubernetes中用于为Pod提供身份认证的机制。每个Pod在创建时都会自动关联一个ServiceAccount,用于与Kubernetes API Server进行交互。ServiceAccount是Kubernetes安全模型的重要组成部分,它实现了Pod级别的身份认证和权限控制。
核心概念
1. ServiceAccount(服务账户)
ServiceAccount是Kubernetes中的一种资源类型,用于为Pod提供身份标识。每个命名空间都会自动创建一个名为default的ServiceAccount。
2. Token(令牌)
ServiceAccount使用JWT(JSON Web Token)进行身份认证。Token会被自动挂载到Pod中,供应用程序访问Kubernetes API使用。
3. 自动挂载
默认情况下,Kubernetes会将ServiceAccount的Token、CA证书和命名空间信息自动挂载到Pod的/var/run/secrets/kubernetes.io/serviceaccount/目录。
4. ImagePullSecrets
ServiceAccount可以关联镜像拉取密钥,用于从私有镜像仓库拉取镜像。
ServiceAccount工作原理
┌─────────────────────────────────────────────┐
│ Pod │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Application │ │
│ │ │ │
│ │ 使用Token访问API Server │ │
│ └──────────────┬────────────────────────┘ │
│ │ │
│ ▼ │
│ /var/run/secrets/kubernetes.io/serviceaccount/ │
│ ├── ca.crt (集群CA证书) │
│ ├── namespace (命名空间) │
│ └── token (JWT Token) │
└─────────────────────────────────────────────┘
│
│ 认证
▼
┌────────────────┐
│ API Server │
│ │
│ 验证Token │
│ 检查RBAC权限 │
└────────────────┘
│
│ 授权
▼
┌────────────────┐
│ RBAC │
│ (Role/RoleBinding) │
└────────────────┘YAML配置示例
示例1:创建ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: default示例2:在Pod中使用ServiceAccount
apiVersion: v1
kind: Pod
metadata:
name: my-pod
namespace: default
spec:
serviceAccountName: my-service-account
containers:
- name: app
image: nginx:latest
command:
- sh
- -c
- |
# 使用ServiceAccount Token访问API
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/default/pods示例3:禁用ServiceAccount Token自动挂载
apiVersion: v1
kind: ServiceAccount
metadata:
name: no-mount-sa
namespace: default
automountServiceAccountToken: false
---
apiVersion: v1
kind: Pod
metadata:
name: no-token-pod
spec:
serviceAccountName: no-mount-sa
containers:
- name: app
image: nginx:latest示例4:ServiceAccount关联ImagePullSecrets
apiVersion: v1
kind: Secret
metadata:
name: docker-registry-secret
namespace: default
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: private-registry-sa
namespace: default
imagePullSecrets:
- name: docker-registry-secret
---
apiVersion: v1
kind: Pod
metadata:
name: private-image-pod
spec:
serviceAccountName: private-registry-sa
containers:
- name: app
image: private-registry.example.com/myapp:latest示例5:完整的ServiceAccount配置
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
namespace: production
labels:
app: myapp
environment: production
annotations:
description: "ServiceAccount for myapp production deployment"
automountServiceAccountToken: true
imagePullSecrets:
- name: docker-registry-secret
secrets:
- name: app-secret示例6:创建Token Secret(Kubernetes 1.23及以下版本)
apiVersion: v1
kind: Secret
metadata:
name: my-service-account-token
namespace: default
annotations:
kubernetes.io/service-account.name: my-service-account
type: kubernetes.io/service-account-token示例7:使用TokenRequest API(Kubernetes 1.24+)
apiVersion: v1
kind: Pod
metadata:
name: token-pod
spec:
serviceAccountName: my-service-account
containers:
- name: app
image: nginx:latest
volumeMounts:
- name: token
mountPath: /var/run/secrets/tokens
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: my-token
expirationSeconds: 3600
audience: apikubectl操作命令
查看ServiceAccount
# 查看所有ServiceAccount
kubectl get serviceaccounts --all-namespaces
# 查看特定命名空间的ServiceAccount
kubectl get serviceaccounts -n default
# 查看ServiceAccount详细信息
kubectl describe serviceaccount my-service-account -n default
# 以YAML格式查看ServiceAccount
kubectl get serviceaccount my-service-account -n default -o yaml
# 查看ServiceAccount的Secret
kubectl get serviceaccount my-service-account -n default -o jsonpath='{.secrets}'创建和删除ServiceAccount
# 创建ServiceAccount
kubectl create serviceaccount my-service-account -n default
# 通过YAML文件创建
kubectl apply -f serviceaccount.yaml
# 删除ServiceAccount
kubectl delete serviceaccount my-service-account -n defaultToken管理
# 创建Token(Kubernetes 1.24+)
kubectl create token my-service-account -n default
# 创建指定有效期的Token
kubectl create token my-service-account -n default --duration=24h
# 创建指定受众的Token
kubectl create token my-service-account -n default --audience=api
# 查看Token内容(解码JWT)
kubectl create token my-service-account -n default | cut -d. -f2 | base64 -d
# 查看Secret(Kubernetes 1.23及以下版本)
kubectl get secrets | grep my-service-account
# 查看Secret内容
kubectl describe secret my-service-account-token-xxxxx -n default权限管理
# 为ServiceAccount创建RoleBinding
kubectl create rolebinding my-sa-binding \
--serviceaccount=default:my-service-account \
--role=pod-reader \
-n default
# 检查ServiceAccount权限
kubectl auth can-i list pods \
--as=system:serviceaccount:default:my-service-account \
-n default
# 列出ServiceAccount的所有权限
kubectl auth can-i --list \
--as=system:serviceaccount:default:my-service-account \
-n default
# 检查是否可以执行特定操作
kubectl auth can-i create deployments \
--as=system:serviceaccount:default:my-service-account \
-n default在Pod中测试ServiceAccount
# 进入Pod
kubectl exec -it my-pod -- sh
# 在Pod内查看Token
cat /var/run/secrets/kubernetes.io/serviceaccount/token
# 在Pod内查看命名空间
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
# 在Pod内访问API Server
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/default/pods
# 使用CA证书验证
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
curl --cacert $CACERT -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/default/podsImagePullSecrets管理
# 为ServiceAccount添加ImagePullSecret
kubectl patch serviceaccount my-service-account -n default \
-p '{"imagePullSecrets": [{"name": "docker-registry-secret"}]}'
# 查看ServiceAccount的ImagePullSecrets
kubectl get serviceaccount my-service-account -n default -o jsonpath='{.imagePullSecrets}'
# 从私有镜像仓库拉取镜像
kubectl create secret docker-registry my-registry-key \
--docker-server=private-registry.example.com \
--docker-username=user \
--docker-password=password \
--docker-email=user@example.com \
-n default真实场景实践示例
场景1:为应用配置最小权限ServiceAccount
需求:一个应用需要读取ConfigMap和Secret,但不能修改它们。
解决方案:
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: config-reader-sa
namespace: default
automountServiceAccountToken: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: config-reader-role
namespace: default
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: config-reader-binding
namespace: default
subjects:
- kind: ServiceAccount
name: config-reader-sa
namespace: default
roleRef:
kind: Role
name: config-reader-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Pod
metadata:
name: config-reader-pod
spec:
serviceAccountName: config-reader-sa
containers:
- name: app
image: busybox:latest
command:
- sh
- -c
- |
# 读取ConfigMap
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
curl -k -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/configmaps
# 保持运行
sleep 3600验证:
# 应用配置
kubectl apply -f config-reader-sa.yaml
# 检查权限
kubectl auth can-i get configmaps \
--as=system:serviceaccount:default:config-reader-sa \
-n default
# 应该返回: yes
kubectl auth can-i update configmaps \
--as=system:serviceaccount:default:config-reader-sa \
-n default
# 应该返回: no场景2:为CI/CD Pipeline配置ServiceAccount
需求:Jenkins需要能够在特定命名空间中部署和管理应用。
解决方案:
---
apiVersion: v1
kind: Namespace
metadata:
name: jenkins
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-deployer
namespace: jenkins
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer-role
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jenkins-deployer-binding
namespace: production
subjects:
- kind: ServiceAccount
name: jenkins-deployer
namespace: jenkins
roleRef:
kind: Role
name: deployer-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Pod
metadata:
name: jenkins
namespace: jenkins
spec:
serviceAccountName: jenkins-deployer
containers:
- name: jenkins
image: jenkins/jenkins:lts
ports:
- containerPort: 8080
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-home
emptyDir: {}获取Token供外部CI/CD使用:
# 创建长期Token
kubectl create token jenkins-deployer -n jenkins --duration=87600h
# 或者创建Secret(推荐用于长期使用)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: jenkins-deployer-token
namespace: jenkins
annotations:
kubernetes.io/service-account.name: jenkins-deployer
type: kubernetes.io/service-account-token
EOF
# 等待Token生成
kubectl get secret jenkins-deployer-token -n jenkins -o jsonpath='{.data.token}' | base64 -d场景3:多命名空间应用访问
需求:一个应用需要访问多个命名空间的资源。
解决方案:
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: multi-ns-app-sa
namespace: app-namespace
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: multi-ns-reader
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: multi-ns-app-binding
subjects:
- kind: ServiceAccount
name: multi-ns-app-sa
namespace: app-namespace
roleRef:
kind: ClusterRole
name: multi-ns-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: multi-ns-app
namespace: app-namespace
spec:
replicas: 1
selector:
matchLabels:
app: multi-ns-app
template:
metadata:
labels:
app: multi-ns-app
spec:
serviceAccountName: multi-ns-app-sa
containers:
- name: app
image: busybox:latest
command:
- sh
- -c
- |
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# 访问多个命名空间
for ns in default production staging; do
echo "Listing pods in $ns namespace:"
curl -k -s -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/$ns/pods | \
grep -o '"name":"[^"]*"' | head -5
done
sleep 3600场景4:使用TokenRequest API创建短期Token
需求:应用需要短期Token来访问API,而不是使用长期Token。
解决方案:
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: short-lived-token-sa
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: default
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: short-lived-token-binding
namespace: default
subjects:
- kind: ServiceAccount
name: short-lived-token-sa
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Pod
metadata:
name: short-lived-token-pod
spec:
serviceAccountName: short-lived-token-sa
containers:
- name: app
image: nginx:latest
volumeMounts:
- name: token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: my-token
expirationSeconds: 600 # 10分钟有效期
audience: api在应用中使用:
# 在Pod内读取Token
TOKEN=$(cat /var/run/secrets/tokens/my-token)
# 使用Token访问API
curl -k -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/default/pods场景5:为每个Pod创建独立的ServiceAccount
需求:在多租户环境中,每个应用使用独立的ServiceAccount,实现权限隔离。
解决方案:
---
# 租户A的应用
apiVersion: v1
kind: ServiceAccount
metadata:
name: tenant-a-app-sa
namespace: tenant-a
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tenant-a-role
namespace: tenant-a
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tenant-a-binding
namespace: tenant-a
subjects:
- kind: ServiceAccount
name: tenant-a-app-sa
namespace: tenant-a
roleRef:
kind: Role
name: tenant-a-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tenant-a-app
namespace: tenant-a
spec:
replicas: 1
selector:
matchLabels:
app: tenant-a-app
template:
metadata:
labels:
app: tenant-a-app
spec:
serviceAccountName: tenant-a-app-sa
containers:
- name: app
image: nginx:latest
---
# 租户B的应用
apiVersion: v1
kind: ServiceAccount
metadata:
name: tenant-b-app-sa
namespace: tenant-b
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tenant-b-role
namespace: tenant-b
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tenant-b-binding
namespace: tenant-b
subjects:
- kind: ServiceAccount
name: tenant-b-app-sa
namespace: tenant-b
roleRef:
kind: Role
name: tenant-b-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tenant-b-app
namespace: tenant-b
spec:
replicas: 1
selector:
matchLabels:
app: tenant-b-app
template:
metadata:
labels:
app: tenant-b-app
spec:
serviceAccountName: tenant-b-app-sa
containers:
- name: app
image: nginx:latest验证隔离:
# 验证租户A无法访问租户B的资源
kubectl auth can-i list pods \
--as=system:serviceaccount:tenant-a:tenant-a-app-sa \
-n tenant-b
# 应该返回: no
# 验证租户A可以访问自己的资源
kubectl auth can-i list pods \
--as=system:serviceaccount:tenant-a:tenant-a-app-sa \
-n tenant-a
# 应该返回: yes场景6:使用ServiceAccount访问私有镜像仓库
需求:从私有镜像仓库拉取镜像。
解决方案:
---
apiVersion: v1
kind: Secret
metadata:
name: docker-registry-secret
namespace: default
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: ewogICJhdXRocyI6IHsKICAgICJwcml2YXRlLXJlZ2lzdHJ5LmV4YW1wbGUuY29tIjogewogICAgICAiYXV0aCI6ICJiMlJsZEdGNVYybHVaWGdnUW1WbVBTSWciCiAgICB9CiAgfQp9
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: private-registry-sa
namespace: default
imagePullSecrets:
- name: docker-registry-secret
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: private-image-app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: private-image-app
template:
metadata:
labels:
app: private-image-app
spec:
serviceAccountName: private-registry-sa
containers:
- name: app
image: private-registry.example.com/myapp:latest
ports:
- containerPort: 80创建镜像拉取密钥:
# 创建Docker Registry Secret
kubectl create secret docker-registry my-registry-key \
--docker-server=private-registry.example.com \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=myuser@example.com \
-n default
# 查看Secret内容
kubectl get secret my-registry-key -n default -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d
# 将Secret添加到ServiceAccount
kubectl patch serviceaccount default -n default \
-p '{"imagePullSecrets": [{"name": "my-registry-key"}]}'故障排查指南
问题1:Pod无法访问API Server
症状:
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:my-sa" cannot list resource "pods" in API group "" in the namespace "default"排查步骤:
# 1. 检查Pod使用的ServiceAccount
kubectl get pod <pod-name> -o jsonpath='{.spec.serviceAccountName}'
# 2. 检查ServiceAccount是否存在
kubectl get serviceaccount <sa-name> -n <namespace>
# 3. 检查ServiceAccount的RoleBinding
kubectl get rolebinding -n <namespace> -o json | \
jq -r '.items[] | select(.subjects[]?.name=="<sa-name>") | .metadata.name'
# 4. 检查权限
kubectl auth can-i list pods \
--as=system:serviceaccount:<namespace>:<sa-name> \
-n <namespace>
# 5. 进入Pod检查Token
kubectl exec -it <pod-name> -- ls -la /var/run/secrets/kubernetes.io/serviceaccount/
# 6. 检查Token内容
kubectl exec -it <pod-name> -- cat /var/run/secrets/kubernetes.io/serviceaccount/token解决方案:
- 确保ServiceAccount存在
- 创建对应的Role和RoleBinding
- 检查RBAC权限配置
问题2:Token未自动挂载
症状:Pod中找不到/var/run/secrets/kubernetes.io/serviceaccount/目录。
排查步骤:
# 1. 检查ServiceAccount配置
kubectl get serviceaccount <sa-name> -n <namespace> -o yaml | grep automountServiceAccountToken
# 2. 检查Pod配置
kubectl get pod <pod-name> -o yaml | grep -A 5 automountServiceAccountToken
# 3. 检查Pod的ServiceAccount
kubectl get pod <pod-name> -o jsonpath='{.spec.serviceAccountName}'解决方案:
# 确保ServiceAccount允许自动挂载
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-sa
automountServiceAccountToken: true # 默认为true
# 或在Pod中显式启用
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-sa
automountServiceAccountToken: true
containers:
- name: app
image: nginx问题3:镜像拉取失败
症状:
Failed to pull image "private-registry.example.com/myapp:latest": rpc error: code = Unknown desc = Error response from daemon: pull access denied for private-registry.example.com/myapp排查步骤:
# 1. 检查ServiceAccount的ImagePullSecrets
kubectl get serviceaccount <sa-name> -n <namespace> -o jsonpath='{.imagePullSecrets}'
# 2. 检查Secret是否存在
kubectl get secret <secret-name> -n <namespace>
# 3. 检查Secret类型
kubectl get secret <secret-name> -n <namespace> -o jsonpath='{.type}'
# 4. 检查Secret内容
kubectl get secret <secret-name> -n <namespace> -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d
# 5. 检查Pod使用的ServiceAccount
kubectl get pod <pod-name> -o jsonpath='{.spec.serviceAccountName}'解决方案:
# 创建正确的Docker Registry Secret
kubectl create secret docker-registry my-registry-key \
--docker-server=private-registry.example.com \
--docker-username=<username> \
--docker-password=<password> \
--docker-email=<email> \
-n <namespace>
# 将Secret添加到ServiceAccount
kubectl patch serviceaccount <sa-name> -n <namespace> \
-p '{"imagePullSecrets": [{"name": "my-registry-key"}]}'问题4:Token过期(Kubernetes 1.24+)
症状:使用旧的Token Secret无法访问API。
原因:Kubernetes 1.24+不再自动创建ServiceAccount Token Secret。
解决方案:
# 方法1:创建短期Token
kubectl create token <sa-name> -n <namespace> --duration=24h
# 方法2:创建长期Token Secret
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: <sa-name>-token
namespace: <namespace>
annotations:
kubernetes.io/service-account.name: <sa-name>
type: kubernetes.io/service-account-token
EOF
# 等待Token生成
kubectl get secret <sa-name>-token -n <namespace> -o jsonpath='{.data.token}' | base64 -d问题5:ServiceAccount权限不足
症状:应用无法执行某些操作。
排查步骤:
# 1. 列出ServiceAccount的所有权限
kubectl auth can-i --list \
--as=system:serviceaccount:<namespace>:<sa-name> \
-n <namespace>
# 2. 检查特定的权限
kubectl auth can-i <verb> <resource> \
--as=system:serviceaccount:<namespace>:<sa-name> \
-n <namespace>
# 3. 检查RoleBinding
kubectl get rolebinding -n <namespace> -o yaml | grep -A 10 <sa-name>
# 4. 检查ClusterRoleBinding
kubectl get clusterrolebinding -o yaml | grep -A 10 <sa-name>解决方案:
# 添加缺失的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: additional-permissions
namespace: <namespace>
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: additional-permissions-binding
namespace: <namespace>
subjects:
- kind: ServiceAccount
name: <sa-name>
namespace: <namespace>
roleRef:
kind: Role
name: additional-permissions
apiGroup: rbac.authorization.k8s.io问题6:无法创建Token
症状:
error: failed to create token: serviceaccounts "my-sa" not found排查步骤:
# 1. 检查ServiceAccount是否存在
kubectl get serviceaccount <sa-name> -n <namespace>
# 2. 检查命名空间是否正确
kubectl get serviceaccount <sa-name> --all-namespaces
# 3. 检查是否有权限创建Token
kubectl auth can-i create serviceaccounts/token \
--as=<your-username>解决方案:
# 确保ServiceAccount存在
kubectl create serviceaccount <sa-name> -n <namespace>
# 确保在正确的命名空间
kubectl create token <sa-name> -n <namespace>最佳实践建议
1. 为每个应用创建独立的ServiceAccount
# 不推荐:使用default ServiceAccount
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: app
image: nginx
# 推荐:使用专用ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: default
---
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-app-sa
containers:
- name: app
image: nginx2. 禁用不必要的Token自动挂载
apiVersion: v1
kind: ServiceAccount
metadata:
name: no-api-access-sa
automountServiceAccountToken: false
---
apiVersion: v1
kind: Pod
metadata:
name: static-web
spec:
serviceAccountName: no-api-access-sa
automountServiceAccountToken: false
containers:
- name: web
image: nginx3. 使用最小权限原则
# 只授予必要的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: minimal-role
namespace: default
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["app-config"] # 限制到特定资源
verbs: ["get"]4. 使用TokenRequest API创建短期Token
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
serviceAccountName: my-sa
containers:
- name: app
image: nginx
volumeMounts:
- name: token
mountPath: /var/run/secrets/tokens
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600 # 1小时有效期
audience: api5. 定期轮换Token和Secret
# 创建新的Token
kubectl create token my-sa -n default --duration=24h
# 删除旧的Token Secret
kubectl delete secret old-token-secret -n default
# 创建新的Token Secret
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: new-token-secret
namespace: default
annotations:
kubernetes.io/service-account.name: my-sa
type: kubernetes.io/service-account-token
EOF6. 使用注释记录ServiceAccount用途
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: default
annotations:
description: "ServiceAccount for my-app frontend"
owner: "frontend-team@example.com"
created-by: "admin"
purpose: "Read configmaps and secrets"7. 集中管理ImagePullSecrets
# 在命名空间级别创建Secret
apiVersion: v1
kind: Secret
metadata:
name: docker-registry-secret
namespace: production
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-config>
---
# 为默认ServiceAccount添加ImagePullSecret
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: production
imagePullSecrets:
- name: docker-registry-secret8. 审计ServiceAccount使用情况
# 列出所有ServiceAccount
kubectl get serviceaccounts --all-namespaces
# 查找使用特定ServiceAccount的Pod
kubectl get pods --all-namespaces -o json | \
jq -r '.items[] | select(.spec.serviceAccountName=="<sa-name>") | "\(.metadata.namespace)/\(.metadata.name)"'
# 检查ServiceAccount的权限
kubectl auth can-i --list \
--as=system:serviceaccount:<namespace>:<sa-name> \
-n <namespace>
# 审计Token使用情况
kubectl get pods --all-namespaces -o json | \
jq -r '.items[] | select(.spec.automountServiceAccountToken!=false) | "\(.metadata.namespace)/\(.metadata.name)"'9. 使用命名约定
# 清晰的命名约定
apiVersion: v1
kind: ServiceAccount
metadata:
name: <app-name>-<component>-sa
namespace: <environment>
labels:
app: <app-name>
component: <component>
environment: <environment>示例:
apiVersion: v1
kind: ServiceAccount
metadata:
name: frontend-api-sa
namespace: production
labels:
app: frontend
component: api
environment: production10. 监控和告警
# 使用Prometheus监控ServiceAccount Token过期
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-rules
namespace: monitoring
data:
serviceaccount.rules: |
groups:
- name: serviceaccount
rules:
- alert: ServiceAccountTokenExpiringSoon
expr: serviceaccount_token_expiry_timestamp_seconds - time() < 3600
for: 5m
labels:
severity: warning
annotations:
summary: "ServiceAccount token expiring soon"
description: "Token for ServiceAccount {{ $labels.serviceaccount }} in namespace {{ $labels.namespace }} will expire in less than 1 hour"ServiceAccount安全检查清单
创建前检查
- [ ] 是否需要访问Kubernetes API?
- [ ] 需要访问哪些资源?
- [ ] 需要执行哪些操作?
- [ ] 是否需要跨命名空间访问?
- [ ] 是否需要从私有镜像仓库拉取镜像?
配置检查
- [ ] ServiceAccount名称是否清晰?
- [ ] 是否添加了描述性注释?
- [ ] 是否禁用了不必要的Token自动挂载?
- [ ] RBAC权限是否遵循最小权限原则?
- [ ] ImagePullSecrets是否正确配置?
运行时检查
- [ ] Token是否正确挂载到Pod?
- [ ] 应用是否能正常访问API?
- [ ] 是否有权限被拒绝的错误?
- [ ] Token是否即将过期?
- [ ] 是否有异常的API访问行为?
定期审计
- [ ] 是否有未使用的ServiceAccount?
- [ ] 权限是否过于宽泛?
- [ ] Token是否需要轮换?
- [ ] 是否有ServiceAccount被误用?
- [ ] ImagePullSecrets是否仍然有效?
总结
ServiceAccount是Kubernetes中实现Pod身份认证的关键机制,通过合理配置ServiceAccount和RBAC,可以实现:
- 身份认证:为Pod提供明确的身份标识
- 权限控制:通过RBAC实现精细化的权限管理
- 安全隔离:不同应用使用不同的ServiceAccount
- 镜像拉取:安全地从私有镜像仓库拉取镜像
关键要点
- 每个Pod都会关联一个ServiceAccount
- ServiceAccount使用JWT Token进行认证
- 遵循最小权限原则配置RBAC
- 禁用不必要的Token自动挂载
- 使用短期Token提高安全性
- 定期审计和轮换Token
下一步学习
- RBAC基础 - 深入了解权限管理
- 网络安全 - 学习网络策略和安全隔离
- Pod安全 - 掌握Pod安全策略
- SecurityContext - 配置容器安全上下文