作为 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-ns
kubectl 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.io
kubectl 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: 3306
kubectl 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-ns

4. 案例验证(关键步骤)

(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

四、避坑指南(运维老炮经验)

  1. RBAC:别用 ClusterRole 绑定普通 SA,避免权限过大;定期审计权限(kubectl get rolebindings -A);
  2. NetworkPolicy:默认拒绝所有的前提是 “没有任何允许规则”,如果有多个 NetworkPolicy,规则是 “或” 关系;
  3. PodSecurityContext:根目录只读时,一定要挂载 emptyDir 到 /tmp 等临时目录,否则应用会崩溃;
  4. K8S 1.33 兼容:seccompProfile 用 RuntimeDefault(别用Localhost,需要额外配置);fsGroupChangePolicy 用 OnRootMismatch(比 Always 性能好)。

总结

这三板斧的核心就是 “最小权限 + 最小通信 + 最小运行权限”,先明确每个组件的控制边界,再按 “定义 - 绑定 - 验证” 的步骤落地,最后用案例验证。作为 10 年运维,你应该能感受到:这些配置不是越复杂越好,而是 “刚好满足安全合规,又不影响业务运行” 才是最优解。

更多推荐