Kubernetes-08 RBAC 与安全
目录
Kubernetes RBAC 与安全
系列第八篇。安全是 K8s 生产运维的重要课题。本篇讲解 RBAC 权限模型、安全上下文、Pod 安全策略和零信任网络。
目录
- K8s 安全概览
- 认证(Authentication)
- RBAC 授权
- ServiceAccount
- 安全上下文(Security Context)
- Pod Security Standards
- 镜像安全
- Go Operator 的 RBAC 实践
- 安全检查清单
- 小结
1. K8s 安全概览
K8s 的安全模型分为四层(4C 安全模型):
Cloud(云/数据中心)
└── Cluster(K8s 集群)
└── Container(容器)
└── Code(代码)
每层都需要独立的安全措施,本篇重点讲 Cluster 和 Container 层。
K8s 安全三大核心:
- 认证(Authentication):你是谁?
- 授权(Authorization):你能做什么?
- 准入控制(Admission Control):你的请求合规吗?
2. 认证(Authentication)
K8s 支持多种认证方式:
| 认证方式 | 适用场景 |
|---|---|
| 客户端证书(X.509) | 组件间通信、管理员 kubectl |
| Bearer Token | ServiceAccount、OIDC |
| OIDC(OpenID Connect) | 集成 GitHub/Google/Dex 登录 |
| Webhook Token | 自定义认证服务 |
2.1 kubeconfig 文件
kubectl 通过 ~/.kube/config 管理集群连接信息:
apiVersion: v1
kind: Config
clusters:
- name: production
cluster:
server: https://api.k8s.company.com:6443
certificate-authority-data: <base64-ca-cert>
- name: staging
cluster:
server: https://api.staging.k8s.company.com:6443
certificate-authority-data: <base64-ca-cert>
users:
- name: admin-user
user:
client-certificate-data: <base64-cert>
client-key-data: <base64-key>
- name: dev-user
user:
token: <bearer-token>
contexts:
- name: prod-admin
context:
cluster: production
user: admin-user
namespace: production
- name: staging-dev
context:
cluster: staging
user: dev-user
namespace: development
current-context: staging-dev
# 查看所有 context
kubectl config get-contexts
# 切换 context
kubectl config use-context prod-admin
# 查看当前 context
kubectl config current-context
# 合并多个 kubeconfig
export KUBECONFIG=~/.kube/config:~/.kube/config-staging
kubectl config view --merge --flatten > ~/.kube/config-merged
3. RBAC 授权
RBAC(Role-Based Access Control,基于角色的访问控制) 是 K8s 默认的授权机制。
3.1 RBAC 核心概念
资源(Resource) = 什么:Pod、Deployment、Secret、ConfigMap...
操作(Verb) = 怎么做:get/list/watch/create/update/patch/delete
角色(Role) = 权限集合(允许对哪些资源执行哪些操作)
主体(Subject) = 谁:User、Group、ServiceAccount
角色绑定(RoleBinding)= 将角色授予主体
3.2 Role vs ClusterRole
| Role | ClusterRole | |
|---|---|---|
| 作用范围 | 单个 Namespace | 整个集群 |
| 绑定对象 | RoleBinding | RoleBinding 或 ClusterRoleBinding |
| 能访问的资源 | Namespace 级别资源 | 集群级别 + Namespace 级别资源 |
3.3 常用 Verbs
get → 查看单个资源
list → 列出资源
watch → 监听资源变化
create → 创建
update → 全量更新(PUT)
patch → 部分更新(PATCH)
delete → 删除
deletecollection → 批量删除
3.4 Role 示例
只读 Role(适合监控/审计):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""] # "" 表示 core API 组
resources: ["pods", "pods/log", "pods/exec"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get", "list", "watch"]
开发者 Role(可以部署和调试):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: development
rules:
# 管理工作负载
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# 查看 Pod、日志、进入容器
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch", "delete"]
- apiGroups: [""]
resources: ["pods/exec", "pods/portforward"]
verbs: ["create"]
# 管理 Service
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 管理 ConfigMap
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 只能查看 Secret(不能查看内容)
- apiGroups: [""]
resources: ["secrets"]
verbs: ["list"]
# 查看事件
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list", "watch"]
ClusterRole(集群管理员):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-viewer
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
resources: ["nodes", "pods"]
verbs: ["get", "list"]
3.5 RoleBinding 示例
# 将 pod-reader Role 授予用户 alice
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: alice-pod-reader
namespace: production
subjects:
# 可以绑定多种主体
- kind: User
name: alice # 用户名(来自认证信息)
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: dev-team # 用户组
apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
name: monitoring-sa # ServiceAccount
namespace: monitoring # ServiceAccount 所在 Namespace
roleRef:
kind: Role # Role 或 ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
ClusterRoleBinding:
# 将 ClusterRole 授予集群范围
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admin-binding
subjects:
- kind: User
name: admin@company.com
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin # K8s 内置的超级管理员角色
apiGroup: rbac.authorization.k8s.io
3.6 内置 ClusterRole
| ClusterRole | 描述 |
|---|---|
cluster-admin |
超级管理员,所有权限 |
admin |
命名空间管理员,不能修改 Namespace 本身 |
edit |
可以修改大多数资源,不能修改 Role/RoleBinding |
view |
只读(不包括 Secret) |
# 快速给用户只读权限
kubectl create clusterrolebinding view-binding \
--clusterrole=view \
--user=alice
# 给 Namespace 管理员权限
kubectl create rolebinding admin-binding \
--clusterrole=admin \
--user=alice \
--namespace=development
3.7 权限检查
# 检查当前用户是否有权限
kubectl auth can-i create pods
kubectl auth can-i get secrets -n production
kubectl auth can-i list deployments --all-namespaces
# 检查其他用户的权限
kubectl auth can-i create pods --as=alice
kubectl auth can-i create pods --as=system:serviceaccount:default:my-sa
# 查看用户的所有权限
kubectl auth can-i --list
kubectl auth can-i --list --as=alice
# 查看所有 ClusterRole 和 RoleBinding
kubectl get clusterrole,clusterrolebinding
kubectl get role,rolebinding -n production
4. ServiceAccount
ServiceAccount 是 Pod 的"身份证",让 Pod 能够调用 K8s API。
4.1 默认 ServiceAccount
每个 Namespace 都有一个 default ServiceAccount。如果不指定,Pod 使用 default ServiceAccount。
kubectl get serviceaccount
kubectl describe serviceaccount default
Token 自动挂载:
kubectl exec -it <pod> -- ls /var/run/secrets/kubernetes.io/serviceaccount/
# ca.crt namespace token
4.2 创建自定义 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: go-operator-sa
namespace: production
annotations:
# AWS IRSA(IAM Roles for Service Accounts)
eks.amazonaws.com/role-arn: "arn:aws:iam::123456789:role/my-app-role"
---
# 给 ServiceAccount 赋予权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: go-operator-role
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: go-operator-binding
namespace: production
subjects:
- kind: ServiceAccount
name: go-operator-sa
namespace: production
roleRef:
kind: Role
name: go-operator-role
apiGroup: rbac.authorization.k8s.io
4.3 Pod 使用 ServiceAccount
spec:
serviceAccountName: go-operator-sa # 指定 ServiceAccount
automountServiceAccountToken: false # 不自动挂载 Token(不需要 API 访问时禁用)
containers:
- name: go-app
...
4.4 Go 应用使用 ServiceAccount 调用 K8s API
package main
import (
"context"
"fmt"
"log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
func main() {
// InCluster 配置:自动读取 ServiceAccount Token
config, err := rest.InClusterConfig()
if err != nil {
log.Fatalf("Failed to get InCluster config: %v", err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatalf("Failed to create clientset: %v", err)
}
// 列出当前 Namespace 的 Pod
namespace := getCurrentNamespace()
pods, err := clientset.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
log.Fatalf("Failed to list pods: %v", err)
}
fmt.Printf("Found %d pods in namespace %s:\n", len(pods.Items), namespace)
for _, pod := range pods.Items {
fmt.Printf(" - %s (%s)\n", pod.Name, pod.Status.Phase)
}
}
func getCurrentNamespace() string {
// 从 ServiceAccount 挂载的文件中读取当前 Namespace
data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
if err != nil {
return "default"
}
return string(data)
}
5. 安全上下文(Security Context)
Security Context 定义容器的权限和访问控制设置,实现最小权限原则。
5.1 Pod 级别安全上下文
spec:
securityContext:
runAsUser: 1000 # 以用户 ID 1000 运行(非 root)
runAsGroup: 3000 # 主组 ID
fsGroup: 2000 # 挂载 Volume 的组 ID(写入文件时的 group)
runAsNonRoot: true # 强制非 root(如果镜像是 root 则启动失败)
seccompProfile: # Seccomp 安全计算模式
type: RuntimeDefault # 使用运行时默认配置(限制危险系统调用)
sysctls:
- name: net.core.somaxconn
value: "1024"
5.2 容器级别安全上下文
spec:
containers:
- name: go-app
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
readOnlyRootFilesystem: true # 根文件系统只读(防止容器写文件)
allowPrivilegeEscalation: false # 禁止提权(sudo、SUID 等)
capabilities: # Linux Capabilities
drop:
- ALL # 删除所有 Capabilities
add:
- NET_BIND_SERVICE # 只添加需要的(绑定 < 1024 端口)
seccompProfile:
type: RuntimeDefault
5.3 Linux Capabilities 常用列表
| Capability | 描述 | 是否需要 |
|---|---|---|
NET_BIND_SERVICE |
绑定低于 1024 的端口 | Web 服务监听 80/443 |
SYS_PTRACE |
调试其他进程 | 通常不需要 |
SYS_ADMIN |
各种系统管理操作 | 几乎不需要 |
NET_ADMIN |
网络管理 | CNI 插件需要 |
CHOWN |
修改文件所有者 | 通常不需要 |
SETUID |
修改进程 UID | 通常不需要 |
最佳实践:drop ALL,只 add 需要的。
5.4 只读根文件系统 + 临时写目录
spec:
containers:
- name: go-app
securityContext:
readOnlyRootFilesystem: true # 根文件系统只读
volumeMounts:
- name: tmp
mountPath: /tmp # 允许写 /tmp
- name: logs
mountPath: /var/log/app # 允许写日志目录
volumes:
- name: tmp
emptyDir: {}
- name: logs
emptyDir: {}
6. Pod Security Standards
K8s 1.25+ 弃用 PodSecurityPolicy,改用 Pod Security Standards(PSS)。
6.1 三种安全级别
| 级别 | 描述 |
|---|---|
privileged |
无限制(等同于 root 在宿主机上) |
baseline |
防止已知权限提升,允许默认配置 |
restricted |
最严格,遵循 Pod 安全最佳实践 |
6.2 为 Namespace 设置安全策略
通过 Namespace 的 Label 控制:
# 对 production Namespace 强制执行 restricted 策略
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=v1.29 \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted # 不满足则拒绝
pod-security.kubernetes.io/warn: restricted # 不满足则警告
pod-security.kubernetes.io/audit: restricted # 不满足则审计日志
restricted 级别要求(部分):
runAsNonRoot: trueallowPrivilegeEscalation: falsereadOnlyRootFilesystem: trueseccompProfile.type: RuntimeDefault或Localhostcapabilities.drop: [ALL]- 不允许 hostNetwork、hostPID、hostIPC
7. 镜像安全
7.1 使用私有镜像仓库
# 创建镜像仓库认证 Secret
kubectl create secret docker-registry registry-secret \
--docker-server=myregistry.io \
--docker-username=myuser \
--docker-password=mypassword
spec:
imagePullSecrets:
- name: registry-secret
containers:
- name: go-app
image: myregistry.io/go-app:v1.0.0
7.2 镜像 Tag 规范
# 不要用 latest!生产环境始终使用具体版本
image: go-app:v1.2.0 # 好:版本号
image: go-app:20260316 # 好:日期
image: go-app:abc1234 # 好:Git commit hash
image: go-app:latest # 差:无法追踪版本,缓存导致行为不一致
7.3 镜像扫描
# 使用 Trivy 扫描镜像漏洞
trivy image myregistry.io/go-app:v1.0.0
# 在 CI/CD 中集成(GitHub Actions 示例)
# - name: Run Trivy vulnerability scanner
# uses: aquasecurity/trivy-action@master
# with:
# image-ref: 'myregistry.io/go-app:${{ github.sha }}'
# format: 'sarif'
# exit-code: '1'
# severity: 'CRITICAL,HIGH'
7.4 Go 应用的最小化 Dockerfile
# 多阶段构建
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# 最终镜像:使用 distroless(无 shell,攻击面极小)
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
EXPOSE 8080
USER nonroot:nonroot # 非 root 用户
ENTRYPOINT ["/server"]
或使用 scratch(空镜像,极度精简):
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server
EXPOSE 8080
USER 65534:65534 # nobody
ENTRYPOINT ["/server"]
8. Go Operator 的 RBAC 实践
使用 controller-runtime 或 kubebuilder 开发 Operator 时的 RBAC 配置:
// 在 Controller 代码上添加标记注释,kubebuilder 自动生成 RBAC YAML
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
//+kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch
//+kubebuilder:rbac:groups=mygroup.io,resources=myresources,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=mygroup.io,resources=myresources/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=mygroup.io,resources=myresources/finalizers,verbs=update
func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// Reconcile 逻辑
return ctrl.Result{}, nil
}
生成 RBAC:
make manifests # 生成 config/rbac/ 目录下的 YAML
9. 安全检查清单
9.1 Namespace 级别
☐ 为每个团队/应用创建独立 Namespace
☐ 设置 ResourceQuota 限制资源使用
☐ 设置 LimitRange 设置默认资源限制
☐ 应用 Pod Security Standards(至少 baseline)
☐ 配置 NetworkPolicy(默认拒绝所有)
9.2 Pod 级别
☐ 设置 requests 和 limits
☐ runAsNonRoot: true
☐ readOnlyRootFilesystem: true
☐ allowPrivilegeEscalation: false
☐ capabilities: drop [ALL]
☐ seccompProfile: RuntimeDefault
☐ 不使用 hostNetwork/hostPID/hostIPC(除非必须)
☐ imagePullPolicy: Always(或使用 digest)
☐ 设置 livenessProbe 和 readinessProbe
9.3 RBAC 级别
☐ 最小权限原则:只给必要的权限
☐ 不要直接使用 cluster-admin
☐ 为每个应用创建独立 ServiceAccount
☐ 不使用 default ServiceAccount
☐ automountServiceAccountToken: false(不需要 API 访问时)
☐ 定期审计 RBAC 配置
9.4 镜像安全
☐ 使用私有镜像仓库
☐ 固定镜像版本(不使用 latest)
☐ 定期扫描镜像漏洞(Trivy/Snyk)
☐ 使用多阶段构建减小镜像大小
☐ 使用 distroless 或 scratch 基础镜像
☐ 配置 ImagePullSecrets
10. 小结
RBAC 快速参考
# 创建 Role
kubectl create role pod-reader \
--verb=get,list,watch \
--resource=pods \
-n production
# 创建 RoleBinding
kubectl create rolebinding alice-pod-reader \
--role=pod-reader \
--user=alice \
-n production
# 检查权限
kubectl auth can-i list pods -n production --as=alice
# 查看谁有什么权限
kubectl get rolebinding,clusterrolebinding -A \
-o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .subjects[*]}{.kind}{"\t"}{.name}{"\n"}{end}{end}'
关键安全原则
- 最小权限(Least Privilege):只给必要的权限
- 纵深防御(Defense in Depth):多层安全防护
- 默认拒绝(Default Deny):NetworkPolicy 默认拒绝所有流量
- 不可变基础设施:容器只读文件系统,配置从外部注入
- 零信任:不因为在集群内就默认信任
xingliuhua