从 OpenShift 迁移到 GKE Enterprise:将 OpenShift 安全上下文限制条件迁移到 GKE Enterprise

Last reviewed 2022-01-24 UTC

本文档可帮助您计划将安全政策从源 OpenShift 集群上定义的 OpenShift 安全上下文限制条件 (SCC) 迁移到目标 GKE 集群。该实现使用 Policy Controller 限制条件定义目标集群上已迁移的政策。

本文档假定您熟悉将容器迁移到 Google Cloud:从 OpenShift 迁移到 GKE Enterprise。本文假定您熟悉 OpenShift 和安全上下文限制条件,并且您有权访问源 OpenShift 集群和目标 GKE 集群。

本文档是关于迁移到 Google Cloud 的系列文章中的一篇。如需简要了解本系列,请参阅迁移到 Google Cloud:选择迁移路径

本文档是一系列文章中的一篇,介绍了如何将容器迁移到 Google Cloud:

如果您计划将 OpenShift SCC 迁移到 GKE Enterprise,则本文档非常有用。如果您正在评估迁移的时机并希望了解潜在的情况,本文档也可以提供帮助。

本文档涉及迁移到 Google Cloud:使用入门将容器迁移到 Google Cloud:将 Kubernetes 迁移到 GKE将容器迁移到 Google Cloud:从 OpenShift 迁移到 GKE EnterpriseGKE 网络的最佳实践中介绍的概念。其中包含指向这些文档的链接(如适用)。

OpenShift SCC

SCC 是特定于 OpenShift 的资源,用于为 Pod 定义策略,这些策略指定 Pod 可以执行的操作以及可在节点上访问的资源。当您发出 API 请求来创建 Pod 时,SCC 会根据通过 SCC 定义的一组政策按照进程权限评估 Pod 请求。SCC 会评估请求,以根据配置的政策允许或禁止执行 Pod。如需详细了解 OpenShift SCC,请参阅管理安全上下文限制条件

默认 OpenShift SCC

OpenShift 4.x 集群包含由 Red Hat 发布的在 OpenShift 中管理 SCC 博文中说明的一组默认 SCC。

Config Sync 和 Policy Controller

本部分介绍了 Config Sync 和 Policy Controller。本部分提供相关文档和指导的链接,用于设置 Policy Controller 以执行本文档后面所述的迁移任务。

Config Sync

借助 Config Sync,您可以使用符合 Git 标准的通用代码库来集中定义任何资源(适用于任何 GKE Enterprise 管理的 Kubernetes 集群)的配置。您可以将该配置应用到多个集群。

Policy Controller

Policy Controller 是一种 Kubernetes 动态准入控制器,根据集中定义的政策来检查、审核和实施集群的合规性。Policy Controller 基于 Open Policy Agent (OPA) Gatekeeper 开源项目。

Config Sync 和 Policy Controller 设置

如需准备实施镜像 OpenShift SCC 的安全政策,请为每个目标集群启用 Config SyncPolicy Controller 组件。本部分介绍了如何设置这些组件。本文档后面的迁移 OpenShift SCC 部分介绍了如何使用 Policy Controller 限制条件模板来实施安全政策。

设置 Policy Controller 时,最好将不运行应用 Pod 的系统相关命名空间放入豁免命名空间字段中。豁免与系统相关的命名空间有助于避免阻止任何需要更高权限的系统 Pod 的风险。GKE 集群上的系统相关命名空间可以包含以下内容:

  • kube-system
  • kube-public
  • gke-connect
  • gke-system
  • config-management-system
  • config-management-monitoring
  • gatekeeper-system
  • istio-system
  • cnrm-system
  • knative-serving
  • monitoring-system

在具有上述异常的情况下设置 Policy Controller 后,通过向每个命名空间添加 admission.gatekeeper.sh/ignore=true 标签,从限制条件应用中排除命名空间。如果您不将标签添加到每个命名空间,则系统 Pod(以及您的整个集群)可能会受到限制政策的影响。

将 OpenShift SCC 迁移到 Policy Controller 限制条件

本部分介绍如何从 OpenShift 集群导出 SCC,以及如何配置目标 GKE Enterprise Policy Controller 限制条件以匹配所需的政策。本部分还介绍了 SCC 和 Policy Controller 限制条件之间的一些差异,以便您可以相应地规划迁移。

评估 OpenShift SCC

如需导出 OpenShift 集群中安装的 SCC 列表和配置,您可以使用以下命令:

  1. 获取所有 SCC 的列表:

    oc get scc
    
  2. 导出每个 SCC 的配置:

    oc get scc SCC_NAME > SCC_NAME.yaml
    

    SCC_NAME 替换为要针对其导出配置的 SCC 的名称。

导出配置后,您可以对其进行分析,然后使用下面的 Map OpenShift SCC 部分中的表来配置与应用安全性要求匹配的 Policy Controller 限制条件。

将 OpenShift SCC 映射到 Policy Controller 限制条件模板

下表提供了与 OpenShift SCC 字段对应的 Policy Controller 限制条件和设置及其可能的值。使用该表来帮助配置与通过源环境中的 OpenShift SCC 实现的应用安全要求相匹配的目标 Policy Controller 限制条件。本文档后面的示例端到端迁移部分提供了如何使用表中的信息的示例。

OpenShift SCC 字段 类型/可能的值 Policy Controller 限制条件模板 Policy Controller 限制条件规范
allowPrivilegedContainer: 布尔值 K8sPSPPrivilegedContainer 阻止特权容器(如果已应用)。
allowHostIPC: 布尔值 K8sPSPHostNamespace 阻止访问主机 pidipc 命名空间(如果已应用)。
allowHostPID: 布尔值 K8sPSPHostNamespace 阻止访问主机 pidipc 命名空间(如果已应用)。
allowHostNetwork: 布尔值 K8sPSPHostNetworkingPorts 具有防止访问主机网络的布尔参数,并且可以定义可访问主机端口的范围。
allowHostPorts: 布尔值 K8sPSPHostNetworkingPorts 具有防止访问主机网络的布尔参数,并且可以定义可访问主机端口的范围。
readOnlyRootFilesystem: 布尔值 K8sPSPReadOnlyRootFilesystem 可让您仅以只读方式装载容器根文件系统(如果已应用)。
allowPrivilegeEscalation: true 布尔值 K8sPSPAllowPrivilegeEscalationContainer 阻止将 AllowPrivilegeEscalation 安全上下文设置为 true 的 Pod(如果已应用)。
allowHostDirVolumePlugin: 布尔值 K8sPSPVolumeTypes 有一个参数用于定义允许的卷类型列表(如 SCC 中所示),包括主机目录。
volumes: 具有允许的卷类型的数组列表 K8sPSPVolumeTypes 有一个参数用于定义允许的卷类型列表(如 SCC 中所示),包括主机目录。
allowedCapabilities: 可以请求的 Linux 功能的数组列表。 K8sPSPCapabilities 具有用于定义可请求 (allowedCapabilities) 和禁止 (requiredDropCapabilites) 的 Linux 功能的参数。

不能像在 OpenShift SCC 的 defaultAddCapabilities:requiredDropCapabilities: 中那样直接添加或删除列出的功能。

defaultAddCapabilities: 必须添加到每个容器的 Linux 功能的数组列表。 K8sPSPCapabilities 具有用于定义可请求 (allowedCapabilities) 和禁止 (requiredDropCapabilites) 的 Linux 功能的参数。

不能像在 OpenShift SCC 的 defaultAddCapabilities:requiredDropCapabilities: 中那样直接添加或删除列出的功能。

requiredDropCapabilities: 从 Pod 或容器中自动删除的 Linux 功能的数组列表。 K8sPSPCapabilities 具有用于定义可请求 (allowedCapabilities) 和禁止 (requiredDropCapabilites) 的 Linux 功能的参数。

不能像在 OpenShift SCC 的 defaultAddCapabilities:requiredDropCapabilities: 中那样直接添加或删除列出的功能。

fsGroup: 有一个 type: key,其可以是以下任一项:
  • MustRunAs:如果不使用预分配值,则至少需要指定一个范围。
  • RunAsAny:允许指定任何 fsGroup ID。
K8sPSPAllowedUsers 允许您定义与 SCC 中的 type: keyrunAsUserrunAsGroupsupplementalGroupsfsGroup 参数的 id 范围具有类似功能的规则。

可用于为用户、群组、补充群组或 FS 群组定义允许的范围,但不能像在 OpenShift SCC 中那样直接在 Pod 中设置用户 ID。

runAsUser: 有一个 type: key,其可以是以下任一项:
  • MustRunAs:需要配置 runAsUser。
  • MustRunAsRange:如果不使用命名空间中的预分配值,则需要定义最小值和最大值。
  • MustRunAsNonRoot:要求使用非零 runAsUser 来提交 Pod 或在映像中定义 USER 指令。
  • RunAsAny:允许指定任何 runAsUser
K8sPSPAllowedUsers 允许您定义与 SCC 中的 type: keyrunAsUserrunAsGroupsupplementalGroupsfsGroup 参数的 id 范围具有类似功能的规则。

可用于为用户、群组、补充群组或 FS 群组定义允许的范围,但不能像在 OpenShift SCC 中那样直接在 Pod 中设置用户 ID。

supplementalGroups: 有一个 type: key,其可以是以下任一项:
  • MustRunAs:如果不使用命名空间中的预分配值,则需要指定至少一个范围
  • RunAsAny:允许指定任何 supplementalGroup。
K8sPSPAllowedUsers 允许您定义与 SCC 中的 type: keyrunAsUserrunAsGroupsupplementalGroupsfsGroup 参数的 id 范围具有类似功能的规则。

可用于为用户、群组、补充群组或 FS 群组定义允许的范围,但不能像在 OpenShift SCC 中那样直接在 Pod 中设置用户 ID。

seLinuxContext: 有一个 type: key,其可以是以下任一项:
  • MustRunAs:如果未使用命名空间中的预分配值,则需要配置 seLinuxOptions
  • RunAsAny:允许指定任何 seLinuxOptions
K8sPSPSELinuxV2 具有 allowedSELinuxOptions 参数,您可以在该参数中设置允许的级别、角色、类型和用户 seLinuxOptions

OpenShift SCC 和 Policy Controller 限制条件之间的差异

本部分介绍 Policy Controller 限制条件和 OpenShift SCC 之间的一些差异。在使用上表部署目标环境的限制条件之前,请考虑这些差异。

如何将限制条件应用于资源

您可以使用 SCC 对象中存在的 users:group: 规范将 OpenShift SCC 分配给用户和群组。借助 OpenShift 4.x 及更高版本,您还可以使用基于角色的访问权限控制 (RBAC) 为用户或群组分配 SCC。SCC 还有一个 priority: 字段,用于订购应用于 Pod 的 SCC。

Policy Controller 限制条件使用限制条件中的特定资源选择器应用于目标集群、命名空间或 pod,而不是针对用户或服务账号。如需了解详情,请参阅 Policy Controller 文档。使用特定资源选择器有助于确保 Pod 的行为相同,无论低权限用户是使用部署工具运行集群还是由集群管理员从命令行启动集群。

Policy Controller 限制条件还支持试运行模式,使您可以在实际强制执行之前测试政策和审核违规行为。 使用此模式可帮助您防止对现有工作负载的影响,而如果适用于用户,则始终强制执行 SCC。

使用 OpenShift 命名空间中预分配的值进行的 SCC Pod 变更

在 OpenShift SCC 中,您可以使用命名空间中注释提供的预分配范围中的特定 ID 更改应用 SCC 的每个 Pod 的相关安全上下文。您可以使用 MustRunAsMustRunAsRange 策略类型的 RunAsUserfsGroupsupplementalGroupsseLinuxContext 字段执行此操作。

例如,假设 restricted SCC 的 RunAsUser 字段的策略类型为 MustRunAsRange,且未在 SCC 中定义。在这种情况下,SCC 应用的每个 Pod 都会从 Pod 命名空间的 openshift.io/sa.scc.uid-range 注释中指定的范围内获得 RunAsUser ID。

Policy Controller 限制条件以及变更功能同时提供了 Pod 验证和变更。但是,这些限制条件不使用命名空间中的注释来提供 Pod 安全上下文的值。下一部分(端到端迁移示例)举例说明了您的应用交付团队必须如何在 Pod 上明确配置要遵守的安全上下文实现与上述限制类似的限制条件。

端到端迁移示例

本部分提供了一个示例目标清单文件,其中包含您在目标 GKE 集群上映射以下 OpenShift 默认 SCC 所需的所有 Policy Controller 限制条件和变更器:

  • privileged
  • anyuid
  • nonroot
  • restricted

根据 Pod 运行的命名空间,Pod 会在您映射来源 OpenShift 环境中定义的 SCC 政策时获得不同的 Policy Controller 限制条件:

  • 需要最高特权访问权限(例如能够在特权模式下运行或访问任何主机资源)的工作负载应在 Policy Controller 配置中定义的某个豁免命名空间中运行。

    系统不会对豁免命名空间应用任何限制条件。具有此最高特权访问权限的工作负载通常是系统组件或特权 OpenShift 环境中已应用特权 SCC 的任何工作负载。

  • 在任何非豁免命名空间中创建的所有 Pod 都将获得最严格的限制条件。这些限制条件拒绝访问所有主机功能,并要求 Pod 使用属于特定范围的 UID 运行。此配置与 OpenShift restricted SCC 应用的政策匹配。此配置的例外情况包括下列各项:

    • 在具有 security=anyuid 标签的命名空间中创建的 Pod 获得上述严格限制条件,但可以使用任何 UID 和任何 GID 运行。这与 OpenShift 上 anyuid SCC 的限制条件相匹配。
    • 在具有 security=nonroot 标签的命名空间中创建的 Pod 获得上述严格限制条件。但是,可以使用任何非根 UID 运行 Pod。这与 OpenShift 上 nonroot SCC 的限制条件相匹配。

目标清单示例

以下是单个清单的示例,其中包含一组与上述端到端迁移示例中描述的行为匹配的 Policy Controller 限制条件和更改器。我们建议您根据组织的需求检查并调整此示例中的限制条件或其范围。

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNamespace
metadata:
  name: psp-host-namespace
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNetworkingPorts
metadata:
  name: psp-host-network-ports
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    hostNetwork: false
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: psp-privileged-container
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
---
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: restricted-capabilities
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  match:
    scope: Namespaced
    kinds:
    - apiGroups: ["*"]
      kinds: ["Pod"]
    namespaceSelector:
      matchExpressions:
        - operator: NotIn
          key: security
          values: ["anyuid"]
  location: "spec.containers[name:*].securityContext.capabilities.drop"
  parameters:
    assign:
      value: ["KILL","MKNOD","SYS_CHROOT"]
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPCapabilities
metadata:
  name: restricted-capabilities
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaceSelector:
      matchExpressions:
        - operator: NotIn
          key: security
          values: ["anyuid"]
  parameters:
    requiredDropCapabilities: ["KILL","MKNOD","SYS_CHROOT"]
---
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: anyuid-capabilities
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  match:
    scope: Namespaced
    kinds:
    - apiGroups: ["*"]
      kinds: ["Pod"]
    namespaceSelector:
      matchExpressions:
        - operator: In
          key: security
          values: ["anyuid"]
  location: "spec.containers[name:*].securityContext.capabilities.drop"
  parameters:
    assign:
      value: ["MKNOD"]
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPCapabilities
metadata:
  name: anyuid-capabilities
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaceSelector:
      matchExpressions:
        - operator: In
          key: security
          values: ["anyuid"]
  parameters:
    requiredDropCapabilities: ["MKNOD"]
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPAllowedUsers
metadata:
  name: restricted-users-and-groups
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaceSelector:
      matchExpressions:
        - operator: NotIn
          key: security
          values: ["anyuid","nonroot"]
  parameters:
    runAsUser:
      rule: MustRunAs # MustRunAsNonRoot # RunAsAny
      ranges:
        - min: 1000
          max: 2000
    fsGroup:
      rule: MustRunAs # MayRunAs # RunAsAny
      ranges:
        - min: 1000
          max: 2000
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPAllowedUsers
metadata:
  name: nonroot-users-and-groups
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaceSelector:
      matchExpressions:
        - operator: In
          key: security
          values: ["nonroot"]
  parameters:
    runAsUser:
      rule: MustRunAsNonRoot
    fsGroup:
      rule: MustRunAsNonRoot
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPVolumeTypes
metadata:
  name: psp-volume-types
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    volumes:
      - configMap
      - downwardAPI
      - emptyDir
      - nfs
      - persistentVolumeClaim
      - projected
      - secret

示例清单中的 restricted-users-and-groups 限制条件使用 K8sPSPAllowedUsers 模板明确设置 runAsUser:fsGroup: 参数的示例范围 1000-2000。任何未设置为使用 runAsUser:fsGroup: 范围内 ID 的 Pod 都会被阻止。

GKE Enterprise 和 Kubernetes 不使用命名空间注释来自动改变具有特定用户或组 ID 的 Pod。因此,要像前面的示例那样限制 UID 范围,您的应用交付团队需要在创建的 pod 上显式设置兼容的 UID,或者您需要完全移除限制条件以允许任何 ID。

以下是符合您在其中创建的任何命名空间中的上述限制条件的 Pod 清单示例(清单符合 restricted SCC):

apiVersion: v1
kind: Pod
metadata:
  name: restricted-pod-example
spec:
  securityContext:
    runAsUser: 1000
    fsGroup: 1100
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo

后续步骤