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

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

为了简化用户体验并确保持续符合要求,你可以将 KyvernoAlauda Container Platform Compliance with Kyverno 插件结合使用,根据命名空间的 PSA 策略动态注入所需的 securityContext 字段。

前提条件

在继续之前,请确保:

  • Alauda Container Platform Compliance with Kyverno 插件已在你的集群中安装并启用。有关安装详细信息,请参阅 安装 Compliance 插件
  • 目标命名空间已应用 PSA 标签,例如 pod-security.kubernetes.io/enforce: restrictedbaseline

工作原理

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

PSA 强制配置

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

1. Restricted 策略变更

当命名空间强制使用 restricted 配置文件时,Workloads 必须删除所有权限、以非 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 配置文件时,Workloads 禁止使用 host namespaces 和特权容器。

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 配置文件的要求。