符合 Pod Security Restricted 标准

功能概述

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

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

使用场景

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

前提条件

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

了解 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 controller。此更改之后创建的新 TaskRunsPipelineRuns 会自动为 init 容器应用所需的安全上下文。

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

对于自定义 Tasks(不是平台提供的内置 Tasks),你需要显式为每个 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..."

重要: 此步骤适用于自定义 Tasks。平台提供的内置 Tasksbuildah 除外,见“故障排除”部分)与这些安全约束兼容,但它们的 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)

有关调整 Containerfiles 的详细指南,请参见 调整用于构建兼容 Task 的自定义镜像的 Containerfile

操作结果

完成上述步骤后:

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

你可以通过在受限的 namespace 中创建 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),你可以自动 mutate Pods,为其添加必要的安全上下文配置。

更多信息请参见 场景 4:指定 Namespace 安全上下文强制执行

这种方式无需为每个 Task 手动配置安全上下文,从而简化合规管理。

有关为此目的配置 Kyverno 策略的详细指南,请参见:

故障排除

内置 Tasks 与 Restricted 标准

在平台提供的内置 Tasks 中,只有 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. 在单独的 namespace 中使用 buildah Task,并将其设置为 baseline 模式,而不是 restricted 模式(例如,pod-security.kubernetes.io/enforce=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. Task securityContext 中指定用户:

    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. namespace 具有适当的安全策略
  2. Tekton 使用的容器镜像已配置为以非 root 身份运行
  3. Tekton 安装版本是最新的

了解更多