1 - Pod 安全性标准
Pod 的安全性配置一般通过使用 安全性上下文(Security Context) 来保证。安全性上下文允许用户逐个 Pod 地定义特权级及访问控制。
以前,对集群的安全性上下文的需求的实施及其基于策略的定义都通过使用 Pod 安全性策略来实现。 Pod 安全性策略(Pod Security Policy) 是一种集群层面的资源,控制 Pod 规约中 安全性敏感的部分。
不过,新的策略实施方式不断涌现,或增强或替换 PodSecurityPolicy 的使用。 本页的目的是详细介绍建议实施的 Pod 安全框架;这些内容与具体的实现无关。
策略类型
在进一步讨论整个策略谱系之前,有必要对基本的策略下个定义。 策略可以是很严格的也可以是很宽松的:
- Privileged - 不受限制的策略,提供最大可能范围的权限许可。这些策略 允许已知的特权提升。
- Baseline - 限制性最弱的策略,禁止已知的策略提升。 允许使用默认的(规定最少)Pod 配置。
- Restricted - 限制性非常强的策略,遵循当前的保护 Pod 的最佳实践。
策略
Privileged
Privileged 策略是有目的地开放且完全无限制的策略。此类策略通常针对由 特权较高、受信任的用户所管理的系统级或基础设施级负载。
Privileged 策略定义中限制较少。对于默认允许(Allow-by-default)实施机制(例如 gatekeeper), Privileged 框架可能意味着不应用任何约束而不是实施某策略实例。 与此不同,对于默认拒绝(Deny-by-default)实施机制(如 Pod 安全策略)而言, Privileged 策略应该默认允许所有控制(即,禁止所有限制)。
Baseline
Baseline 策略的目标是便于常见的容器化应用采用,同时禁止已知的特权提升。 此策略针对的是应用运维人员和非关键性应用的开发人员。 下面列举的控制应该被实施(禁止):
控制(Control) | 策略(Policy) |
宿主名字空间 | 必须禁止共享宿主名字空间。 限制的字段: spec.hostNetwork spec.hostPID spec.hostIPC 允许的值: false |
特权容器 | 特权 Pod 禁用大多数安全性机制,必须被禁止。 限制的字段: spec.containers[*].securityContext.privileged spec.initContainers[*].securityContext.privileged 允许的值: false、未定义/nil |
权能 | 必须禁止添加默认集合之外的权能。 限制的字段: spec.containers[*].securityContext.capabilities.add spec.initContainers[*].securityContext.capabilities.add 允许的值: 空(或限定为一个已知列表) |
HostPath 卷 | 必须禁止 HostPath 卷。 限制的字段: spec.volumes[*].hostPath 允许的值: 未定义/nil |
宿主端口 | 应禁止使用宿主端口,或者至少限定为已知列表。 限制的字段: spec.containers[*].ports[*].hostPort spec.initContainers[*].ports[*].hostPort 允许的值: 0、未定义(或限定为已知列表) |
AppArmor | 在被支持的主机上,默认使用 'runtime/default' AppArmor Profile。
基线策略应避免覆盖或者禁用默认策略,以及限制覆盖一些 profile 集合的权限。 限制的字段: metadata.annotations['container.apparmor.security.beta.kubernetes.io/*'] 允许的值: 'runtime/default'、未定义 |
SELinux | 设置 SELinux 类型的操作是被限制的,设置自定义的 SELinux 用户或角色选项是被禁止的。 限制的字段: spec.securityContext.seLinuxOptions.type spec.containers[*].securityContext.seLinuxOptions.type spec.initContainers[*].securityContext.seLinuxOptions.type 允许的值: 未定义/空 container_t container_init_t container_kvm_t 被限制的字段: spec.securityContext.seLinuxOptions.user spec.containers[*].securityContext.seLinuxOptions.user spec.initContainers[*].securityContext.seLinuxOptions.user spec.securityContext.seLinuxOptions.role spec.containers[*].securityContext.seLinuxOptions.role spec.initContainers[*].securityContext.seLinuxOptions.role 允许的值: 未定义或空 |
/proc 挂载类型 | 要求使用默认的 /proc 掩码以减小攻击面。限制的字段: spec.containers[*].securityContext.procMount spec.initContainers[*].securityContext.procMount 允许的值: 未定义/nil、'Default' |
Sysctls | Sysctls 可以禁用安全机制或影响宿主上所有容器,因此除了若干『安全』的子集之外,应该被禁止。
如果某 sysctl 是受容器或 Pod 的名字空间限制,且与节点上其他 Pod 或进程相隔离,可认为是安全的。 限制的字段: spec.securityContext.sysctls 允许的值: kernel.shm_rmid_forced net.ipv4.ip_local_port_range net.ipv4.tcp_syncookies net.ipv4.ping_group_range 未定义/空值 |
Restricted
Restricted 策略旨在实施当前保护 Pod 的最佳实践,尽管这样作可能会牺牲一些兼容性。 该类策略主要针对运维人员和安全性很重要的应用的开发人员,以及不太被信任的用户。 下面列举的控制需要被实施(禁止):
控制(Control) | 策略(Policy) |
基线策略的所有要求。 | |
卷类型 | 除了限制 HostPath 卷之外,此类策略还限制可以通过 PersistentVolumes 定义的非核心卷类型。 限制的字段: spec.volumes[*].hostPath spec.volumes[*].gcePersistentDisk spec.volumes[*].awsElasticBlockStore spec.volumes[*].gitRepo spec.volumes[*].nfs spec.volumes[*].iscsi spec.volumes[*].glusterfs spec.volumes[*].rbd spec.volumes[*].flexVolume spec.volumes[*].cinder spec.volumes[*].cephFS spec.volumes[*].flocker spec.volumes[*].fc spec.volumes[*].azureFile spec.volumes[*].vsphereVolume spec.volumes[*].quobyte spec.volumes[*].azureDisk spec.volumes[*].portworxVolume spec.volumes[*].scaleIO spec.volumes[*].storageos spec.volumes[*].csi 允许的值: 未定义/nil |
特权提升 | 禁止(通过 SetUID 或 SetGID 文件模式)获得特权提升。 限制的字段: spec.containers[*].securityContext.allowPrivilegeEscalation spec.initContainers[*].securityContext.allowPrivilegeEscalation 允许的值: false |
以非 root 账号运行 | 必须要求容器以非 root 用户运行。 限制的字段: spec.securityContext.runAsNonRoot spec.containers[*].securityContext.runAsNonRoot spec.initContainers[*].securityContext.runAsNonRoot 允许的值: true |
非 root 组 (可选) | 禁止容器使用 root 作为主要或辅助 GID 来运行。 限制的字段: spec.securityContext.runAsGroup spec.securityContext.supplementalGroups[*] spec.securityContext.fsGroup spec.containers[*].securityContext.runAsGroup spec.initContainers[*].securityContext.runAsGroup 允许的值: 非零值 未定义/nil ( *.runAsGroup 除外) |
Seccomp | 必须要求使用 RuntimeDefault seccomp profile 或者允许使用特定的 profiles。 限制的字段: spec.securityContext.seccompProfile.type spec.containers[*].securityContext.seccompProfile spec.initContainers[*].securityContext.seccompProfile 允许的值: 'runtime/default' 未定义/nil |
策略实例化
将策略定义从策略实例中解耦出来有助于形成跨集群的策略理解和语言陈述, 以免绑定到特定的下层实施机制。
随着相关机制的成熟,这些机制会按策略分别定义在下面。特定策略的实施方法不在这里定义。
常见问题
为什么不存在介于 Privileged 和 Baseline 之间的策略类型
这里定义的三种策略框架有一个明晰的线性递进关系,从最安全(Restricted)到最不安全, 并且覆盖了很大范围的工作负载。特权要求超出 Baseline 策略者通常是特定于应用的需求, 所以我们没有在这个范围内提供标准框架。 这并不意味着在这样的情形下仍然只能使用 Privileged 框架,只是说处于这个范围的 策略需要因地制宜地定义。
SIG Auth 可能会在将来考虑这个范围的框架,前提是有对其他框架的需求。
安全策略与安全上下文的区别是什么?
安全上下文在运行时配置 Pod 和容器。安全上下文是在 Pod 清单中作为 Pod 和容器规约的一部分来定义的,所代表的是 传递给容器运行时的参数。
安全策略则是控制面用来对安全上下文以及安全性上下文之外的参数实施某种设置的机制。 在 2020 年 2 月,目前实施这些安全性策略的原生解决方案是 Pod 安全性策略 - 一种对集群中 Pod 的安全性策略进行集中控制的机制。 Kubernetes 生态系统中还在开发一些其他的替代方案,例如 OPA Gatekeeper。
我应该为我的 Windows Pod 实施哪种框架?
Kubernetes 中的 Windows 负载与标准的基于 Linux 的负载相比有一些局限性和区别。 尤其是 Pod SecurityContext 字段 对 Windows 不起作用。 因此,目前没有对应的标准 Pod 安全性框架。
沙箱(Sandboxed) Pod 怎么处理?
现在还没有 API 标准来控制 Pod 是否被视作沙箱化 Pod。 沙箱 Pod 可以通过其是否使用沙箱化运行时(如 gVisor 或 Kata Container)来辨别,不过 目前还没有关于什么是沙箱化运行时的标准定义。
沙箱化负载所需要的保护可能彼此各不相同。例如,当负载与下层内核直接隔离开来时, 限制特权化操作的许可就不那么重要。这使得那些需要更多许可权限的负载仍能被有效隔离。
此外,沙箱化负载的保护高度依赖于沙箱化的实现方法。 因此,现在还没有针对所有沙箱化负载的建议策略。
2 - 云原生安全概述
本概述定义了一个模型,用于在 Cloud Native 安全性上下文中考虑 Kubernetes 安全性。
警告: 此容器安全模型只提供建议,而不是经过验证的信息安全策略。
云原生安全的 4 个 C
你可以分层去考虑安全性,云原生安全的 4 个 C 分别是云(Cloud)、集群(Cluster)、容器(Container)和代码(Code)。
说明: 这种分层方法增强了深度防护方法在安全性方面的 防御能力,该方法被广泛认为是保护软件系统的最佳实践。
云原生安全模型的每一层都是基于下一个最外层,代码层受益于强大的基础安全层(云、集群、容器)。你无法通过在代码层解决安全问题来为基础层中糟糕的安全标准提供保护。
云
在许多方面,云(或者位于同一位置的服务器,或者是公司数据中心)是 Kubernetes 集群中的 可信计算基。 如果云层容易受到攻击(或者被配置成了易受攻击的方式),就不能保证在此基础之上构建的组件是安全的。 每个云提供商都会提出安全建议,以在其环境中安全地运行工作负载。
云提供商安全性
如果您是在您自己的硬件或者其他不同的云提供商上运行 Kubernetes 集群, 请查阅相关文档来获取最好的安全实践。
下面是一些比较流行的云提供商的安全性文档链接:
IaaS 提供商 | 链接 |
---|---|
Alibaba Cloud | https://www.alibabacloud.com/trust-center |
Amazon Web Services | https://aws.amazon.com/security/ |
Google Cloud Platform | https://cloud.google.com/security/ |
IBM Cloud | https://www.ibm.com/cloud/security |
Microsoft Azure | https://docs.microsoft.com/en-us/azure/security/azure-security |
VMWare VSphere | https://www.vmware.com/security/hardening-guides.html |
基础设施安全
关于在 Kubernetes 集群中保护你的基础设施的建议:
Kubetnetes 基础架构关注领域 | 建议 |
---|---|
通过网络访问 API 服务(控制平面) | 所有对 Kubernetes 控制平面的访问不允许在 Internet 上公开,同时应由网络访问控制列表控制,该列表包含管理集群所需的 IP 地址集。 |
通过网络访问 Node(节点) | 节点应配置为 仅能 从控制平面上通过指定端口来接受(通过网络访问控制列表)连接,以及接受 NodePort 和 LoadBalancer 类型的 Kubernetes 服务连接。如果可能的话,这些节点不应完全暴露在公共互联网上。 |
Kubernetes 访问云提供商的 API | 每个云提供商都需要向 Kubernetes 控制平面和节点授予不同的权限集。为集群提供云提供商访问权限时,最好遵循对需要管理的资源的最小特权原则。Kops 文档提供有关 IAM 策略和角色的信息。 |
访问 etcd | 对 etcd(Kubernetes 的数据存储)的访问应仅限于控制平面。根据配置情况,你应该尝试通过 TLS 来使用 etcd。更多信息可以在 etcd 文档中找到。 |
etcd 加密 | 在所有可能的情况下,最好对所有驱动器进行静态数据加密,但是由于 etcd 拥有整个集群的状态(包括机密信息),因此其磁盘更应该进行静态数据加密。 |
集群
保护 Kubernetes 有两个方面需要注意:
- 保护可配置的集群组件
- 保护在集群中运行的应用程序
集群组件
如果想要保护集群免受意外或恶意的访问,采取良好的信息管理实践,请阅读并遵循有关保护集群的建议。
集群中的组件(您的应用)
根据您的应用程序的受攻击面,您可能需要关注安全性的特定面,比如: 如果您正在运行中的一个服务(A 服务)在其他资源链中很重要,并且所运行的另一工作负载(服务 B) 容易受到资源枯竭的攻击,则如果你不限制服务 B 的资源的话,损害服务 A 的风险就会很高。 下表列出了安全性关注的领域和建议,用以保护 Kubernetes 中运行的工作负载:
容器
容器安全性不在本指南的探讨范围内。下面是一些探索此主题的建议和连接:
容器关注领域 | 建议 |
---|---|
容器漏洞扫描和操作系统依赖安全性 | 作为镜像构建的一部分,您应该扫描您的容器里的已知漏洞。 |
镜像签名和执行 | 对容器镜像进行签名,以维护对容器内容的信任。 |
禁止特权用户 | 构建容器时,请查阅文档以了解如何在具有最低操作系统特权级别的容器内部创建用户,以实现容器的目标。 |
使用带有较强隔离能力的容器运行时 | 选择提供较强隔离能力的容器运行时类。 |
代码
应用程序代码是您最能够控制的主要攻击面之一,虽然保护应用程序代码不在 Kubernetes 安全主题范围内,但以下是保护应用程序代码的建议:
代码安全性
代码关注领域 | 建议 |
---|---|
仅通过 TLS 访问 | 如果您的代码需要通过 TCP 通信,请提前与客户端执行 TLS 握手。除少数情况外,请加密传输中的所有内容。更进一步,加密服务之间的网络流量是一个好主意。这可以通过被称为相互 LTS 或 mTLS 的过程来完成,该过程对两个证书持有服务之间的通信执行双向验证。 |
限制通信端口范围 | 此建议可能有点不言自明,但是在任何可能的情况下,你都只应公开服务上对于通信或度量收集绝对必要的端口。 |
第三方依赖性安全 | 最好定期扫描应用程序的第三方库以了解已知的安全漏洞。每种编程语言都有一个自动执行此检查的工具。 |
静态代码分析 | 大多数语言都提供给了一种方法,来分析代码段中是否存在潜在的不安全的编码实践。只要有可能,你都应该使用自动工具执行检查,该工具可以扫描代码库以查找常见的安全错误,一些工具可以在以下连接中找到:https://owasp.org/www-community/Source_Code_Analysis_Tools |
动态探测攻击 | 您可以对服务运行一些自动化工具,来尝试一些众所周知的服务攻击。这些攻击包括 SQL 注入、CSRF 和 XSS。OWASP Zed Attack 代理工具是最受欢迎的动态分析工具之一。 |
接下来
学习了解相关的 Kubernetes 安全主题:
3 - Kubernetes API 访问控制
本页面概述了对 Kubernetes API 的访问控制。
用户使用 kubectl
、客户端库或构造 REST 请求来访问 Kubernetes API。
人类用户和 Kubernetes 服务账户都可以被鉴权访问 API。
当请求到达 API 时,它会经历多个阶段,如下图所示:
传输安全
在典型的 Kubernetes 集群中,API 服务器在 443 端口上提供服务,受 TLS 保护。 API 服务器出示证书。 该证书可以使用私有证书颁发机构(CA)签名,也可以基于链接到公认的 CA 的公钥基础架构签名。
如果你的集群使用私有证书颁发机构,你需要在客户端的 ~/.kube/config
文件中提供该 CA 证书的副本,
以便你可以信任该连接并确认该连接没有被拦截。
你的客户端可以在此阶段出示 TLS 客户端证书。
认证
如上图步骤 1 所示,建立 TLS 后, HTTP 请求将进入认证(Authentication)步骤。 集群创建脚本或者集群管理员配置 API 服务器,使之运行一个或多个身份认证组件。 身份认证组件在认证节中有更详细的描述。
认证步骤的输入整个 HTTP 请求;但是,通常组件只检查头部或/和客户端证书。
认证模块包含客户端证书、密码、普通令牌、引导令牌和 JSON Web 令牌(JWT,用于服务账户)。
可以指定多个认证模块,在这种情况下,服务器依次尝试每个验证模块,直到其中一个成功。
如果请求认证不通过,服务器将以 HTTP 状态码 401 拒绝该请求。
反之,该用户被认证为特定的 username
,并且该用户名可用于后续步骤以在其决策中使用。
部分验证器还提供用户的组成员身份,其他则不提供。
鉴权
如上图的步骤 2 所示,将请求验证为来自特定的用户后,请求必须被鉴权。
请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。 如果现有策略声明用户有权完成请求的操作,那么该请求被鉴权通过。
例如,如果 Bob 有以下策略,那么他只能在 projectCaribou
名称空间中读取 Pod。
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "bob",
"namespace": "projectCaribou",
"resource": "pods",
"readonly": true
}
}
如果 Bob 执行以下请求,那么请求会被鉴权,因为允许他读取 projectCaribou
名称空间中的对象。
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"spec": {
"resourceAttributes": {
"namespace": "projectCaribou",
"verb": "get",
"group": "unicorn.example.org",
"resource": "pods"
}
}
}
如果 Bob 在 projectCaribou
名字空间中请求写(create
或 update
)对象,其鉴权请求将被拒绝。
如果 Bob 在诸如 projectFish
这类其它名字空间中请求读取(get
)对象,其鉴权也会被拒绝。
Kubernetes 鉴权要求使用公共 REST 属性与现有的组织范围或云提供商范围的访问控制系统进行交互。 使用 REST 格式很重要,因为这些控制系统可能会与 Kubernetes API 之外的 API 交互。
Kubernetes 支持多种鉴权模块,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。 管理员创建集群时,他们配置应在 API 服务器中使用的鉴权模块。 如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块鉴权该请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。
要了解更多有关 Kubernetes 鉴权的更多信息,包括有关使用支持鉴权模块创建策略的详细信息, 请参阅鉴权。
准入控制
准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。
准入控制器对创建、修改、删除或(通过代理)连接对象的请求进行操作。 准入控制器不会对仅读取对象的请求起作用。 有多个准入控制器被配置时,服务器将依次调用它们。
这一操作如上图的步骤 3 所示。
与身份认证和鉴权模块不同,如果任何准入控制器模块拒绝某请求,则该请求将立即被拒绝。
除了拒绝对象之外,准入控制器还可以为字段设置复杂的默认值。
可用的准入控制模块在准入控制器中进行了描述。
请求通过所有准入控制器后,将使用检验例程检查对应的 API 对象,然后将其写入对象存储(如步骤 4 所示)。
API 服务器端口和 IP
前面的讨论适用于发送到 API 服务器的安全端口的请求(典型情况)。 API 服务器实际上可以在 2 个端口上提供服务:
默认情况下,Kubernetes API 服务器在 2 个端口上提供 HTTP 服务:
localhost
端口:- 用于测试和引导,以及主控节点上的其他组件(调度器,控制器管理器)与 API 通信
- 没有 TLS
- 默认为端口 8080,使用
--insecure-port
进行更改 - 默认 IP 为 localhost,使用
--insecure-bind-address
进行更改 - 请求 绕过 身份认证和鉴权模块
- 由准入控制模块处理的请求
- 受需要访问主机的保护
“安全端口”:
- 尽可能使用
- 使用 TLS。 用
--tls-cert-file
设置证书,用--tls-private-key-file
设置密钥 - 默认端口 6443,使用
--secure-port
更改 - 默认 IP 是第一个非本地网络接口,使用
--bind-address
更改 - 请求须经身份认证和鉴权组件处理
- 请求须经准入控制模块处理
- 身份认证和鉴权模块运行
接下来
阅读更多有关身份认证、鉴权和 API 访问控制的文档:
你可以了解
- Pod 如何使用 Secrets 获取 API 凭证.