作为 10 年运维老炮,咱不整虚的,全程说人话、讲逻辑、给实操、上案例,保证你看完就能落地。先把核心逻辑讲透,再拆操作步骤,最后给一个完整的电商场景案例,兼容 K8S 1.33。
一、核心逻辑总览
先把这三个组件的定位说清楚,避免你混着用:
| 组件 | 核心作用 | 控制维度 | 类比人话 |
|---|---|---|---|
| RBAC | 控制 “谁能对资源做什么” | 身份 + 权限 | 公司门禁:谁能进哪个部门、能操作哪些设备 |
| NetworkPolicy | 控制 “Pod 之间能不能通信” | 网络流量 | 办公室隔墙 + 门禁:哪个部门能和哪个部门联网 |
| PodSecurityContext | 控制 “Pod 内容器怎么运行” | 容器运行安全属性 | 员工操作规范:不能用管理员权限干活、不能乱碰文件 |
三者组合:先通过 RBAC 确保只有授权的人能操作资源,再通过 NetworkPolicy 确保 Pod 之间只做必要通信,最后通过 PodSecurityContext 确保 Pod 本身运行在安全的权限范围内,形成 “人 - 网络 - 容器” 三层安全防护。
二、逐个拆解:技术逻辑 + 操作步骤
(一)RBAC 精细化权限控制
1. 技术逻辑(说人话)
RBAC = 角色(Role)+ 绑定(Binding)+ 主体(User/Group/ServiceAccount),核心是 “最小权限原则”:
- 主体:谁要操作 K8S(比如运维张三、应用 ServiceAccount、开发组 dev-team);
- 角色:定义 “能对哪些资源(Pod/Deployment/Namespace)做哪些动作(get/list/create/delete)”;
- 绑定:把 “主体” 和 “角色” 绑在一起,相当于 “给张三分配运维岗的权限清单”。
K8S 1.33 里 RBAC 的核心变化:支持更细的资源粒度(比如 Pod 的 ephemeralcontainers 子资源)、支持 RoleBinding 的条件判断(比如只允许在特定时间段操作),但基础逻辑和 1.20 + 一致。
2. 核心操作步骤
步骤 1:明确权限边界(先画清单)
比如:给电商项目的 “订单服务 ServiceAccount” 仅允许在 “order-ns” 命名空间操作 Pod(get/list)、Deployment(update),不允许 delete。
步骤 2:创建 Role(定义权限清单)
Role 是 “命名空间级” 的,集群级用 ClusterRole(比如允许跨命名空间操作)。
# order-role.yaml(命名空间级Role) apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: order-service-role namespace: order-ns # 仅在该命名空间生效 rules: - apiGroups: [""] # 核心API组(Pod/Service等) resources: ["pods"] # 允许操作的资源 verbs: ["get", "list"] # 允许的动作(只查不改) - apiGroups: ["apps"] # Deployment属于apps组 resources: ["deployments"] resourceNames: ["order-deployment"] # 精细化:仅允许操作这个Deployment verbs: ["get", "update"] # 允许查看和更新,不允许删除/创建执行创建:
kubectl apply -f order-role.yaml -n order-ns步骤 3:创建 ServiceAccount(Pod 用的 “身份”)
如果是给 Pod 内应用授权,先建 ServiceAccount:
# order-sa.yaml apiVersion: v1 kind: ServiceAccount metadata: name: order-service-sa namespace: order-ns执行创建:
kubectl apply -f order-sa.yaml步骤 4:绑定 Role 和 ServiceAccount(RoleBinding)
# order-rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: order-service-rb namespace: order-ns subjects: # 绑定的主体 - kind: ServiceAccount name: order-service-sa namespace: order-ns roleRef: # 绑定的角色 kind: Role name: order-service-role apiGroup: rbac.authorization.k8s.io执行创建:
kubectl apply -f order-rolebinding.yaml -n order-ns步骤 5:验证权限(关键)
用该 ServiceAccount 登录测试:
# 获取SA的token TOKEN=$(kubectl get secret $(kubectl get sa order-service-sa -n order-ns -o jsonpath='{.secrets[0].name}') -n order-ns -o jsonpath='{.data.token}' | base64 -d) # 测试能否更新order-deployment(应该能) kubectl --token=$TOKEN -n order-ns patch deployment order-deployment --type='json' -p='[{"op":"replace","path":"/spec/replicas","value":2}]' # 测试能否删除Pod(应该拒绝) kubectl --token=$TOKEN -n order-ns delete pod order-pod-xxxx(二)NetworkPolicy 网络隔离
1. 技术逻辑(说人话)
NetworkPolicy 是 “Pod 的防火墙”,基于标签、命名空间、端口控制流量:
- 只允许 “必要的通信”,默认拒绝所有(关键:得先装网络插件支持,比如 Calico/Flannel(1.33 版 Flannel 已支持));
- 分为 Ingress(入站流量)和 Egress(出站流量),可以单独控制;
- 核心:“谁能访问我”(Ingress)、“我能访问谁”(Egress)。
K8S 1.33 里 NetworkPolicy 的变化:支持 IPv6 双栈、支持更细的端口范围(比如 1000-2000)、支持匹配 Pod 的拓扑域(topologyKey)。
2. 核心操作步骤
步骤 1:确认网络插件支持
先检查:
kubectl get pods -n kube-system -l k8s-app=calico-node # 有则支持,Flannel同理如果没装,参考 K8S 1.33 官方文档装 Calico(5 分钟搞定)。
步骤 2:明确网络隔离需求
比如:订单服务(order-ns,标签 app=order)仅允许:
- 入站:仅支付服务(pay-ns,标签 app=pay)的 8080 端口访问;
- 出站:仅允许访问数据库服务(db-ns,标签 app=mysql)的 3306 端口;
- 拒绝所有其他入站 / 出站流量。
步骤 3:创建 NetworkPolicy
# order-networkpolicy.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: order-deny-all namespace: order-ns spec: podSelector: # 作用于哪些Pod:标签app=order的Pod matchLabels: app: order policyTypes: # 控制入站+出站 - Ingress - Egress ingress: # 入站规则:仅允许pay服务的8080端口 - from: - namespaceSelector: # 匹配pay-ns命名空间 matchLabels: kubernetes.io/metadata.name: pay-ns podSelector: # 匹配pay-ns里标签app=pay的Pod matchLabels: app: pay ports: # 仅允许8080端口 - protocol: TCP port: 8080 egress: # 出站规则:仅允许访问mysql的3306端口 - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: db-ns podSelector: matchLabels: app: mysql ports: - protocol: TCP port: 3306 # 注意:没有匹配的规则=拒绝所有(NetworkPolicy的默认逻辑)执行创建:
kubectl apply -f order-networkpolicy.yaml -n order-ns步骤 4:验证网络隔离(关键)
- 测试 1:pay 服务 Pod 访问 order 服务 8080(应该通):
kubectl exec -it pay-pod-xxxx -n pay-ns -- curl order-ns/order-service:8080 - 测试 2:商品服务 Pod 访问 order 服务 8080(应该不通):
kubectl exec -it goods-pod-xxxx -n goods-ns -- curl order-ns/order-service:8080 - 测试 3:order 服务 Pod 访问百度(应该不通,因为 Egress 只允许 mysql):
kubectl exec -it order-pod-xxxx -n order-ns -- curl www.baidu.com
(三)PodSecurityContext 安全配置
1. 技术逻辑(说人话)
PodSecurityContext 是 “容器的运行规则”,控制 Pod / 容器的安全属性,核心是 “降权运行”:
- 禁止容器以 root 用户运行(避免容器逃逸后拿到主机 root 权限);
- 控制文件权限(比如容器内文件只能读,不能写);
- 禁止容器挂载主机目录、禁止特权模式(比如不能访问主机 /dev 目录)。
K8S 1.33 里的变化:支持更细的 seccomp 配置(限制系统调用)、支持 Pod 的 apparmor 配置(Linux 安全模块)、默认禁止特权容器。
2. 核心操作步骤
步骤 1:明确安全配置需求
比如:订单服务 Pod 要求:
- 整个 Pod 的运行用户 ID=1000(非 root);
- 容器内文件的默认权限 = 0644(只读);
- 禁止容器获取主机 PID/IPC 命名空间;
- 禁止特权模式,禁止修改内核参数。
步骤 2:在 Deployment 中配置 PodSecurityContext
# order-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: order-deployment namespace: order-ns spec: replicas: 2 selector: matchLabels: app: order template: metadata: labels: app: order spec: serviceAccountName: order-service-sa # 关联之前的SA securityContext: # Pod级别的安全配置(作用于所有容器) runAsUser: 1000 # 运行用户ID(非root) runAsGroup: 1000 # 运行组ID fsGroup: 1000 # 容器内文件的属组ID fsGroupChangePolicy: "OnRootMismatch" # 仅当根目录权限不匹配时修改 seccompProfile: # 限制系统调用 type: RuntimeDefault allowPrivilegeEscalation: false # 禁止权限提升 privileged: false # 禁止特权模式 readOnlyRootFilesystem: true # 根文件系统只读(关键!) procMount: Default # 禁止修改/proc挂载 containers: - name: order-container image: order-service:v1 ports: - containerPort: 8080 securityContext: # 容器级别的安全配置(覆盖Pod级) runAsNonRoot: true # 强制非root运行 capabilities: # 移除所有特权能力 drop: ["ALL"] volumeMounts: # 因为根目录只读,需要挂载临时可写目录 - name: tmp-volume mountPath: /tmp volumes: # 定义临时可写目录 - name: tmp-volume emptyDir: {}执行创建:
kubectl apply -f order-deployment.yaml -n order-ns步骤 3:验证安全配置(关键)
- 检查容器运行用户(应该是 1000,不是 root):
kubectl exec -it order-pod-xxxx -n order-ns -- id - 检查根目录是否只读(创建文件应该失败):
kubectl exec -it order-pod-xxxx -n order-ns -- touch /test.txt - 检查是否禁止特权模式:
kubectl exec -it order-pod-xxxx -n order-ns -- mount /dev/sda1 /mnt # 应该拒绝
三、完整案例:电商订单系统安全合规配置
1. 案例背景
电商平台的订单系统包含 3 个命名空间:
- order-ns:订单服务(核心,需最高安全级别);
- pay-ns:支付服务(仅能访问订单服务);
- db-ns:MySQL 数据库(仅订单服务能访问)。
2. 整体安全需求
| 维度 | 需求 |
|---|---|
| RBAC | 1. 订单服务 SA 仅能操作 order-ns 的 Pod/Deployment;2. 支付服务 SA 无权限操作订单服务资源;3. 运维仅能在工作时间操作 order-ns |
| NetworkPolicy | 1. 订单服务仅允许支付服务的 8080 端口入站;2. 订单服务仅允许访问数据库 3306 端口出站;3. 数据库仅允许订单服务访问 |
| PodSecurityContext | 1. 所有服务 Pod 均以非 root(UID=1000)运行;2. 根目录只读;3. 禁止特权模式;4. 移除所有 Linux 特权能力 |
3. 完整配置文件(可直接运行)
(1)创建命名空间
# namespaces.yaml apiVersion: v1 kind: Namespace metadata: name: order-ns labels: kubernetes.io/metadata.name: order-ns --- apiVersion: v1 kind: Namespace metadata: name: pay-ns labels: kubernetes.io/metadata.name: pay-ns --- apiVersion: v1 kind: Namespace metadata: name: db-ns labels: kubernetes.io/metadata.name: db-nskubectl apply -f namespaces.yaml(2)RBAC 配置(订单服务)
# order-rbac.yaml # 1. Role apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: order-service-role namespace: order-ns rules: - apiGroups: [""] resources: ["pods", "services"] verbs: ["get", "list"] - apiGroups: ["apps"] resources: ["deployments"] resourceNames: ["order-deployment"] verbs: ["get", "update"] --- # 2. ServiceAccount apiVersion: v1 kind: ServiceAccount metadata: name: order-service-sa namespace: order-ns --- # 3. RoleBinding apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: order-service-rb namespace: order-ns subjects: - kind: ServiceAccount name: order-service-sa namespace: order-ns roleRef: kind: Role name: order-service-role apiGroup: rbac.authorization.k8s.iokubectl apply -f order-rbac.yaml(3)NetworkPolicy 配置
① 订单服务网络策略
# order-np.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: order-np namespace: order-ns spec: podSelector: matchLabels: app: order policyTypes: ["Ingress", "Egress"] ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: pay-ns podSelector: matchLabels: app: pay ports: - protocol: TCP port: 8080 egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: db-ns podSelector: matchLabels: app: mysql ports: - protocol: TCP port: 3306② 数据库网络策略(仅允许订单服务访问)
# db-np.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: db-np namespace: db-ns spec: podSelector: matchLabels: app: mysql policyTypes: ["Ingress"] ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: order-ns podSelector: matchLabels: app: order ports: - protocol: TCP port: 3306kubectl apply -f order-np.yaml -n order-ns kubectl apply -f db-np.yaml -n db-ns(4)PodSecurityContext + Deployment 配置
① 订单服务 Deployment
# order-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: order-deployment namespace: order-ns spec: replicas: 2 selector: matchLabels: app: order template: metadata: labels: app: order spec: serviceAccountName: order-service-sa securityContext: runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 fsGroupChangePolicy: "OnRootMismatch" seccompProfile: type: RuntimeDefault allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true procMount: Default containers: - name: order-container image: nginx:alpine # 用nginx模拟订单服务 ports: - containerPort: 8080 securityContext: runAsNonRoot: true capabilities: drop: ["ALL"] volumeMounts: - name: tmp-volume mountPath: /tmp - name: nginx-conf mountPath: /etc/nginx/conf.d volumes: - name: tmp-volume emptyDir: {} - name: nginx-conf configMap: name: order-nginx-conf --- # 订单服务Nginx配置(模拟8080端口) apiVersion: v1 kind: ConfigMap metadata: name: order-nginx-conf namespace: order-ns data: default.conf: | server { listen 8080; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } }② 支付服务 Deployment(简化版,仅验证网络)
# pay-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: pay-deployment namespace: pay-ns spec: replicas: 1 selector: matchLabels: app: pay template: metadata: labels: app: pay spec: securityContext: runAsUser: 1000 runAsGroup: 1000 allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true containers: - name: pay-container image: curlimages/curl:latest command: ["sleep", "3600"] # 保持容器运行 securityContext: runAsNonRoot: true capabilities: drop: ["ALL"] volumeMounts: - name: tmp-volume mountPath: /tmp volumes: - name: tmp-volume emptyDir: {}③ 数据库 Deployment(简化版)
# db-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: mysql-deployment namespace: db-ns spec: replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: securityContext: runAsUser: 999 # mysql默认UID runAsGroup: 999 allowPrivilegeEscalation: false privileged: false containers: - name: mysql-container image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD value: "123456" ports: - containerPort: 3306 securityContext: runAsNonRoot: true capabilities: drop: ["ALL"]kubectl apply -f order-deploy.yaml -n order-ns kubectl apply -f pay-deploy.yaml -n pay-ns kubectl apply -f db-deploy.yaml -n db-ns4. 案例验证(关键步骤)
(1)RBAC 验证
# 获取订单服务SA的token TOKEN=$(kubectl get secret $(kubectl get sa order-service-sa -n order-ns -o jsonpath='{.secrets[0].name}') -n order-ns -o jsonpath='{.data.token}' | base64 -d) # 测试更新订单Deployment(成功) kubectl --token=$TOKEN -n order-ns patch deployment order-deployment --type='json' -p='[{"op":"replace","path":"/spec/replicas","value":3}]' # 测试删除订单Pod(失败) kubectl --token=$TOKEN -n order-ns delete pod $(kubectl get pods -n order-ns -l app=order -o jsonpath='{.items[0].metadata.name}')(2)NetworkPolicy 验证
# 支付服务访问订单服务8080(成功) kubectl exec -it $(kubectl get pods -n pay-ns -l app=pay -o jsonpath='{.items[0].metadata.name}') -n pay-ns -- curl http://$(kubectl get svc -n order-ns -l app=order -o jsonpath='{.items[0].spec.clusterIP}'):8080 # 随便起一个Pod访问订单服务(失败) kubectl run test-pod -n default --image=curlimages/curl --rm -it -- curl http://$(kubectl get svc -n order-ns -l app=order -o jsonpath='{.items[0].spec.clusterIP}'):8080 # 订单服务访问数据库(成功) kubectl exec -it $(kubectl get pods -n order-ns -l app=order -o jsonpath='{.items[0].metadata.name}') -n order-ns -- curl $(kubectl get svc -n db-ns -l app=mysql -o jsonpath='{.items[0].spec.clusterIP}'):3306(3)PodSecurityContext 验证
# 检查订单服务Pod运行用户(1000,非root) kubectl exec -it $(kubectl get pods -n order-ns -l app=order -o jsonpath='{.items[0].metadata.name}') -n order-ns -- id # 检查根目录是否只读(创建文件失败) kubectl exec -it $(kubectl get pods -n order-ns -l app=order -o jsonpath='{.items[0].metadata.name}') -n order-ns -- touch /test.txt # 检查是否禁止特权模式(挂载主机目录失败) kubectl exec -it $(kubectl get pods -n order-ns -l app=order -o jsonpath='{.items[0].metadata.name}') -n order-ns -- mount /dev/sda1 /mnt四、避坑指南(运维老炮经验)
- RBAC:别用 ClusterRole 绑定普通 SA,避免权限过大;定期审计权限(kubectl get rolebindings -A);
- NetworkPolicy:默认拒绝所有的前提是 “没有任何允许规则”,如果有多个 NetworkPolicy,规则是 “或” 关系;
- PodSecurityContext:根目录只读时,一定要挂载 emptyDir 到 /tmp 等临时目录,否则应用会崩溃;
- K8S 1.33 兼容:seccompProfile 用 RuntimeDefault(别用Localhost,需要额外配置);fsGroupChangePolicy 用 OnRootMismatch(比 Always 性能好)。
总结
这三板斧的核心就是 “最小权限 + 最小通信 + 最小运行权限”,先明确每个组件的控制边界,再按 “定义 - 绑定 - 验证” 的步骤落地,最后用案例验证。作为 10 年运维,你应该能感受到:这些配置不是越复杂越好,而是 “刚好满足安全合规,又不影响业务运行” 才是最优解。