3. Kubernetes集群安全:RBAC权限控制、网络策略、Pod安全策略

聊到Kubernetes集群安全,我脑子里第一个蹦出来的词就是「层层设防」。你想想看,一个K8s集群里跑着几十上百个服务,有内部的、有外部的,有敏感的、有公开的。如果不做权限隔离,那跟把家门钥匙挂在门口有什么区别?

这一章,咱们就聚焦三个最核心的安全机制:RBAC权限控制网络策略Pod安全策略。这三板斧用好了,集群的安全水位能提升一大截。

3.1 RBAC权限控制:谁能在集群里干什么

RBAC,全称是Role-Based Access Control,基于角色的访问控制。说白了,就是给不同的人或服务分配不同的「工作证」,有的只能看,有的能改,有的能删。

我在项目中遇到过好几次这样的情况:开发同学不小心用kubectl delete删掉了生产环境的命名空间。嗯,那场面,真是「一地鸡毛」。从那以后,我强制要求所有非运维人员只能拥有只读权限,并且严格限制操作范围。

3.1.1 核心概念

  • Role:定义一组权限,作用范围是某个命名空间。比如,允许读取Pod、创建Deployment。
  • ClusterRole:和Role类似,但作用范围是整个集群。比如,查看所有命名空间的Pod、管理节点。
  • RoleBinding:把Role绑定到用户或ServiceAccount上,限定在某个命名空间内生效。
  • ClusterRoleBinding:把ClusterRole绑定到用户或ServiceAccount上,在整个集群生效。

重要原则:最小权限原则

只给用户或服务恰好够用的权限,多一个都不给。我见过有人图省事,直接给开发人员绑了个cluster-admin角色。这相当于把整个集群的root密码交出去了,风险极高。

3.1.2 实战:创建一个只读用户

假设我们想让一个开发人员只能查看default命名空间下的Pod和Service。可以这样配置:

# 1. 创建Role,只允许读取Pod和Service
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["get", "list", "watch"]
---
# 2. 创建RoleBinding,绑定到用户dev-user
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: dev-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

这样配置完,dev-user就只能看,不能改。我曾经用这个模式帮一个团队解决了「误删命名空间」的顽疾,效果立竿见影。

小技巧:对于ServiceAccount,建议每个微服务都使用独立的ServiceAccount,并绑定最小权限的Role。不要所有服务共用一个默认的ServiceAccount。

3.2 网络策略:控制Pod之间的通信

默认情况下,K8s集群里的所有Pod是可以互相通信的。这听起来很方便,但安全角度来说,这简直是「裸奔」。你想想,如果某个Pod被攻破了,攻击者可以横向移动到任何其他Pod,那后果不堪设想。

网络策略(NetworkPolicy)就是用来解决这个问题的。它像是一道道防火墙规则,控制哪些Pod可以访问哪些Pod。

3.2.1 网络策略的工作原理

网络策略是Kubernetes原生的资源对象,但需要CNI插件支持(比如Calico、Cilium、Weave Net)。如果集群没有安装支持网络策略的CNI,那配置了也没用。

我个人习惯是:默认拒绝所有入站和出站流量,然后按需放行。这样最安全,也最清晰。

3.2.2 实战:隔离前端和后端

假设我们有一个前端Pod和一个后端Pod。前端需要访问后端的API,但后端不应该主动访问前端。可以这样配置:

# 默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress
---
# 允许前端访问后端
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
spec:
  podSelector:
    matchLabels:
      app: backend
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

这样配置后,只有带有app: frontend标签的Pod才能访问后端Pod的8080端口。其他Pod一律被拒绝。

注意:网络策略是「白名单」机制。如果你没有显式放行某个流量,默认就是拒绝。我曾经见过有人配置了放行规则,但忘了加默认拒绝策略,结果所有流量还是通的。所以,一定要先加默认拒绝策略。

3.3 Pod安全策略:限制Pod的行为

Pod安全策略(PodSecurityPolicy,简称PSP)是用来控制Pod在宿主机上能做什么的。比如,能不能以root用户运行?能不能挂载宿主机目录?能不能使用特权模式?

说实话,PSP在K8s 1.21版本之后已经被废弃了,取而代之的是Pod Security Admission(PSA)。但很多老项目还在用PSP,所以咱们两个都得了解。

3.3.1 Pod Security Admission(PSA)

PSA是K8s 1.23版本引入的,它通过标签来控制命名空间的安全级别。有三个级别:

级别 说明 典型限制
Privileged 无限制,最宽松
Baseline 最低限度的安全限制 禁止特权容器、禁止hostNetwork、禁止hostPID等
Restricted 最严格的安全限制 禁止所有特权操作,强制使用只读根文件系统等

我建议:生产环境至少使用Baseline级别,敏感业务使用Restricted级别

3.3.2 实战:配置PSA

给命名空间打上标签,就能启用PSA:

# 启用Baseline级别,拒绝不符合规则的Pod
kubectl label namespace production pod-security.kubernetes.io/enforce=baseline

# 启用Restricted级别,并记录警告但不拒绝
kubectl label namespace sensitive pod-security.kubernetes.io/warn=restricted

这样配置后,任何试图在production命名空间创建特权容器的操作都会被拒绝。在sensitive命名空间,会收到警告但不会拒绝。

避坑指南:我曾经在迁移旧项目时,直接给所有命名空间打了Restricted标签,结果一堆Pod启动失败。因为很多旧镜像默认以root用户运行,而Restricted级别禁止这样做。所以,建议先使用warnaudit模式观察一段时间,确认没问题后再切换到enforce模式。

3.4 三者协同:构建纵深防御

RBAC、网络策略、Pod安全策略,这三者不是孤立的,而是层层递进的关系:

  • RBAC:控制「谁」能操作「什么资源」。
  • 网络策略:控制「哪个Pod」能访问「哪个Pod」。
  • Pod安全策略:控制「Pod本身」能做什么。

举个例子:即使某个攻击者通过RBAC漏洞获得了集群的只读权限,他也无法通过网络策略访问敏感服务,更无法通过Pod安全策略创建特权容器。这就是纵深防御的价值。

我个人习惯是:先配RBAC,再配网络策略,最后配Pod安全策略。这个顺序能最大程度减少配置冲突和遗漏。

总结一句话:K8s集群安全没有银弹,但RBAC、网络策略、Pod安全策略这三板斧用好了,能挡住90%的常见攻击。剩下的10%,靠持续监控和应急响应来补。

好了,这一章就到这里。下一章咱们聊聊容器镜像安全,那可是另一个大坑。嗯,到时候再细说。