在 Kubernetes 集群中操纵的应用步调堆栈通常遵循微服务架构。“最小化微服务漏洞”领域涵盖了在 Pod 级别上实施安全设置的治理和强制执行。我们将介绍 Kubernetes 核心功能以及外部工具,帮助减少安全漏洞。此外,我们还将讨论运行微服务的 Pod 之间的加密网络通信。
在高层次上,本章涵盖以下概念:
设置恰当的操纵体系级安全域与安全上下文,Pod 安全审计(PSA)和 Open Policy Agent Gatekeeper
管理秘密
利用容器运行时沙盒,如 gVisor 和 Kata 容器
通过双向传输层安全协议(TLS)实现 Pod 之间的通信加密
设置恰当的操纵体系级安全域
Kubernetes 核心和 Kubernetes 生态体系均提供了界说、实施和管理 Pod 和容器级别安全设置的解决方案。本节将讨论安全上下文、Pod 安全审计和 Open Policy Agent Gatekeeper。您将学习怎样通过示例应用每个功能和工具,展示它们在安全性方面的紧张性。让我们从设置一个场景开始。
景象:攻击者滥用 root 用户容器访问权限
作为容器编排引擎的 Kubernetes 可以应用额外的设置来增强容器安全性。通过界说安全上下文来实现。安全上下文界说了 Pod 或容器的特权和访问控制设置。以下列表提供了一些示例:
应用步调应利用的用户 ID 来运行 Pod 和/或容器
文件体系访问应利用的组 ID
授予容器内运行的历程部分 root 用户权限但不是全部权限
安全上下文不是 Kubernetes 的原语。它被建模为 Pod 规范内的一组属性,在指令 securityContext 下。在 Pod 级别界说的安全设置适用于在 Pod 中运行的所有容器;但容器级别的设置优先。有关 Pod 级别安全属性的更多信息,请参阅 PodSecurityContext API。容器级别的安全属性可以在 SecurityContext API 中找到。
强制利用非 root 用户
让我们快速检查容器利用哪个用户 ID。进入容器并运行 id 下令。输出显示用户 ID、组 ID 和附加组的 ID。镜像 bitnami/nginx 在构建容器镜像时通过指令将用户 ID 设置为 1001:
$ kubectl exec non-root-success -it -- /bin/sh
$ id
uid=1001 gid=0(root) groups=0(root)
$ exit
复制代码
设置特定的用户和组 ID
很多容器镜像未设置显式的用户 ID 或组 ID。倒霉用 root 默认用户运行,可以设置所需的用户 ID 和组 ID,以最小化埋伏的安全风险。文件 container-user-id.yaml 中存储的 YAML 清单文件(示例 5-3)将用户 ID 设置为 1000,组 ID 设置为 3000。
示例 5-3. 利用特定用户和组 ID 运行容器
apiVersion: v1
kind: Pod
metadata:
name: user-id
spec:
containers:
- image: busybox:1.35.0
name: busybox
command: ["sh", "-c", "sleep 1h"]
securityContext:
runAsUser: 1000
runAsGroup: 3000
复制代码
创建 Pod 将无问题。容器转换为“运行”状态:
$ kubectl apply -f container-user-id.yaml
pod/user-id created
$ kubectl get pods user-id
NAME READY STATUS RESTARTS AGE
user-id 1/1 Running 0 6s
复制代码
进入容器后,您可以检查用户 ID 和组 ID。当前用户无权在/目录中创建文件。在/tmp目录中创建文件将起作用,因为大多数用户都有写入权限:
$ kubectl exec user-id -it -- /bin/sh
/ $ id
uid=1000 gid=3000 groups=3000
/ $ touch test.txt
touch: test.txt: Permission denied
/ $ touch /tmp/test.txt
/ $ exit
复制代码
避免利用特权容器
Kubernetes 在历程、网络、挂载、用户 ID 等方面在容器命名空间和主机命名空间之间创建了明白的分离。您可以设置容器的安全上下文以获取对主机命名空间某些方面的权限。利用特权容器时,请思量以下影响:
容器内的历程险些具有与主机上历程相同的权限。
容器具有访问主机上所有设备的权限。
容器中的root用户具有与主机上的root用户类似的权限。
可以在容器中挂载主机文件体系上的所有目录。
可以通过利用sysctl下令更改内核设置。
利用特权模式的容器
设置容器以利用特权模式应该是一个罕见的情况。大多数运行在容器中的应用步调和历程不需要超出容器命名空间之外的提拔权限。假如遇到设置为利用特权模式的 Pod,请接洽负责的团队或开发者举行澄清,因为这将为攻击者打开入侵主机体系的漏洞。
让我们比力设置为非特权容器和设置为特权模式运行的容器的行为。首先,我们将设置一个常规的 Pod,如示例 5-4 所示。在 Pod 或容器级别未设置安全上下文。
示例 5-4. 利用非特权模式的容器中的 Pod
作为 Kubernetes 安全专家,您就是这里的关键人物。Kubernetes 生态体系提供了核心功能和外部工具,用于强制执行 Pod 的安全标准,以便没有正确设置的对象将被拒绝或至少会举行审计。下一节将探究名为 Pod 安全入场的 Kubernetes 核心功能。
理解 Pod 安全入场(PSA)
Kubernetes 的旧版本附带了名为 Pod 安全计谋(PSP)的功能。Pod 安全计谋是一个概念,帮助强制执行 Pod 对象的安全标准。Kubernetes 1.21 弃用了 Pod 安全计谋,并引入了其替代功能 Pod 安全入场。PSA 决定要遵循哪个 Pod 安全标准(PSS)。PSS 界说了从高度限制到高度宽松的安全计谋范围。
然而,Kubernetes 1.25 完全删除了 Pod 安全计谋。您大概仍然会在旧版本的 CKS 课程中看到此功能。我们在本书中将仅关注 Pod 安全入场。PSA 在 Kubernetes 1.23 中默认启用;但是,您需要声明哪些 Pod 应遵循安全标准。要选择 PSA 功能,您只需在命名空间中添加特定格式的标签。该命名空间中的所有 Pod 将必须遵循声明的标准。
标签包罗三个部分:前缀、模式和级别。前缀 总是利用硬编码值 pod-security.kubernetes.io,背面跟着一个斜线。模式 决定了违规处理方式。最后,级别 规定了遵循的安全标准的程度。这样的标签示例大概如下所示:
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted
复制代码
模式允许设置三种差别的选项,如 Table 5-1 所示。
Table 5-1. Pod 安全入场模式
模式行为enforce违规将导致 Pod 被拒绝。audit允许创建 Pod。违规将被记载到审计日志中。warn允许创建 Pod。违规将在控制台上显示。 Table 5-2 阐明了由 PSA 设置的级别确定的安全计谋。
Table 5-2. Pod 安全入场级别
级别行为privileged完全无限制的计谋。baseline最低限度的限制性计谋,覆盖了关键标准。restricted遵循从安全角度加固 Pod 的最佳实践的严格限制性计谋。 详细了解 PSA 的内容,请参阅 Kubernetes 文档。
为命名空间执行 Pod 安全标准
Kubernetes 安全功能讨论不完整,没有提到 Secrets。我假设你已经非常认识 API 原语 Secret 用于界说敏感数据以及在 Pod 中消费的差别选项。思量到这个主题已经是 CKA 考试的一部分,我在这里不再赘述。更多信息,请参阅《Certified Kubernetes Administrator (CKA) Study Guide》中的相关章节或者Kubernetes 文档。我在“Configuring a Container with a ConfigMap or Secret”章节中讨论在容器中消费 ConfigMaps 和 Secrets 时的安全方面。
CKS 考试更增强调 Secret 管理的更专业方面。其中一个场景是处理可以分配给服务账户的 Secret,我们已经提到过。回顾“Creating a Secret for a service account”以刷新你对该主题的影象。由于我们不会在这里讨论所有内置的 Secret 范例,请阅读相关章节了解它们的用途和创建方式在Kubernetes 文档中。
存储 Secrets 键值对的中心位置是 etcd。让我们来看看,假如攻击者能够访问 Kubernetes 后端存储集群数据,大概会出现的埋伏问题。
场景:攻击者获得访问运行 etcd 的节点
您可以利用下令行选项--encryption-provider-config控制 API 数据在 etcd 中怎样加密,该选项提供给 API 服务器历程。分配给参数的值需要指向一个界说了EncryptionConfiguration对象的设置文件。我们将首先创建设置文件,然后设置 API 服务器历程来消耗它。
生成一个 32 字节的随机密钥并对其举行 base64 编码。该值用于设置所谓的加密设置中的提供步调:
[ 0.415862] Rewriting operating system in Javascript...
[ 0.593368] Reading process obituaries...
[ 0.741642] Segmenting fault lines...
[ 0.797360] Daemonizing children...
[ 0.831010] Creating bureaucratic processes...
[ 1.313731] Searching for needles in stacks...
[ 1.455084] Constructing home...
[ 1.834278] Gathering forks...
[ 1.928142] Mounting deweydecimalfs...
[ 2.109973] Setting up VFS...
[ 2.157224] Ready!
复制代码
理解 Pod 与 Pod 之间的 mTLS 加密
在 “利用网络计谋限制 Pod 与 Pod 通信” 中,我们谈论了 Pod 与 Pod 之间的通信。一个紧张的要点是,除非你订定更严格的网络计谋,否则每个 Pod 都可以通过定位其捏造 IP 地点与任何其他 Pod 通信。默认情况下,两个 Pod 之间的通信是未加密的。
TLS 提供网络通信的加密,通常与 HTTP 协议一起利用。当我们谈论从浏览器对 Web 页面的调用时,就会涉及利用 HTTPS 协议。作为认证过程的一部分,客户端向服务器提供其客户端证书以证实其身份。但服务器不对客户端举行认证。
在加载 Web 页面时,客户端(即浏览器)的身份通常并不紧张。紧张的是 Web 页面证实其身份。双向 TLS(mTLS)类似于 TLS,但两边都必须举行身份验证。这种方法有以下几个好处。首先,通过加密实现安全通信。其次,可以验证客户端身份。攻击者不能轻易假冒另一个 Pod。
景象:攻击者监听两个 Pod 之间的通信
攻击者可以利用默认的未加密 Pod 与 Pod 之间的网络通信行为来加以利用。如你在 图 5-5 中所见,攻击者甚至不需要侵入 Pod。他们可以简单地通过假冒发送端或接收端来监听 Pod 与 Pod 的通信,提取敏感信息,然后用于更高级的攻击向量。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0505.png
图 5-5. 攻击者监听 Pod 与 Pod 之间的通信
一些容器镜像大概有千兆字节甚至更多的大小。您真的需要这种容器镜像捆绑的所有功能吗?大概性不大。荣幸的是,很多容器生产商为同一个版本上传了各种变体的容器镜像。其中一个变体是alpine镜像,这是一个小巧、轻量且较少漏洞的 Linux 发行版。正如您在下面的输出中所看到的,下载的带有标签3.17.0的alpine容器镜像只有 7.05MB 的大小:
另一种验证允许利用的镜像注册表的方法是拦截在创建 Pod 时向 API 服务器发出的调用。通过启用 Admission Controller 插件可以实现此机制。设置后,当 API 服务器接收请求时,会自动调用 Admission Controller 插件的逻辑,但在验证调用者身份后。我们已经讨论了 API 调用必须颠末的阶段,见 “处理请求”。
Admission Controller 提供了在请求生效之前批准、拒绝或变更请求的方式。比方,我们可以检查与创建 Pod 的 API 请求一起发送的数据,迭代分配的容器镜像,并执行自界说逻辑以验证容器镜像的表示法。假如容器镜像不符合预期的约定,我们可以拒绝创建 Pod。图 6-5 展示了高级别的工作流程。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0605.png
图 6-5. 拦截特定 Pod API 调用并利用 Webhook 处理它
ImagePolicyWebhook 准入控制器插件是我们可以设置用于拦截 Kubernetes API 调用的插件之一。 您可以从其名称推断出插件的功能。 它为 Pod 中界说的所有容器镜像界说计谋。 为了与界说的计谋比力容器镜像,插件通过 HTTPS 调用到一个服务外部于 Kubernetes 的 webhook。 外部服务然后决定怎样验证数据。 在准入控制器插件的上下文中,外部服务也被称为后端。
实施后端应用