为工作负载动态注入 PSA 安全上下文

在命名空间上启用 Pod Security Admission(PSA)后,在其中创建的任何 Workload(例如 Deployment 或 StatefulSet)都必须符合指定的安全配置文件。如果 Workload 的 securityContext 不符合要求,则由该 Workload 生成的 Pod 将在 Pod 准入期间被 Kubernetes API server 拒绝,即使 Workload 资源本身已被接受。

为了简化用户体验并确保持续合规,您可以将 Kyverno 与 Alauda Container Platform Policy Management 插件结合使用,根据命名空间的 PSA 策略动态注入所需的 securityContext 字段。

前提条件

继续操作前,请确保:

  • 集群中已安装并启用 Alauda Container Platform Policy Management 插件。有关安装详情,请参阅安装 Compliance 插件。
  • 目标命名空间已应用 PSA 标签,例如 pod-security.kubernetes.io/enforce: restricted 或 baseline。

工作原理

  1. 命名空间标记:为命名空间分配特定的 PSA 级别(例如 restricted)。
  2. Kyverno 拦截:用户提交 Workload 创建请求时,Kyverno 会在准入阶段拦截该请求。
  3. Compliance 插件变更:Kyverno 与 Compliance 插件协同工作,读取目标命名空间的 PSA 配置。
  4. 动态修补:Kyverno 根据 PSA 级别,使用适当的 securityContext 动态修补 Workload 的 Pod 模板(例如添加 runAsNonRoot: true、删除特定 capabilities,或设置 seccompProfile)。

PSA 强制执行配置

安装 Compliance 插件后,您可以应用以下 ClusterPolicy 配置。这些策略使用上下文变量查询命名空间的 pod-security.kubernetes.io/enforce 标签,并针对相应的 PSA 配置文件,在所有容器类型(包括 initContainers 和 ephemeralContainers)中应用所需的精确 securityContext 变更。

1. Restricted 策略变更

当命名空间强制执行 restricted 配置文件时,Workload 必须删除所有权限、以非 root 用户身份运行,并使用默认 seccomp 配置文件。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: mutate-psa-restricted
  annotations:
    policies.kyverno.io/title: Mutate Workload for PSA Restricted
    policies.kyverno.io/description: >-
      Dynamically injects the Restricted PSA securityContext into Workloads based on the target Namespace label.
spec:
  rules:
    - name: inject-restricted-security-context-controllers
      match:
        any:
          - resources:
              kinds:
                - Deployment
                - StatefulSet
                - DaemonSet
                - Job
                - CronJob
      context:
        - name: namespacePSA
          apiCall:
            urlPath: "/api/v1/namespaces/{{request.namespace}}"
            jmesPath: "metadata.labels.\"pod-security.kubernetes.io/enforce\" || 'none'"
      preconditions:
        all:
        - key: "{{namespacePSA}}"
          operator: Equals
          value: "restricted"
      mutate:
        patchStrategicMerge:
          spec:
            template:
              spec:
                securityContext:
                  runAsNonRoot: true
                  seccompProfile:
                    type: RuntimeDefault
                containers:
                  - (name): "?*"
                    securityContext:
                      allowPrivilegeEscalation: false
                      capabilities:
                        drop:
                          - ALL
                initContainers:
                  - (name): "?*"
                    securityContext:
                      allowPrivilegeEscalation: false
                      capabilities:
                        drop:
                          - ALL
                ephemeralContainers:
                  - (name): "?*"
                    securityContext:
                      allowPrivilegeEscalation: false
                      capabilities:
                        drop:
                          - ALL
    - name: inject-restricted-security-context-pods
      match:
        any:
          - resources:
              kinds:
                - Pod
      context:
        - name: namespacePSA
          apiCall:
            urlPath: "/api/v1/namespaces/{{request.namespace}}"
            jmesPath: "metadata.labels.\"pod-security.kubernetes.io/enforce\" || 'none'"
      preconditions:
        all:
        - key: "{{namespacePSA}}"
          operator: Equals
          value: "restricted"
      mutate:
        patchStrategicMerge:
          spec:
            securityContext:
              runAsNonRoot: true
              seccompProfile:
                type: RuntimeDefault
            containers:
              - (name): "?*"
                securityContext:
                  allowPrivilegeEscalation: false
                  capabilities:
                    drop:
                      - ALL
            initContainers:
              - (name): "?*"
                securityContext:
                  allowPrivilegeEscalation: false
                  capabilities:
                    drop:
                      - ALL
            ephemeralContainers:
              - (name): "?*"
                securityContext:
                  allowPrivilegeEscalation: false
                  capabilities:
                    drop:
                      - ALL

2. Baseline 策略变更

当命名空间强制执行 baseline 配置文件时,Workload 禁止使用主机命名空间和特权容器。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: mutate-psa-baseline
  annotations:
    policies.kyverno.io/title: Mutate Workload for PSA Baseline
    policies.kyverno.io/description: >-
      Dynamically injects the Baseline PSA securityContext into Workloads based on the target Namespace label.
spec:
  rules:
    - name: inject-baseline-security-context-controllers
      match:
        any:
          - resources:
              kinds:
                - Deployment
                - StatefulSet
                - DaemonSet
                - Job
                - CronJob
      context:
        - name: namespacePSA
          apiCall:
            urlPath: "/api/v1/namespaces/{{request.namespace}}"
            jmesPath: "metadata.labels.\"pod-security.kubernetes.io/enforce\" || 'none'"
      preconditions:
        all:
        - key: "{{namespacePSA}}"
          operator: Equals
          value: "baseline"
      mutate:
        patchStrategicMerge:
          spec:
            template:
              spec:
                hostNetwork: false
                hostIPC: false
                hostPID: false
                containers:
                  - (name): "?*"
                    securityContext:
                      privileged: false
                initContainers:
                  - (name): "?*"
                    securityContext:
                      privileged: false
                ephemeralContainers:
                  - (name): "?*"
                    securityContext:
                      privileged: false
    - name: inject-baseline-security-context-pods
      match:
        any:
          - resources:
              kinds:
                - Pod
      context:
        - name: namespacePSA
          apiCall:
            urlPath: "/api/v1/namespaces/{{request.namespace}}"
            jmesPath: "metadata.labels.\"pod-security.kubernetes.io/enforce\" || 'none'"
      preconditions:
        all:
        - key: "{{namespacePSA}}"
          operator: Equals
          value: "baseline"
      mutate:
        patchStrategicMerge:
          spec:
            hostNetwork: false
            hostIPC: false
            hostPID: false
            containers:
              - (name): "?*"
                securityContext:
                  privileged: false
            initContainers:
              - (name): "?*"
                securityContext:
                  privileged: false
            ephemeralContainers:
              - (name): "?*"
                securityContext:
                  privileged: false

注意:对于 privileged PSA 配置文件,无需进行变更,因为它允许不受限制的访问。

验证

要验证动态配置是否正常工作:

  1. 使用 restricted PSA 标签创建命名空间:
    kubectl create namespace test-psa
    kubectl label namespace test-psa pod-security.kubernetes.io/enforce=restricted
  2. 部署一个未指定 securityContext 的简单原生应用:
    kubectl create deployment nginx --image=nginxinc/nginx-unprivileged -n test-psa
  3. 检查所创建 Pod 的 YAML 定义:
    kubectl get pod -n test-psa -l app=nginx -o yaml
    您应看到自动注入的 securityContext 字段,以满足 restricted 配置文件的要求。