安全壁垒 – K8s 的 RBAC、NetworkPolicy 与 SecurityContext 精要
如果说 Kubernetes 是我们构建云原生应用的“城市”,那么我们已经学会了如何规划道路(网络)、建设住宅(Pod 调度)、提供水电(存储)以及智能调节城市规模(自动伸缩)。现在,是时候为这座城市安装“城门门禁”(API 访问控制)、划分“安全区域”(网络隔离)、加固“建筑安防”(运行时安全)以及设置“保险柜”(敏感信息管理)了。
K8s API 访问控制:RBAC (基于角色的访问控制)
Kubernetes 的所有操作都是通过与其 API Server 组件交互来完成的。因此,保护 API Server 并精确控制谁(Subject)可以对哪些资源(Resource)执行哪些操作(Verb) 是 K8s 安全的第一道防线。这就是 RBAC (Role-Based Access Control) 的用武之地。
核心概念:
Subject (主体):发起请求的“人”或“程序”。可以是:
User
: 代表真实的人类用户。
Group
: 代表一组用户。
ServiceAccount
: 代表运行在 Pod 内的进程身份,供应用程序或 K8s 内部组件与 API Server 交互。
Resource (资源):API Server 中可以被操作的对象,如 pods
, deployments
, services
, nodes
, secrets
, configmaps
等。
Verb (动词):可以对资源执行的操作,如 get
, list
, watch
, create
, update
, patch
, delete
, exec
等。
Role / ClusterRole (角色 / 集群角色):一组权限规则的集合,定义了允许对哪些资源执行哪些动词。
Role
: 命名空间级别的,其权限仅在定义的 Namespace 内有效。
ClusterRole
: 集群级别的,其权限在整个集群内都有效(也可以用于授权访问非命名空间资源,如 nodes
)。
RoleBinding / ClusterRoleBinding (角色绑定 / 集群角色绑定):将一个主体(User, Group 或 ServiceAccount)与一个角色(Role 或 ClusterRole)绑定起来,从而将角色中定义的权限授予该主体。
RoleBinding
: 在特定命名空间内进行绑定。
ClusterRoleBinding
: 在整个集群范围内进行绑定。
SRE 的最佳实践:
最小权限原则 (Principle of Least Privilege):永远只授予必要的最小权限。避免随意给用户或 ServiceAccount 授予 cluster-admin
这种超级权限。
为应用使用 ServiceAccount
: 应用 Pod 如果需要访问 API Server(例如,某些监控组件或 Operator),应该为其创建专用的 ServiceAccount
,并精确绑定所需的 Role
或 ClusterRole
。不要使用默认的 ServiceAccount 或共享高权限账户。
定期审计 RBAC 配置: 检查是否有过多或不必要的权限分配。
配置示例:
假设我们要创建一个 pod-reader
Role,允许读取 my-ns
命名空间下的 Pod 信息,并将其绑定到一个名为 app-reader-sa
的 ServiceAccount。
# role-pod-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: my-ns
name: pod-reader
rules:
- apiGroups: [""] # "" 表示核心 API 组
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
# sa-app-reader.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-reader-sa
namespace: my-ns
---
# rolebinding-app-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: my-ns
subjects:
- kind: ServiceAccount
name: app-reader-sa # 绑定到上面的 ServiceAccount
namespace: my-ns
roleRef: # 引用上面的 Role
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
应用这些 YAML 后(kubectl apply -f ...
),在 my-ns
命名空间中,拥有 app-reader-sa
这个 ServiceAccount 身份的 Pod 内进程就只能对 Pod 进行 get/watch/list 操作了。任何未授权的 API 请求(输入)都会收到 403 Forbidden 错误(输出)。
网络隔离:NetworkPolicy
默认情况下,Kubernetes 集群中的所有 Pod 之间可以互相通信(取决于 CNI 插件的默认行为),即使它们位于不同的命名空间。这显然不符合零信任安全模型,也增大了潜在攻击的“爆炸半径”。NetworkPolicy
资源允许我们定义 Pod 间的网络流量规则,实现网络隔离。
工作原理: NetworkPolicy
依赖于实现了该功能的 CNI 网络插件(如 Calico, Cilium, Weave Net 等)。这些插件会根据 NetworkPolicy
资源的定义,在底层(如使用 iptables, eBPF)配置相应的网络规则来允许或阻止流量。如果你的 CNI 插件不支持 NetworkPolicy,那么创建这个资源也不会有任何效果。
核心概念:
Pod 选择器 (podSelector
): NetworkPolicy
应用于匹配特定标签的 Pod。
策略类型 (policyTypes
): 定义策略是针对入站流量 (Ingress
) 还是出站流量 (Egress
),或者两者都有。
规则 (rules
):
from
(用于 Ingress 规则): 定义允许哪些来源的流量进入。
to
(用于 Egress 规则): 定义允许流量出去到哪些目的地。
ports
: 定义允许通信的端口和协议 (TCP/UDP)。
来源和目的地可以通过 podSelector
(选择其他 Pod)、namespaceSelector
(选择其他 Namespace 中的 Pod) 或 ipBlock
(指定 CIDR 网段) 来定义。
默认行为:
如果一个 Pod 没有被任何 NetworkPolicy
的 podSelector
选中,那么它的所有出入站流量都是允许的(默认放行)。
一旦一个 Pod 被至少一个 NetworkPolicy
选中(针对特定方向,如 Ingress),那么该方向的流量就会变为默认拒绝,只有被该方向策略中明确 ALLOW
的规则匹配到的流量才会被放行。
YAML 示例: 只允许带有 app: frontend
标签的 Pod 访问带有 app: backend
标签的 Pod 的 TCP 8080 端口。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend-ingress
namespace: my-app
spec:
podSelector: # 此策略应用于带有 app=backend 标签的 Pod
matchLabels:
app: backend
policyTypes:
- Ingress # 只定义入站规则
ingress:
- from: # 允许来自以下源的流量
- podSelector:
matchLabels:
app: frontend # 只允许带有 app=frontend 标签的 Pod 访问
ports: # 只允许访问以下端口
- protocol: TCP
port: 8080
对 SRE 的意义: NetworkPolicy
是实现微服务间网络微隔离、遵循最小权限原则、减少攻击面的关键工具。SRE 需要与应用团队合作定义合理的网络策略。
Pod 与容器安全:SecurityContext
容器与宿主机共享内核,如果容器内部进程权限过高或配置不当,可能会对宿主机或其他容器造成安全威胁。SecurityContext
允许我们在 Pod 级别或单个 Container 级别定义特权和访问控制设置,以加固运行时安全。
关键设置举例:
runAsUser
/ runAsGroup
/ fsGroup
: 以指定的非 root 用户 ID / 组 ID 运行容器进程,并控制文件系统访问权限。
runAsNonRoot: true
: 强制容器必须以非 root 用户运行。
privileged: false
: 强烈建议设置为 false
(通常也是默认值)。禁止容器以特权模式运行(特权容器几乎拥有宿主机的所有权限)。
readOnlyRootFilesystem: true
: 使容器的根文件系统变为只读,阻止恶意代码修改系统文件。
allowPrivilegeEscalation: false
: 阻止容器内的进程获取比其父进程更高的权限。
capabilities
: 精细控制容器被授予或移除的 Linux Capabilities(例如 drop: ["ALL"]
移除所有特权,然后 add: ["NET_BIND_SERVICE"]
只允许绑定低位端口)。
YAML 示例:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext: # Pod 级别的安全上下文
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: my-secure-container
image: my-image
securityContext: # Container 级别的安全上下文 (可以覆盖 Pod 级别)
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
# add:
# - NET_BIND_SERVICE
Pod 安全准入 (Pod Security Admission – PSA) / Pod 安全策略 (Pod Security Policies – PSP, 已废弃): K8s 提供集群级别的机制来强制实施 Pod 的安全基线标准(如禁止特权容器、限制 hostPath
挂载等)。PSA 是较新的、内置的替代 PSP 的方案。
对 SRE 的意义: SRE 应该推动并帮助实施合理的 SecurityContext
默认配置和 Pod 安全标准,以降低工作负载的潜在攻击面。
敏感信息管理:Secrets
应用程序经常需要密码、API 密钥、TLS 证书等敏感信息。直接将这些信息硬编码到代码或配置文件中是严重的安全隐患。K8s 提供了 Secret
对象来存储和管理这些少量敏感数据。
使用方式:
可以将 Secret 中的数据作为文件挂载到 Pod 的指定路径中(推荐)。
也可以将 Secret 中的数据作为环境变量注入到容器中(不推荐,因为环境变量可能更容易通过日志、/proc
文件系统等途径泄露)。
安全考量:
默认情况下,K8s Secret 中的数据只是进行了 Base64 编码,并非加密存储在 etcd 中! 要实现静态加密 (encryption at rest),需要在 K8s API Server 中配置 etcd 加密提供者。
必须通过 RBAC 严格限制对 Secret 对象的读取权限。
对于更高安全性的需求,推荐集成外部的密钥管理系统 (KMS),如 HashiCorp Vault, AWS KMS, Google Cloud KMS 等,并使用 Secrets Store CSI Driver 或 External Secrets Operator 等工具将外部密钥安全地同步或挂载到 Pod 中。
对 SRE 的意义: SRE 负责管理 Secret 的生命周期(创建、轮换、删除),确保其存储和访问的安全性,并推广安全使用敏感信息的最佳实践,可能还需要负责集成和维护与外部 KMS 的对接。
总结
Kubernetes 提供了多层次的安全机制来保护集群和运行在上面的应用:通过 RBAC 控制对 API Server 的访问,通过 NetworkPolicy 实现网络层面的隔离,通过 SecurityContext 加固 Pod 和容器的运行时环境,并通过 Secrets(最好结合外部 KMS)来管理敏感信息。
作为 SRE,理解并有效运用这些安全工具和原则,是保障 Kubernetes 环境可靠、稳定运行不可或缺的一环。安全是一个持续的过程,需要与开发团队紧密合作,共同构建和维护一个安全可信的平台。
暂无评论内容