本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

Kubernetes 1.23:Pod 安全性进阶至 Beta

随着 Kubernetes v1.23 的发布,Pod 安全性准入现已进入 Beta 阶段。Pod 安全性是一个内置准入控制器,它根据一组预定义的Pod 安全性标准评估 Pod 规范,并确定是准入还是拒绝 Pod 运行。

Pod 安全性是PodSecurityPolicy的继任者,PodSecurityPolicy 在 v1.21 版本中已弃用,并将于 Kubernetes v1.25 中移除。在本文中,我们将介绍 Pod 安全性的关键概念以及如何使用它。我们希望集群管理员和开发人员都能使用这种新机制为其工作负载强制执行安全的默认设置。

为何选择 Pod 安全性

Pod 安全性的总体目标是让您能够隔离工作负载。您可以运行一个运行不同工作负载的集群,并且无需添加额外的第三方工具,即可实施控制,要求工作负载的 Pod 将其自身权限限制在一个定义的边界集合内。

Pod 安全性克服了 Kubernetes 现有但已弃用的 PodSecurityPolicy (PSP) 机制的关键缺点

  • 策略授权模型 — 部署控制器时面临挑战。
  • 切换风险 — 缺乏干运行/审计功能使得启用 PodSecurityPolicy 变得困难。
  • 不一致且无边界的 API — 庞大的配置表面和不断演变的约束导致 API 复杂且令人困惑。

PSP 的缺点使得其使用非常困难,这促使社区重新评估是否能通过更好的实现来达到相同的目标。其中一个目标是提供开箱即用的解决方案来应用安全最佳实践。Pod 安全性附带预定义的 Pod 安全级别,集群管理员可以配置这些级别以满足所需的安全性态势。

重要的是,Pod 安全性与已弃用的 PodSecurityPolicy 并非完全功能对等。具体来说,它不具备变异或更改 Kubernetes 资源以代表用户自动修复策略违反的能力。此外,它不提供对 Pod 规范或您可能希望评估的任何其他 Kubernetes 资源中每个允许的字段和值的细粒度控制。如果您需要更细粒度的策略控制,请查看支持此类用例的其他项目。

Pod 安全性还遵循 Kubernetes 声明式对象管理的最佳实践,即拒绝违反策略的资源。这要求在部署到 Kubernetes 之前更新源存储库中的资源和工具。

Pod 安全性如何工作?

Pod 安全性是 Kubernetes v1.22 及更高版本中内置的准入控制器,但也可以作为独立的Webhook运行。准入控制器通过在 Kubernetes API 服务器将请求持久化到存储之前拦截请求来发挥作用。它们可以准入拒绝请求。对于 Pod 安全性,Pod 规范将根据以 Pod 安全性标准形式配置的策略进行评估。这意味着 Pod 规范中的安全敏感字段将只允许具有特定值。

配置 Pod 安全性

Pod 安全性标准

为了使用 Pod 安全性,我们首先需要了解Pod 安全性标准。这些标准定义了三种不同的策略级别,范围从宽松到严格。这些级别如下:

  • privileged — 开放且不受限制
  • baseline — 涵盖已知的特权升级,同时最大限度地减少限制
  • restricted — 高度受限,防范已知和未知的特权升级。可能会导致兼容性问题

这些策略级别中的每一个都定义了 Pod 规范中受限制的字段和允许的值。这些策略限制的一些字段包括

  • spec.securityContext.sysctls
  • spec.hostNetwork
  • spec.volumes[*].hostPath
  • spec.containers[*].securityContext.privileged

策略级别通过对 Namespace 资源应用标签来实施,从而实现细粒度的按命名空间策略选择。API 服务器中的 AdmissionConfiguration 也可以配置为设置集群范围的默认级别和豁免。

策略模式

策略以特定模式应用。可以在同一个命名空间上设置多个模式(具有不同的策略级别)。以下是模式列表:

  • enforce — 任何违反策略的 Pod 都将被拒绝
  • audit — 违规行为将作为注释记录在审计日志中,但不会影响 Pod 是否被允许。
  • warn — 违规行为将向用户发送警告消息,但不会影响 Pod 是否被允许。

除了模式之外,您还可以将策略固定到特定版本(例如 v1.22)。固定到特定版本允许行为保持一致,即使策略定义在未来的 Kubernetes 版本中发生变化。

动手演示

先决条件

部署 kind 集群

kind create cluster --image kindest/node:v1.23.0

启动可能需要一段时间,启动后节点准备就绪可能还需要一分钟左右。

kubectl cluster-info --context kind-kind

等待节点状态变为就绪。

kubectl get nodes

输出类似于此

NAME                 STATUS   ROLES                  AGE   VERSION
kind-control-plane   Ready    control-plane,master   54m   v1.23.0

确认 Pod 安全性已启用

确认 API 默认启用插件的最佳方法是检查 Kubernetes API 容器的帮助参数。

kubectl -n kube-system exec kube-apiserver-kind-control-plane -it -- kube-apiserver -h | grep "default enabled ones"

输出类似于此

...
      --enable-admission-plugins strings
admission plugins that should be enabled in addition
to default enabled ones (NamespaceLifecycle, LimitRanger,
ServiceAccount, TaintNodesByCondition, PodSecurity, Priority,
DefaultTolerationSeconds, DefaultStorageClass,
StorageObjectInUseProtection, PersistentVolumeClaimResize,
RuntimeClass, CertificateApproval, CertificateSigning,
CertificateSubjectRestriction, DefaultIngressClass,
MutatingAdmissionWebhook, ValidatingAdmissionWebhook,
ResourceQuota).
...

PodSecurity 列在默认启用的准入插件组中。

如果使用云提供商,或者如果无法访问 API 服务器,最好的检查方法是运行一个快速端到端测试

kubectl create namespace verify-pod-security
kubectl label namespace verify-pod-security pod-security.kubernetes.io/enforce=restricted
# The following command does NOT create a workload (--dry-run=server)
kubectl -n verify-pod-security run test --dry-run=server --image=busybox --privileged
kubectl delete namespace verify-pod-security

输出类似于此

Error from server (Forbidden): pods "test" is forbidden: violates PodSecurity "restricted:latest": privileged (container "test" must not set securityContext.privileged=true), 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), seccompProfile (pod or container "test" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")

配置 Pod 安全性

策略通过标签应用于命名空间。这些标签如下:

  • pod-security.kubernetes.io/<MODE>: <LEVEL> (启用 pod 安全性所必需)
  • pod-security.kubernetes.io/<MODE>-version: <VERSION> (可选,默认为最新)

可以为每个强制模式提供特定版本。该版本将策略固定到随 Kubernetes 版本发布的版本。固定到特定 Kubernetes 版本可以实现确定性的策略行为,同时允许未来 Pod 安全性标准的灵活更新。可能的 enforceauditwarn

何时使用 warn

warn 的典型用途是为未来您想要强制执行不同策略的更改做准备。最常见的两种情况是

  • 以相同级别但不同版本warn(例如,将enforce固定到 restricted+v1.23,将warn设置为 restricted+latest
  • 以更严格的级别warn(例如,enforce baseline,warn restricted)

不建议将 warn 用于与 enforce 完全相同的策略级别+版本。在准入序列中,如果 enforce 失败,则整个序列在评估 warn 之前失败。

首先,如果之前未创建,请创建一个名为 verify-pod-security 的命名空间。对于演示,在标记时使用 --overwrite 以允许将单个命名空间用于多个示例。

kubectl create namespace verify-pod-security

部署演示工作负载

每个工作负载都代表更高安全级别,这些级别无法通过其后的配置文件。

对于以下示例,busybox 容器运行 sleep 命令 100 万秒(约 11 天)或直到被删除。Pod 安全性不关心您选择哪个容器镜像,而更关心 Pod 级别的设置及其对安全性的影响。

特权级别和工作负载

对于特权 Pod,使用特权策略。这允许容器内的进程获取新进程(也称为“特权升级”),如果不受信任,可能会很危险。

首先,我们为测试应用受限的 Pod 安全级别。

# enforces a "restricted" security policy and audits on restricted
kubectl label --overwrite ns verify-pod-security \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/audit=restricted

接下来,尝试在该命名空间中部署一个特权工作负载。

cat <<EOF | kubectl -n verify-pod-security apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-privileged
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      allowPrivilegeEscalation: true
EOF

输出类似于此

Error from server (Forbidden): error when creating "STDIN": pods "busybox-privileged" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "busybox" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "busybox" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "busybox" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "busybox" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")

现在让我们应用特权 Pod 安全级别并再试一次。

# enforces a "privileged" security policy and warns / audits on baseline
kubectl label --overwrite ns verify-pod-security \
  pod-security.kubernetes.io/enforce=privileged \
  pod-security.kubernetes.io/warn=baseline \
  pod-security.kubernetes.io/audit=baseline
cat <<EOF | kubectl -n verify-pod-security apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-privileged
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      allowPrivilegeEscalation: true
EOF

输出类似于此

pod/busybox-privileged created

我们可以运行 kubectl -n verify-pod-security get pods 来验证它是否正在运行。使用以下命令清理:

kubectl -n verify-pod-security delete pod busybox-privileged

基线级别和工作负载

基线策略演示了合理的默认值,同时防止了常见的容器漏洞利用。

让我们回到受限的 Pod 安全级别进行快速测试。

# enforces a "restricted" security policy and audits on restricted
kubectl label --overwrite ns verify-pod-security \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/audit=restricted

应用工作负载。

cat <<EOF | kubectl -n verify-pod-security apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-baseline
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        add:
          - NET_BIND_SERVICE
          - CHOWN
EOF

输出类似于此

Error from server (Forbidden): error when creating "STDIN": pods "busybox-baseline" is forbidden: violates PodSecurity "restricted:latest": unrestricted capabilities (container "busybox" must set securityContext.capabilities.drop=["ALL"]; container "busybox" must not include "CHOWN" in securityContext.capabilities.add), runAsNonRoot != true (pod or container "busybox" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "busybox" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")

让我们再次应用基线 Pod 安全级别并重试。

# enforces a "baseline" security policy and warns / audits on restricted
kubectl label --overwrite ns verify-pod-security \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/warn=restricted \
  pod-security.kubernetes.io/audit=restricted
cat <<EOF | kubectl -n verify-pod-security apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-baseline
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        add:
          - NET_BIND_SERVICE
          - CHOWN
EOF

输出类似于以下内容。请注意,警告与上述测试中的错误消息匹配,但 Pod 仍然成功创建。

Warning: would violate PodSecurity "restricted:latest": unrestricted capabilities (container "busybox" must set securityContext.capabilities.drop=["ALL"]; container "busybox" must not include "CHOWN" in securityContext.capabilities.add), runAsNonRoot != true (pod or container "busybox" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "busybox" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/busybox-baseline created

请记住,我们根据受限配置文件将 verify-pod-security 命名空间设置为 warn。我们可以运行 kubectl -n verify-pod-security get pods 来验证它是否正在运行。使用以下命令进行清理:

kubectl -n verify-pod-security delete pod busybox-baseline

受限级别和工作负载

受限策略要求拒绝所有特权参数。它是最安全的,但权衡是复杂性。受限策略仅允许容器添加 NET_BIND_SERVICE 功能。

虽然我们已经测试了将受限作为阻止功能,但让我们尝试运行一个符合所有条件的功能。

首先,我们需要重新应用受限配置文件,这是最后一次。

# enforces a "restricted" security policy and audits on restricted
kubectl label --overwrite ns verify-pod-security \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/audit=restricted
cat <<EOF | kubectl -n verify-pod-security apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-restricted
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        add:
          - NET_BIND_SERVICE
EOF

输出类似于此

Error from server (Forbidden): error when creating "STDIN": pods "busybox-restricted" is forbidden: violates PodSecurity "restricted:latest": unrestricted capabilities (container "busybox" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "busybox" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "busybox" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")

这是因为受限配置文件明确要求将某些值设置为最安全的参数。

通过要求明确的值,清单变得更具声明性,并且您的整个安全模型可以向左移动。通过 restricted 强制级别,公司可以根据允许的清单审计其集群的合规性。

让我们修复每个警告,生成以下文件

cat <<EOF | kubectl -n verify-pod-security apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-restricted
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      seccompProfile:
        type: RuntimeDefault
      runAsNonRoot: true
      allowPrivilegeEscalation: false
      capabilities:
        drop:
          - ALL
        add:
          - NET_BIND_SERVICE
EOF

输出类似于此

pod/busybox-restricted created

运行 kubectl -n verify-pod-security get pods 来验证它是否正在运行。输出类似于此

NAME               READY   STATUS                       RESTARTS   AGE
busybox-restricted   0/1     CreateContainerConfigError   0          2m26s

让我们通过 kubectl -n verify-pod-security describe pod busybox-restricted 找出容器未启动的原因。输出类似于此:

Events:
  Type     Reason     Age                    From               Message
  ----     ------     ----                   ----               -------
  Warning  Failed     2m29s (x8 over 3m55s)  kubelet            Error: container has runAsNonRoot and image will run as root (pod: "busybox-restricted_verify-pod-security(a4c6a62d-2166-41a9-b288-20df17cf5c90)", container: busybox)

要解决此问题,请将有效 UID (runAsUser) 设置为非零(root)值,或使用 nobody UID (65534)。

# delete the original pod
kubectl -n verify-pod-security delete pod busybox-restricted

# create the pod again with new runAsUser
cat <<EOF | kubectl -n verify-pod-security apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-restricted
spec:
  securityContext:
    runAsUser: 65534
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      seccompProfile:
        type: RuntimeDefault
      runAsNonRoot: true
      allowPrivilegeEscalation: false
      capabilities:
        drop:
          - ALL
        add:
          - NET_BIND_SERVICE
EOF

运行 kubectl -n verify-pod-security get pods 来验证它是否正在运行。输出类似于此

NAME                 READY   STATUS    RESTARTS   AGE
busybox-restricted   1/1     Running   0          25s

使用以下命令清除演示(受限 Pod 和命名空间):

kubectl delete namespace verify-pod-security

此时,如果您想深入了解 Linux 权限或某个容器允许哪些权限,可以执行到控制平面并使用 containerdcrictl inspect 进行操作。

# if using docker, shell into the control plane
docker exec -it kind-control-plane bash

# list running containers
crictl ps

# inspect each one by container ID
crictl inspect <CONTAINER ID>

应用集群范围策略

除了通过标签将策略应用于命名空间之外,您还可以使用 AdmissionConfiguration 资源配置集群范围的策略和豁免。

使用此资源,策略定义默认在集群范围内应用,并且通过命名空间标签应用的任何策略都将优先。

对于 AdmissionConfiguration 配置文件,没有运行时可配置的 API,因此集群管理员需要在 API 服务器上通过 --admission-control-config-file 标志指定文件的路径。

在以下资源中,我们正在强制执行基线策略,并对基线策略进行警告和审计。我们还将 kube-system 命名空间从该策略中豁免。

不建议在安装后更改控制平面/集群,因此让我们构建一个新集群,其中所有命名空间都具有默认策略。

首先,删除当前集群。

kind delete cluster

创建一个 Pod 安全性配置,它强制审计基线策略,同时使用受限配置文件警告最终用户。

cat <<EOF > pod-security.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1beta1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "baseline"
      enforce-version: "latest"
      audit: "baseline"
      audit-version: "latest"
      warn: "restricted"
      warn-version: "latest"
    exemptions:
      # Array of authenticated usernames to exempt.
      usernames: []
      # Array of runtime class names to exempt.
      runtimeClasses: []
      # Array of namespaces to exempt.
      namespaces: [kube-system]
EOF

有关其他选项,请查阅官方的标准准入控制器文档。

我们现在有了一个默认的基线策略。接下来,将其传递给 kind 配置,以启用 --admission-control-config-file API 服务器参数并传递策略文件。要将文件传递给 kind 集群,请使用配置文件传递额外的设置指令。Kind 使用 kubeadm 来配置集群,并且配置文件能够传递 kubeadmConfigPatches 以进行进一步自定义。在我们的示例中,本地文件作为 /etc/kubernetes/policies/pod-security.yaml 挂载到控制平面节点中,然后挂载到 apiServer 容器中。我们还传递了指向策略位置的 --admission-control-config-file 参数。

cat <<EOF > kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
        # enable admission-control-config flag on the API server
        extraArgs:
          admission-control-config-file: /etc/kubernetes/policies/pod-security.yaml
        # mount new file / directories on the control plane
        extraVolumes:
          - name: policies
            hostPath: /etc/kubernetes/policies
            mountPath: /etc/kubernetes/policies
            readOnly: true
            pathType: "DirectoryOrCreate"
  # mount the local file on the control plane
  extraMounts:
  - hostPath: ./pod-security.yaml
    containerPath: /etc/kubernetes/policies/pod-security.yaml
    readOnly: true
EOF

使用上面定义的 kind 配置文件创建新集群。

kind create cluster --image kindest/node:v1.23.0 --config kind-config.yaml

让我们看看默认命名空间。

kubectl describe namespace default

输出类似于此

Name:         default
Labels:       kubernetes.io/metadata.name=default
Annotations:  <none>
Status:       Active

No resource quota.

No LimitRange resource.

让我们创建一个新的命名空间,看看标签是否也适用于那里。

kubectl create namespace test-defaults
kubectl describe namespace test-defaults

相同。

Name:         test-defaults
Labels:       kubernetes.io/metadata.name=test-defaults
Annotations:  <none>
Status:       Active

No resource quota.

No LimitRange resource.

是否可以部署特权工作负载?

cat <<EOF | kubectl -n test-defaults apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-privileged
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
    securityContext:
      allowPrivilegeEscalation: true
EOF

嗯……是的。至少默认的warn级别正在工作。

Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "busybox" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "busybox" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "busybox" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "busybox" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/busybox-privileged created

让我们使用 kubectl -n test-defaults delete pod/busybox-privileged 删除 Pod。

我的配置是否正常工作?

# if using docker, shell into the control plane
docker exec -it kind-control-plane bash

# cat out the file we mounted
cat /etc/kubernetes/policies/pod-security.yaml

# check the api server logs
cat /var/log/containers/kube-apiserver*.log 

# check the api server config
cat /etc/kubernetes/manifests/kube-apiserver.yaml 

更新:基线策略允许 allowPrivilegeEscalation。虽然我无法看到 Pod 安全性的默认强制级别,但它们确实存在。让我们尝试提供一个通过请求 hostNetwork 访问来违反基线的清单。

# delete the original pod
kubectl -n test-defaults delete pod busybox-privileged

cat <<EOF | kubectl -n test-defaults apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-privileged
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
  hostNetwork: true
EOF

输出类似于此

Error from server (Forbidden): error when creating "STDIN": pods "busybox-privileged" is forbidden: violates PodSecurity "baseline:latest": host namespaces (hostNetwork=true)

成功了!!!🎉🎉🎉

后来我发现,另一种检查事物是否按预期运行的方法是检查原始 API 服务器指标端点。

运行以下命令

kubectl get --raw /metrics | grep pod_security_evaluations_total

输出类似于此

# HELP pod_security_evaluations_total [ALPHA] Number of policy evaluations that occurred, not counting ignored or exempt requests.
# TYPE pod_security_evaluations_total counter
pod_security_evaluations_total{decision="allow",mode="enforce",policy_level="baseline",policy_version="latest",request_operation="create",resource="pod",subresource=""} 2
pod_security_evaluations_total{decision="allow",mode="enforce",policy_level="privileged",policy_version="latest",request_operation="create",resource="pod",subresource=""} 0
pod_security_evaluations_total{decision="allow",mode="enforce",policy_level="privileged",policy_version="latest",request_operation="update",resource="pod",subresource=""} 0
pod_security_evaluations_total{decision="deny",mode="audit",policy_level="baseline",policy_version="latest",request_operation="create",resource="pod",subresource=""} 1
pod_security_evaluations_total{decision="deny",mode="enforce",policy_level="baseline",policy_version="latest",request_operation="create",resource="pod",subresource=""} 1
pod_security_evaluations_total{decision="deny",mode="warn",policy_level="restricted",policy_version="latest",request_operation="create",resource="controller",subresource=""} 2
pod_security_evaluations_total{decision="deny",mode="warn",policy_level="restricted",policy_version="latest",request_operation="create",resource="pod",subresource=""} 2

监控工具也可以摄取这些指标,用于报告、评估或测量趋势。

清理

完成后,删除 kind 集群。

kind delete cluster

审计

审计是另一种跟踪集群中正在强制执行的策略的方式。要使用 kind 设置审计,请查阅启用审计的官方文档。从版本 1.11开始,Kubernetes 审计日志包含两个注释,指示请求是否已授权 (authorization.k8s.io/decision) 以及决策的原因 (authorization.k8s.io/reason)。审计事件可以流式传输到 Webhook 以进行监控、跟踪或警报。

审计事件看起来类似于以下内容

{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"","pod-security.kubernetes.io/audit":"allowPrivilegeEscalation != false (container \"busybox\" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container \"busybox\" must set securityContext.capabilities.drop=[\"ALL\"]), runAsNonRoot != true (pod or container \"busybox\" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container \"busybox\" must set securityContext.seccompProfile.type to \"RuntimeDefault\" or \"Localhost\")"}}

审计也是评估集群当前 Pod 安全合规性的良好第一步。Kubernetes 增强提案 (KEP) 暗示未来 baseline 可能成为未标记命名空间的默认设置

针对 Pod 安全事件调整的 audit-policy.yaml 配置示例

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
  resources:
    - group: "" # core API group
      resources: ["pods", "pods/ephemeralcontainers", "podtemplates", "replicationcontrollers"]
    - group: "apps"
      resources: ["daemonsets", "deployments", "replicasets", "statefulsets"]
    - group: "batch"
      resources: ["cronjobs", "jobs"]
  verbs: ["create", "update"]
  omitStages:
    - "RequestReceived"
    - "ResponseStarted"
    - "Panic"

启用审计后,如果使用 --audit-log-path,请查看配置的本地文件,如果使用 --audit-webhook-config-file,则查看 Webhook 的目的地。

如果使用文件(--audit-log-path),运行 cat /PATH/TO/API/AUDIT.log | grep "is forbidden:" 查看所有被审计的被拒绝工作负载。

PSP 迁移

如果您已经在使用 PSP,SIG Auth 已创建指南并发布了从 PSP 迁移的步骤

总结过程如下:

  • 更新所有现有 PSP 为非变异
  • warnaudit 模式应用 Pod 安全策略
  • 将 Pod 安全策略升级到 enforce 模式
  • --enable-admission-plugins 中移除 PodSecurityPolicy

SIG Auth 已经讨论过提供一个工具来协助迁移的想法,该工具被列为“可选的未来扩展”且目前不在范围之内。KEP 中有更多详细信息

总结

Pod 安全性是一个很有前途的新功能,它提供了一种开箱即用的方式,让用户可以提高其工作负载的安全态势。与任何已成熟到 Beta 阶段的新增强功能一样,我们恳请您试用它,提供反馈,或通过提出 Github 问题或参加 SIG Auth 社区会议来分享您的经验。我们希望 Pod 安全性能够部署在每个集群上,作为我们社区持续追求将 Kubernetes 安全性作为优先事项的一部分。

有关如何通过 Pod 安全性准入功能启用“基线”Pod 安全性标准的逐步指南,请参阅这些专门的教程,其中涵盖了集群级别和命名空间级别所需的配置。

其他资源