Pod 的安全机制

1. SecurityContext

1.1. 概述

SecurityContext 是 Pod 或容器配置中的一个字段,用于设定操作系统级的安全属性。这些属性最终会传递给容器运行时(如 containerd、CRI-O),由它们来强制执行,确保容器进程在严格限制的权限下运行。

简单来说,它就是 Pod 或容器的 “安全身份证” 和 “行为准则”,告诉系统它应该以什么用户身份运行、能做什么、不能做什么。

1.2. 两个层级的作用域

SecurityContext 可以在两个级别上配置,其应用范围和优先级不同:

  • Pod-level SecurityContext (spec.securityContext)
    • 应用于 Pod 内的所有容器
    • 设置一些影响整个 Pod 的属性,如用户/组ID、文件系统组、SELinux 选项等。
    • 为 Pod 内所有容器提供默认的安全设置
  • Container-level SecurityContext (spec.containers[].securityContext)
    • 仅应用于特定的容器
    • 可以覆盖 Pod-level 的设置(如 runAsUser)。
    • 可以设置容器特有的属性,如能力(Capabilities)、权限提升、只读根文件系统等。

注意:容器级的配置会覆盖 Pod 级的相同配置。

1.3. 核心配置字段详解

以下是 SecurityContext 中最重要和最常用的字段:

用户和组身份 (User and Group Identity)

控制容器进程以什么用户和组身份运行,这是最基本的安全措施。

  • runAsUser:指定容器进程的 UID(用户ID)。强制以非 root 用户(如 1000)运行是安全最佳实践。
securityContext:
  runAsUser: 1000 # 不以 root (0) 运行
YAML
  • runAsGroup:指定容器进程的主 GID(组ID)
securityContext:
  runAsGroup: 2000
YAML
  • runAsNonRoot布尔值。这是一个安全开关,如果设为 true,Kubernetes 会检查镜像是否配置了非 root 用户(通过 USER 指令)或 runAsUser 是否非 0。如果检查失败,容器启动失败。
securityContext:
  runAsNonRoot: true # 必须非root运行
YAML
  • supplementalGroups:为容器进程指定附加的组 GIDs。常用于访问特定组权限的持久卷(PV)。

权限能力 (Linux Capabilities)

Linux Capabilities 将 root 用户的超级权限划分为不同的单元,允许你赋予容器它所需的最小权限,而不是完整的 root 权限。这是实现“最小权限原则”的关键。

  • capabilities:一个包含 add 和 drop 列表的对象。
    • drop必须项。丢弃所有不必要的权限,通常首先丢弃 ALL
    • add可选项。在丢弃全部的基础上,只添加业务必须的个别权限。
securityContext:
  capabilities:
    drop:           # 丢弃所有权限
      - ALL
    add:            # 只添加一个网络相关权限(绑定低端口号)
      - NET_BIND_SERVICE
YAML

常见需添加的权限:

  • NET_BIND_SERVICE:绑定到 1024 以下的端口。
  • CHOWNDAC_OVERRIDE: 文件所有权覆盖(某些应用需要)。
  • SYS_TIME: 修改系统时间。

特权模式 (Privileged Mode)

极度危险,应避免使用! 特权容器几乎拥有对宿主机的全部访问权限。

  • privileged:如果设置为 true,容器将在特权模式下运行。
securityContext:
  privileged: false # 永远不要设为 true,除非有极端特殊情况
YAML

权限提升 (Privilege Escalation)

防止容器进程通过 suid 等机制提升其权限。

  • allowPrivilegeEscalation布尔值。应始终设为 false,除非有明确需求。如果 privileged: true 或添加了 CAP_SYS_ADMIN 能力,则此选项默认为 true
securityContext:
  allowPrivilegeEscalation: false # 禁止权限提升
YAML

1.4. 示例

apiVersion: v1
kind: Pod
metadata:
  name: secured-app
spec:
  securityContext: # Pod级别的设置,是所有容器的默认值
    runAsUser: 10000
    runAsGroup: 10000
    fsGroup: 10000 # 影响卷的文件组权限
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: nginx
    image: nginx:1.25
    securityContext: # 容器级别的设置,覆盖Pod级别的相同设置
      allowPrivilegeEscalation: false
      runAsNonRoot: true # 二次确认,虽然runAsUser已是非0
      capabilities:
        drop: ["ALL"]
        add: ["NET_BIND_SERVICE"] # nginx需要绑定80端口
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: cache
      mountPath: /var/cache/nginx # 为需要写入的目录挂载卷
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: cache
    emptyDir: {}
  - name: tmp
    emptyDir: {}
YAML

2. PodSecurity

2.1. 概述

PodSecurity 是一个内置的准入控制器(Admission Controller),它基于命名空间(Namespace)的标签,对创建 Pod 的请求实施并强制遵守 Pod 安全标准(Pod Security Standards)。

2.2. Pod Security Standards (PSS) – 安全标准

PodSecurity 的实施基于三个预定义的安全级别标准:

  • Baseline Policy:基准等级策略,限制性最弱,允许使用默认的(规定最少)Pod 权限配置,并禁止已知的权限提升。
  • Restricted Policy:限制等级策略,限制性非常强,遵循当前的Pod安全防护的最佳实践。
  • Privileged Policy:特权等级策略,提供最大可能范围的权限许可,此标准允许已知的权限提升。

2.3. 配置和使用 PodSecurity

确保 API Server 启用了准入控制器:

# PodSecurity 准入控制器在 Kubernetes v1.23+ 中默认启用。对于旧版本,你需要确保 API Server 的启动参数包含:
--enable-admission-plugins=...,PodSecurity
Bash

为命名空间打标签:

# 配置的核心是为命名空间添加特定的标签。标签的格式为:
pod-security.kubernetes.io/<MODE>: <LEVEL>
# <LEVEL>:要实现的安全策略的名字,包括 baseline、restricted、privileged
# <MODE>:当违反安全策略后执行的动作。

# 以及一个可选的版本标签:
pod-security.kubernetes.io/<MODE>-version: <VERSION>
# <VERSION>:kubernetes 的版本号,因为 Pod 的安全策略的实现是与 kubernetes 版本相关的
Bash

MODE 包含的选项:

  • enforce
    • 拒绝任何违反政策的 Pod 创建请求。
    • 这是强制执行的模式。
  • warn
    • 向用户发出警告(在 API 响应中返回警告信息),但允许创建违反政策的 Pod。
    • 适用于试运行和过渡期。
  • audit
    • 在审计日志中记录违反政策的事件,但允许创建 Pod。
    • 用于监控和发现集群中存在的不合规资源,而不中断业务。

一个命名空间可以同时配置这三种模式,例如:对 restricted 标准进行 enforce,同时对 baseline 标准进行 audit

示例 1:对一个命名空间实施严格的 Restricted 策略

apiVersion: v1
kind: Namespace
metadata:
  name: my-secure-app
  labels:
    # 核心:强制执行 restricted 标准
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.31 # 可选:指定标准版本
    # 同时,对 baseline 级别的违规进行审计和警告
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
YAML

示例 2:对一个命名空间进行试运行(只警告和审计,不强制)

apiVersion: v1
kind: Namespace
metadata:
  name: my-test-app
  labels:
    pod-security.kubernetes.io/warn: baseline
    pod-security.kubernetes.io/audit: baseline
# 不设置 `enforce` 标签,表示不会拒绝创建 Pod
YAML

在 my-secure-app 名称空间创建 Pod 测试效果:

# test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test
    image: nginx:1.18
    securityContext:
      runAsUser: 0 # 尝试以 root 用户运行
YAML

输出结果:

$ kubectl apply -f test-pod.yaml -n my-secure-app 
Error from server (Forbidden): error when creating "test-pod.yaml": pods "test-pod" is forbidden: violates PodSecurity "restricted:v1.31": allowPrivilegeEscalation != false (container "test" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "test" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "test" must set securityContext.runAsNonRoot=true), runAsUser=0 (container "test" must not set runAsUser=0), seccompProfile (pod or container "test" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
Bash

2.4. 豁免 (Exemptions)

PodSecurity 允许配置豁免,以避免对某些用户、服务账户或运行时类(如 virtlet)进行安全检查。

注意: 豁免是在 Admission Configuration 中静态配置的,而不是通过命名空间标签。你需要创建一个配置文件供 API Server 加载。

示例:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "privileged"
      enforce-version: "latest"
      audit: "privileged"
      audit-version: "latest"
      warn: "privileged"
      warn-version: "latest"
    exemptions:
      usernames: []
      runtimeClasses: []
      namespaces: []
YAML

上述配置文件中的 exemptions 部分即对 Pod 的豁免声明,其中:

  • usernames:表示要豁免的用户列表,即提交API请求时的用户名。
  • runtimeClasses:表示要豁免的 runtimeclass 列表,即对这些 runtime class 的 Pod 实例进行豁免。
  • namespaces:表示要豁免的命名空间列表。

此外,配置文件中的 defaults 部分是用来设置默认安全策略的,当我们给 Namespace 设置的标签中没有指定 Level 时,PodSecurity 就会使用这部分的默认值。

上述配置文件需要通过 --admission-control-config-file 参数让 API Server 加载此配置才能生效。

3. SecurityContext 与 PodSecurity 关系

PodSecurity 和 SecurityContext 是 Kubernetes 中相辅相成的两种安全机制,它们的关系是 “宏观策略控制” 与 “微观具体实现” 的关系。

  • PodSecurity 是集群管理员制定的 “安全规则”(Policy)。它规定 Pod 必须不得做什么。
  • SecurityContext 是 Pod 或容器开发者定义的 “安全配置”(Configuration)。它具体设置 Pod 或容器 如何运行。

类比:建筑安全规范

  • PodSecurity 就像是国家的建筑安全法规
    • 法规规定:“所有高层建筑必须使用防火材料”(这相当于 enforce: restricted 策略)。
  • SecurityContext 就像是建筑师的设计图纸
    • 图纸上写明:“这栋大楼将使用钢筋混凝土和防火石膏板”(这相当于在 Pod 中设置 seccompProfile: RuntimeDefault 和 allowPrivilegeEscalation: false)。
  • PodSecurity 准入控制器的工作就是检查“设计图纸”(Pod Spec)是否违反了“国家法规”(PS 策略)。如果图纸上写的是“使用木材”,它就会拒绝这个建筑申请。

示例:二者如何配合

  • PodSecurity 策略(命名空间层级)
# 命名空间的策略:强制执行 restricted 标准
apiVersion: v1
kind: Namespace
metadata:
  name: secure-app
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.31
YAML
  • SecurityContext 配置(Pod 层级)
apiVersion: v1
kind: Pod
metadata:
  name: nginx-secure
  namespace: secure-app
spec:
  securityContext: # Pod级别的安全上下文(影响所有容器和volumes)
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 3000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: nginx
    image: nginx
    securityContext: # 容器级别的安全上下文(仅影响本容器)
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"] # 丢弃所有权限
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}
YAML
  • 解读:
    • PodSecurity 的 restricted 策略要求 Pod 必须设置:runAsUser != 0allowPrivilegeEscalation: false 等一系列设置。
    • Pod 的开发者就在 securityContext 中具体配置了这些值(runAsUser: 1000allowPrivilegeEscalation: false)来满足要求。
    • 如果没有配置,PodSecurity 会拒绝它。如果配置的值不对(如 runAsUser: 0),PodSecurity 同样会拒绝它。

4. Pod 的网络安全

为了确保 Pod 的安全,Kubernetes 提供了相应的网络访问的安全控制机制。比如 Pod 有专门的网络地址段,它们不能被 Kubernetes 集群之外的主机访问;再如,位于不同 Namespace 的 Pod 如果需要相互访问,则必须知道对方的Namespace,否则也无法访问。为了实现更为严格的 Pod 网络安全,Kubernetes从v1.3版本开始,专门设计了 NetworkPolicy,并在后继版本中不断完善 NetworkPolicy 提供的安全特性。

4.1. Namespace 网络隔离

首先,必须明确一个最关键的概念:Kubernetes 的 Namespace 本身并不提供任何形式的网络隔离。

默认情况下,在一个集群中的 Pod 是在同一个局域网内,所以它们之间的网络是直通的,那么如何禁止不同业务之间的 Pod 之间的访问呢?

有一个简单的方案,就是利用 Service 和 Namespace,将不同业务的 Pod 和 Service 部署到不同的名称空间。这样 PodA 要访问 PodB 则需要知道 PodB 所在的名称空间才能通过 Service 的域名 <service-name>.<namespace>.svc.<clusterDomain> 访问。如果 PodA 想绕过 Service 直接通过 IP 访问 PodB 通常很难获取到 PodB 的 IP 地址,但还是有机会获取到,如通过网络地址和端口扫描工具等。

4.2. NetworkPolicy 网络隔离

NetworkPolicy 是 Kubernetes 中定义 Pod 网络隔离规则的核心 API 对象。它相当于 Pod 的防火墙,控制着进出 Pod 的流量。

NetworkPolicy 关键概念:

  • 选择器 (Selector):NetworkPolicy 通过标签选择器 (podSelector) 来指定该策略适用于哪些 Pod(即保护谁)。
  • 策略类型 (PolicyTypes)
    • Ingress:控制进入 Pod 的流量(入站规则)。
    • Egress:控制 Pod发出的流量(出站规则)。
  • 默认拒绝 (Default Deny)关键原则。一旦某个 Pod 被一个 NetworkPolicy 选中,它将默认拒绝所有未被该策略明确允许的流量(对于该策略指定的类型)。没有任何 NetworkPolicy 选中的 Pod 则默认允许所有流量。

示例:

  • 示例 1:默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {} # 空选择器,选中当前命名空间所有Pod
  policyTypes:
  - Ingress
  # ingress 字段为空,表示不允许任何入站流量
  # 但允许所有出站流量(因为没有指定egress策略)
YAML
  • 示例 2:允许来自特定 Pod 的流量
# 允许 frontend Pod 访问 backend Pod 的 6379 端口。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
spec:
  podSelector:
    matchLabels:
      app: backend # 此策略保护标有 app=backend 的Pod
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: # 允许来自的Pod
        matchLabels:
          app: frontend
    ports: # 允许访问的端口
    - protocol: TCP
      port: 6379
YAML
  • 示例 3:允许出站到外部 DNS
# 允许所有 Pod 访问外部 DNS 服务器(端口 53)。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
spec:
  podSelector: {} # 适用于所有Pod
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector: {} # 选中所有命名空间(但不匹配外部IP)
    ports:
    - protocol: UDP
      port: 53
  - to: # 更佳实践:直接指定外部CIDR块
    - ipBlock:
        cidr: 8.8.8.8/32 # 例如,允许访问Google DNS
    ports:
    - protocol: UDP
      port: 53
YAML
  • 示例 4:跨命名空间访问
# 允许 monitoring 命名空间中带有 app=prometheus 标签的 Pod 访问当前命名空间中所有 Pod 的 9090 端口。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-prometheus
spec:
  podSelector: {} # 允许Prometheus访问本命名空间所有Pod
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector: # 选择命名空间
        matchLabels:
          kubernetes.io/metadata.name: monitoring # 通过标签选择命名空间
      podSelector: # 在选中的命名空间里再选择Pod
        matchLabels:
          app: prometheus
    ports:
    - protocol: TCP
      port: 9090
YAML

NetworkPolicy 只是一个 API 定义,它本身不提供任何网络能力。策略的强制执行依赖于Container Network Interface (CNI) 插件

支持 NetworkPolicy 的流行 CNI 插件:

  • Calico:功能最丰富、性能极高的网络插件,支持复杂的网络策略。
  • Cilium:基于 eBPF 技术的新一代网络插件,提供极强的可观测性和安全能力,性能卓越。
  • Weave Net:易于安装和使用的插件,也提供网络策略支持。
  • Antrea:基于 Open vSwitch,由 VMware 发起,是 CNCF 项目。

如果你的集群没有安装这些插件,那么你创建的 NetworkPolicy 将不会产生任何效果。

上一篇
下一篇