容器服务 Kubernetes 版中,您可以通过限制容器以特权模式运行、限制应用程序进程以root身份运行以及禁用Service Account令牌自动挂载等方式,防止容器中运行的进程逃离容器的隔离边界并获得对宿主机的访问权限。通过正确配置Pod安全策略以加固集群的安全性,防止集群成为攻击者利用的目标。

防止进程逃离容器边界并获得权限

作为使用Kubernetes的开发或者运维人员,您需要重点关注如何防止在容器中运行的进程逃离容器的隔离边界并获得对宿主机的访问权限。这样做主要有两个原因:

  • 首先,容器内运行的进程默认在[Linux] root用户的上下文中运行。尽管root在容器中的操作部分受到Docker分配给容器的Linux capabilities的限制,但这些默认权限可能允许攻击者提权或者访问到宿主机的敏感信息,包括SecretsConfigMap等敏感资源。下面是分配给Docker容器的默认capabilities列表。更多信息,请参见capabilities(7) — Linux manual page

    cap_chown, cap_dac_override, cap_fowner, cap_fsetid, cap_kill, cap_setgid, cap_setuid, cap_setpcap, cap_net_bind_service, cap_net_raw, cap_sys_chroot, cap_mknod, cap_audit_write, cap_setfcap.

    应尽可能避免使用以特权身份(privileged)运行Pod,因为其拥有与宿主机上root关联的所有Linuxcapabilities

  • 其次,所有Kubernetes工作节点都使用一种称为节点授权者的授权模式。节点授权者授权所有源自Kubelet的API请求,并允许节点执行以下操作:

    读操作:

    • Services

    • Endpoints

    • Nodes

    • Pods

    • 与绑定到Kubelet节点的Pod相关的Secrets、Configmaps、PV和PVC

    写操作:

    • 节点和节点状态(启用NodeRestriction准入插件以限制Kubelet修改自己的节点)

    • Pods和Pod状态(启用NodeRestriction准入插件以限制Kubelet修改绑定到自身的Pod)

    • Events

    Auth相关操作:

    • 对用于TLS引导的CertificateSigningRequest (CSR) API的读/写访问权限

    • 能够为委托的身份认证/授权检查创建TokenReviewSubjectAccessReview

ACK集群默认使用节点限制准入控制器。该控制器仅允许节点修改绑定到节点的一组有限节点属性和Pod对象,但是设法访问主机的攻击者仍然能够从Kubernetes API搜集环境中的敏感信息。更多信息,请参见节点限制准入控制器

Pod安全配置建议

  • 限制容器以特权模式运行

    如前所述,以特权身份运行的容器继承了分配给主机上root的所有Linux capabilities。大多数场景下,容器并不是必须拥有这些权限才能保证业务运行。您可以通过创建Pod安全策略来拒绝容器配置为以特权模式运行的Pod。您可以将Pod安全策略视为Pod在创建之前必须满足的一组安全约束。ACK提供了基于OPA和gatekeeper的容器安全策略能力,可以验证在集群上创建和更新Pod的请求,该请求基于用户配置的安全规则。如果创建或更新Pod的请求不符合定义的规则,系统将拒绝该请求并返回错误。同样,您可以通过部署ACKPSPPrivilegedContainer策略限制,在集群指定范围的命名空间中部署privileged特权容器。

  • 限制应用程序进程以root身份运行

    默认情况下,容器都以root身份运行。如果攻击者能够利用应用程序中的漏洞并获得正在运行的容器的Shell访问权限,这可能会出现安全问题。您可以通过多种方式缓解此类风险。一种方式是,通过从容器镜像中删除Shell。另一种方式是,将USER指令添加到您的Dockerfile或以非root用户身份在Pod中运行容器。Kubernetes podSpec在spec.securityContext下包含runAsUserrunAsGroup两个字段,允许您指定运行应用程序的用户和组。您可以通过创建ACKPSPAllowedUsers策略来限制。

  • 禁止以Docker in Docker的方式运行容器或者在容器中挂载Docker.sock

    使用嵌套容器或者挂载Docker.sock的方式可以方便地在Docker容器中构建/运行容器镜像,但您将节点的控制权交给了在容器中运行的进程。关于在Kubernetes上构建容器镜像,请参见使用企业版实例构建镜像Kanikoimg

  • 限制使用HostPath,如果需要使用HostPath,限制只可以挂载指定前缀的目录并将卷配置为只读

    使用HostPath可以直接将宿主机的目录挂载到容器中。很少有业务场景的Pod用到此功能特性。但如果确实有业务需要,您需要了解其中的风险。默认情况下,以root身份运行的Pod将拥有对HostPath暴露的文件系统的写访问权限。这可能允许攻击者修改Kubelet设置,创建指向未直接通过HostPath暴露的目录或文件的符号链接,例如/etc/shadow、安装Ssh密钥、读取挂载到主机的密钥以及进行恶意操作。为了降低使用HostPath的风险,请将spec.containers.volumeMounts 配置为只读,例如:

    volumeMounts:
    - name: hostPath-volume
        readOnly: true
        mountPath: /host-path

    同样,您可以通过部署ACKPSPHostFilesystem策略实例,限制在集群指定命名空间范围内部署的Pod允许挂载的主机Host目录范围。

  • 为每个容器设置请求和资源限制,避免资源争夺或DoS攻击

    没有请求或资源限制的Pod理论上可以消耗掉主机上的所有可用资源。当有Pod被调度到此节点上时,该节点可能会遭遇CPU或内存不足的情况,这可能导致Kubelet崩溃或从节点驱逐Pod。虽然无法完全避免这种情况的发生,但设置请求和资源限制将有助于最大程度地减少资源争夺,并降低应用程序编写不当导致资源消耗过多所带来的风险。

    PodSpec允许您限制CPU和内存的使用。您可以通过在命名空间上设置Resource Quota或创建Limit Range来强制对请求和资源进行限制。资源配额允许您指定分配给命名空间的资源总量,例如CPU和RAM。当应用于命名空间时,会强制您为部署到该命名空间中的所有容器指定请求和资源的限制。相比之下,限制范围可让您更精细地控制资源分配。通过限制范围,您可以为命名空间内的每个Pod或每个容器的CPU和内存资源设置最小值/最大值。如果没有提供,您还可以设置默认请求/限制值。更多信息,请参见Managing Resources for Containers

    同样,您可以通过部署ACKContainerLimits策略实例,要求在集群指定命名空间范围内部署的应用Pod必须配置的资源限制。

  • 禁止使用特权提升配置

    特权提升允许进程更改其运行所在的安全上下文。例如sudo,带有SUIDSGID位的二进制文件也是如此。特权升级基本上是用户以另一个用户或组的权限执行文件的一种方式。您可以将allowPriviledgedEscalation设置为falsepod安全策略或通过在podSpec中设置securityContext.allowPrivilegedEscalation来阻止容器进行特权提升。

    同样,您可以通过部署ACKPSPAllowPrivilegeEscalationContainer策略实例,强制要求在集群指定命名空间范围内部署的Pod配置allowPrivilegeEscalation参数。

  • 禁用Service Account令牌自动挂载

    对于不需要访问Kubernetes API的Pod,您可以在PodSpec上禁止自动挂载ServiceAccount令牌,或禁用所有使用特定ServiceAccount的Pod。

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-no-automount
    spec:
      automountServiceAccountToken: false

    禁用ServiceAccount自动挂载不会阻止Pod对Kubernetes API的网络访问。为阻止Pod对Kubernetes API进行网络访问,您需要修改ACK集群Endpoint访问并使用网络策略Network Policy来阻止Pod对Kubernetes API进行网络访问。具体操作,请参见在ACK集群使用网络策略

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: sa-no-automount
    automountServiceAccountToken: false

    同样,您可以通过部署ACKBlockAutomountToken策略实例,要求在应用Pod中设置automountServiceAccountToken: false字段,以防止自动挂载Service Account。

  • 禁用服务发现

    对于不需要查找或调用集群服务的Pod,您可以减少提供给Pod的信息量。您可以将Pod的DNS策略设置为不使用CoreDNS,并且不将命名空间中的Service暴露为Pod中的环境变量。更多信息,请参见Environment variables

    Pod的DNS策略的默认值是ClusterFirst,使用集群内DNS,而非默认值Default使用底层节点的DNS解析。更多信息,请参见Kubernetes docs on Pod DNS policy

    禁用服务链接和更改Pod的DNS策略不会阻止Pod对集群内DNS服务进行网络访问。攻击者仍然可以通过访问集群内DNS服务来枚举集群中的服务(例如:dig SRV *.*.svc.cluster.local @$CLUSTER_DNS_IP)。关于阻止集群内服务发现,请参见在ACK集群使用网络策略

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-no-service-info
    spec:
        dnsPolicy: Default # “默认值”不是真正的默认值。
        enableServiceLinks: false
  • 配置镜像为只读文件系统

    将您的镜像配置为只读文件系统可防止攻击者覆盖您的应用程序使用的文件系统上的文件。如果您的应用程序必须写入文件系统,请考虑写入临时目录或挂载附加卷。您可以通过如下设置Pod的SecurityContext来强制执行此操作:

    ...
    securityContext:
      readOnlyRootFilesystem: true
    ...

    同样,您可以通过部署ACKPSPReadOnlyRootFilesystem策略实例,限制在集群指定命名空间范围内部署的Pod只能使用只读的根文件系统。