符合 Pod Security Restricted 标准

功能概述

pod-security.kubernetes.io/enforce=restricted 标准是最严格的 Pod Security Standard,用于强制执行当前 pod 加固最佳实践。当某个命名空间标记为此标准时,在该命名空间中创建的所有 pod 都必须符合严格的安全要求。

本指南说明如何配置 Tekton Pipelines 以满足 restricted 安全标准,确保 Tasks 中的所有容器都能在受限命名空间中成功运行。

使用场景

  • 在带有 pod-security.kubernetes.io/enforce=restricted 标记的命名空间中运行 Tekton Pipelines
  • 满足组织的安全合规要求
  • 提升 CI/CD 流水线的安全性
  • 在高度受监管的环境中运行工作负载

前提条件

  • 已通过 TektonPipelineTektonConfig CR 安装 Tekton Pipelines
  • 你有权限修改 TektonPipelineTektonConfig 资源
  • 你可以创建或编辑 Task 定义
  • 对于自定义镜像,你有权限修改 Containerfile 并重新构建镜像

了解 Restricted 标准

pod-security.kubernetes.io/enforce=restricted 标准要求 Pod 中的三种容器都满足以下安全上下文要求:

  • 普通容器 (containers)
  • Init 容器 (initContainers)
  • Ephemeral 容器 (ephemeralContainers)

每个容器都必须具有以下安全上下文配置:

securityContext:
  allowPrivilegeEscalation: false
  runAsNonRoot: true
  capabilities:
    drop:
      - "ALL"
  seccompProfile:
    type: RuntimeDefault

配置说明:

字段作用影响
allowPrivilegeEscalationfalse防止进程获得比其父进程更多的权限阻止恶意代码通过 setuid 二进制文件或文件能力提升权限
runAsNonRoottrue要求容器以非 root 用户运行(UID ≠ 0)防止容器以 root 身份运行,从而降低容器逃逸漏洞的影响
capabilities.drop["ALL"]移除容器的所有 Linux capabilities限制容器执行特权操作的能力(例如网络配置、加载内核模块)
seccompProfile.typeRuntimeDefault应用容器运行时的默认 seccomp 配置文件限制容器可用的系统调用,从而减小内核攻击面

步骤

1. 配置 Tekton 为 init 容器添加安全上下文

Tekton 会为其管理的每个 Pod 创建 init 容器。要符合 restricted 标准,你需要启用 Tekton,使其自动为这些 init 容器添加所需的安全上下文。

选项 A:通过 TektonPipeline 配置

如果你通过 TektonPipeline CR 直接管理 Tekton

apiVersion: operator.tekton.dev/v1alpha1
kind: TektonPipeline
spec:
  set-security-context: true

应用该配置:

$ kubectl patch tektonpipeline tektonpipeline \
  --type merge \
  -p '{"spec":{"set-security-context":true}}'

tektonpipeline.operator.tekton.dev/tektonpipeline patched

选项 B:通过 TektonConfig 配置

如果你通过 TektonConfig CR 管理 Tekton

apiVersion: operator.tekton.dev/v1alpha1
kind: TektonConfig
spec:
  pipeline:
    set-security-context: true

应用该配置:

$ kubectl patch tektonconfig config \
  --type merge \
  -p '{"spec":{"pipeline":{"set-security-context":true}}}'

tektonconfig.operator.tekton.dev/config patched

注意: 此配置更改会立即生效,无需重启 Tekton 控制器。在此更改之后创建的新 TaskRunPipelineRun 会自动将所需的安全上下文应用到 init 容器。

2. 为自定义 Task 定义添加安全上下文

对于自定义 Task(而不是平台提供的内置 Task),你需要显式地为每个 step 添加安全上下文。

带有安全上下文的 Task 示例:

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: custom-build-task
spec:
  steps:
    - name: build
      image: registry.example.com/builder:latest
      securityContext:
        allowPrivilegeEscalation: false
        runAsNonRoot: true
        capabilities:
          drop:
            - "ALL"
        seccompProfile:
          type: RuntimeDefault
      script: |
        #!/bin/sh
        echo "Building application..."

重要: 此步骤适用于自定义 Task。平台提供的内置 Taskbuildah 除外,见故障排查部分)与这些安全限制兼容,但其 Task 定义中不包含显式的 securityContext 配置,因为它们旨在适配多种安全上下文,而不仅仅是 restricted 模式。

3. 配置容器镜像使用非 root 用户

容器镜像必须配置为以非 root 用户运行。必须使用数字 UID 指定用户,而不是用户名。

在你的 Containerfile 中,添加一个非 root 用户并将其设为默认用户。以下是基于 Alpine 的镜像示例:

# Add a non-root user with UID 65532 (Alpine syntax)
RUN adduser -u 65532 -h /home/nonroot -D nonroot

# Set appropriate permissions for working directories
RUN chown -R 65532:65532 /app

# Switch to the non-root user (use numeric UID)
USER 65532

注意: adduser 命令语法因基础镜像而异。有关其他基础镜像的示例,请参见下面引用的文档。

验证镜像配置:

# Check that the image runs as non-root
$ podman run -it --rm ${registry} id

uid=65532(nonroot) gid=65532(nonroot) groups=65532(nonroot)

有关如何调整 Containerfile 的详细说明,请参阅 为构建兼容 Task 的自定义镜像调整 Containerfile

操作结果

完成上述步骤后:

  1. Init 容器Tekton 创建的 init 容器将自动符合 restricted 标准
  2. 自定义 Task:你的自定义 Task 步骤将具有所需的安全上下文
  3. 容器镜像:镜像将以非 root 用户身份运行,并且权限最小化

你可以通过在受限命名空间中创建一个 TaskRun 来验证合规性:

# Label a namespace with the restricted standard
$ kubectl label namespace test-ns pod-security.kubernetes.io/enforce=restricted

namespace/test-ns labeled

# Create a TaskRun in the restricted namespace
$ kubectl -n test-ns create -f taskrun.yaml

taskrun.tekton.dev/example-taskrun created

# Verify the TaskRun succeeds
$ kubectl -n test-ns get taskrun

NAME               SUCCEEDED   REASON      STARTTIME   COMPLETIONTIME
example-taskrun    True        Succeeded   2m ago      1m ago

成功的 TaskRun 会显示 SUCCEEDEDTrueREASONSucceeded,这表明所有容器都已在所需的安全限制下成功运行。

使用策略强制执行工具

为了实现更自动化的方法,你可以使用类似 Kyverno 的策略强制执行工具,自动为所有容器注入所需的安全上下文。通过创建一个 Kyverno Policy(使用 apiVersion: kyverno.io/v1kind: Policy),你可以自动变更 Pods,为其添加必要的安全上下文配置。

更多信息请参阅 场景 4:指定命名空间的安全上下文强制执行

Buildah 限制

这种基于 Kyverno 的方法不会使内置的 buildah Task 兼容 restricted 标准。有关 buildah 的限制,请参阅下面的 内置 Tasks 与 Restricted 标准

这种方法消除了为每个 Task 手动配置安全上下文的需要,从而简化了合规管理。

有关为此目的配置 Kyverno 策略的详细说明,请参阅:

故障排查

内置 Tasks 与 Restricted 标准

在平台提供的内置 Tasks 中,只有 buildah Task 不能在 restricted 标准下运行。这是因为 buildah 需要提升权限来构建容器镜像。

即使使用 Kyverno 自动为 Pods 注入所需的 restricted 安全上下文,这一限制仍然适用。Kyverno 可以添加 restricted 标准所需的字段,但无法使内置的 buildah Task 兼容 restricted,因为 buildah 需要该标准所禁止的权限。

buildah Task 需要以下安全上下文:

securityContext:
  # allowPrivilegeEscalation: false  # Cannot be set to false - buildah needs to escalate privileges to use the SETFCAP capability for setting file capabilities during container image builds; forcing false also blocks newuidmap/newgidmap, causing rootless to fall back to single UID/GID mapping with warnings and potentially incorrect file ownership
  runAsNonRoot: true
  capabilities:
    # drop: ["ALL"]  # Cannot drop ALL capabilities - buildah requires SETFCAP capability to set file capabilities (e.g., cap_net_bind_service) on files within container images during the build process
    add: ["SETFCAP"]
  # seccompProfile:
  #   type: RuntimeDefault  # Cannot use RuntimeDefault - default seccomp blocks unshare/clone(CLONE_NEWUSER)/setns needed by buildah, leading to build failure; use Unconfined or a custom profile that allows these syscalls instead

解决方案:

  1. 在单独的命名空间中使用 buildah Task,并将模式设置为 baseline 而不是 restricted

    要允许 buildah 工作负载运行,将 pod-security.kubernetes.io/enforce 更改为 baseline 是必需步骤。warnaudit 标签不会阻止准入。

    # Required: change enforce to baseline for buildah workloads
    $ kubectl label namespace build-ns \
      pod-security.kubernetes.io/enforce=baseline \
      --overwrite
    
    namespace/build-ns labeled

    重新标记命名空间后,在该命名空间中创建一个使用 buildah Task 的新 TaskRunPipelineRun。当 registry 凭据、Containerfile 和 workspace 配置正确时,buildah Task 可以在 baseline 标准下正常运行。

  2. 探索其他可能具有不同安全要求的容器镜像构建方法。

TaskRun 因容器用户错误而失败

你可能会遇到以下错误之一:

  • "container has runAsNonRoot and image will run as root" - 容器镜像在 Containerfile 中没有 USER 指令(默认为 root),或显式设置为 USER 0USER root
  • "container has runAsNonRoot and image has non-numeric user" - 容器镜像使用了符号用户名(例如 USER appuser),而不是数字 UID

解决方案:

  1. 按照步骤 3 的说明,使用数字非 root 用户 ID 重新构建镜像。例如,使用 USER 65532,而不是 USER appuserUSER root

  2. TasksecurityContext 中指定用户:

    apiVersion: tekton.dev/v1
    kind: Task
    metadata:
      name: example-task
    spec:
      steps:
        - name: step1
          image: registry.example.com/myimage:latest
          securityContext:
            runAsUser: 65532
            runAsNonRoot: true
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - "ALL"
            seccompProfile:
              type: RuntimeDefault
          script: |
            #!/bin/sh
            echo "Running as user $(id -u)"
  3. TaskRunpodTemplate 中指定用户:

    apiVersion: tekton.dev/v1
    kind: TaskRun
    metadata:
      name: example-taskrun
    spec:
      taskRef:
        name: example-task
      podTemplate:
        securityContext:
          runAsUser: 65532
          runAsNonRoot: true
          fsGroup: 65532

注意: podTemplate.securityContext 设置的是 Pod 级别的安全上下文,除非在容器级别被覆盖,否则会被所有容器继承。

当你无法修改容器镜像时,选项 2 和 3 会很有用。

TaskRun 因 "Forbidden: cannot set securityContext.capabilities" 而失败

当安全上下文尝试在删除所有 capabilities 的同时添加 capabilities 时,就会出现此错误。

解决方案: 确保你的 Task 不添加任何 capabilities。仅按示例所示删除 capabilities。

Init 容器在权限错误下失败

如果在设置 set-security-context: true 后 init 容器仍因权限错误而失败,请验证以下内容:

  1. 命名空间具有适当的安全策略
  2. Tekton 使用的容器镜像已配置为以非 root 身份运行
  3. Tekton 安装已更新到最新版本

了解更多