Kubernetes-安全与可观测指南-全-

Kubernetes 安全与可观测指南(全)

原文:zh.annas-archive.org/md5/8ee20dbda2ce1515f121225d872e820c

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Kubernetes 默认情况下并不安全。现有的企业和云安全方法受到 Kubernetes 动态特性的挑战,以及使用它通常带来的增强组织敏捷性的目标。成功地在这种新环境中保护、观察和排错关键微服务需要全面理解一系列考虑因素。这些包括组织挑战、新的云原生方法如何帮助应对这些挑战,以及新的最佳实践以及如何操作化它们。

尽管有大量关于 Kubernetes 的资源,但在其中导航并制定全面的安全和可观察性策略可能是一项艰巨的任务,在许多情况下,会导致显著损害所需的安全姿态。

为了指导您朝向一个综合的安全和可观察性策略,覆盖这些考虑的广度,并为您提供在将应用程序迁移到 Kubernetes 时帮助您的最佳实践和工具,我们撰写了这本书。

在我们在 Tigera 工作并构建 Kubernetes 的网络和安全工具 Calico 多年后,我们近距离地见证了用户的旅程。我们看到许多用户专注于在 Kubernetes 中部署工作负载,而没有深思熟虑他们的安全或可观察性策略,然后在尝试理解如何保护和观察这样一个复杂的分布式系统时遇到困难。我们撰写本书的目标是通过与您分享我们所学到的知识,尽量减少这种痛苦。我们在全书中提到了一些工具示例,其中包括 Calico。我们认为 Calico 是一个优秀且受欢迎的选项,但还有许多好的工具,如 Weave Net、VMware Tanzu、Aqua Security 和 Datadog,供您选择。最终,只有您可以决定哪个对您的需求最合适。

Kubernetes 采用的阶段

任何成功的 Kubernetes 采用过程都遵循三个明确阶段:

学习阶段

作为新用户,您首先要学习 Kubernetes 的工作原理,设置一个沙盒环境,并开始考虑如何在您的环境中使用 Kubernetes。在这个阶段,您希望利用在线 Kubernetes 资源,并使用开源技术。

试点/预生产阶段

一旦你熟悉了 Kubernetes 并理解了它的工作原理,你会开始考虑采用 Kubernetes 的高级战略。在这个阶段,通常会进行试点项目来设置你的集群并引入几个应用程序。随着你在这个阶段的进展,你将会对你将使用的平台有所了解,并且决定它们是在本地还是在云上。如果你选择云端,你将决定是自己托管集群还是利用云提供商提供的托管 Kubernetes 服务。你还需要考虑保护应用程序的策略。此时,你会意识到 Kubernetes 由于其声明性质而与众不同。这意味着平台对于网络、基础设施、主机等方面抽象了很多细节,因此使得你非常容易为你的应用程序使用该平台。由于这一点,你现在需要考虑与 Kubernetes 本地安全相关的安全性,因为你目前使用的保护应用程序、基础设施和网络的方法根本行不通。

生产阶段

到此时,你已经完成了试点项目并成功引入了几个应用程序。你的重点是在生产中运行关键任务应用程序,并考虑是否将大多数应用程序迁移到 Kubernetes 上。在这个阶段,你需要详细计划安全性、合规性、故障排除和可观察性,以便安全有效地将应用程序迁移到生产环境,并实现 Kubernetes 平台的所有好处。

注意

Kubernetes 作为基于容器的应用程序平台的流行和成功使得许多人急于采用它。在过去几年中,托管 Kubernetes 服务提供商已经努力创新并使采用更加容易。新用户可能会被诱惑直接跳过学习和试点阶段,以便快速进入生产阶段。我们警告不要因此而忽视尽职调查。在将关键任务应用程序引入 Kubernetes 之前,你必须将安全性和可观察性视为关键的第一步;否则你的 Kubernetes 采用将是不完整的,并且可能存在安全隐患。

这本书适合谁

这本书适合广泛范围的 Kubernetes 实践者,他们处于采用试点/预生产阶段。你可能是平台工程师,或者是安全或 DevOps 团队的一部分。你们中的一些人是组织中首批采用 Kubernetes 的人,希望从一开始就正确处理安全性和可观察性问题。其他人正在帮助在已经采用 Kubernetes 但尚未解决其带来的安全性和可观察性挑战的组织中建立最佳实践。我们假设你具备 Kubernetes 的基础知识——知道它是什么以及如何将其作为托管应用程序的编排工具使用。我们还假设你了解应用程序在 Kubernetes 集群中的部署方式及其分布式特性。

在这个广泛的受众中,有许多不同的角色。以下是一些帮助设计和实施基于 Kubernetes 架构的团队的非详尽列表,这些团队将在本书中找到价值。请注意,您的组织中可能使用不同的角色名称,因此请查看每个角色的职责,以确定在您的组织中对应的角色。本书将使用这些名称帮助您理解一个概念如何影响每个角色。

平台团队

平台工程团队负责设计和实施 Kubernetes 平台。许多企业选择实施容器即服务平台(CaaS)策略。这是一个跨企业使用的平台,用于实现基于容器的工作负载。平台工程团队负责平台组件,并将它们提供为应用团队的服务。本书将帮助您了解安全平台的重要性,并提供帮助保护平台层的最佳实践,以便为应用团队提供在安全 Kubernetes 平台上部署应用程序的方法。它还将帮助您了解如何管理新应用程序对平台的安全风险。

网络团队

网络团队负责在企业网络中集成 Kubernetes 集群。我们看到这些团队在本地部署 Kubernetes 和在云环境中使用自托管或托管 Kubernetes 服务时扮演不同角色。您将了解网络安全的重要性以及如何建立具有强大安全架构的网络。本书涵盖了将应用程序暴露到 Kubernetes 平台外部以及应用程序访问外部网络的最佳实践。您还将学习如何与其他团队合作,实施保护 Kubernetes 内工作负载外元素的网络安全措施。

安全团队

企业安全团队在向云原生应用转型过程中受到的影响最大。云原生应用是专为云环境设计的应用程序,与传统应用程序有所不同。例如,这些应用程序分布在网络基础设施中。本书将帮助您了解如何保护用于托管应用程序的 Kubernetes 平台的详细信息。它将为您提供一个完整的视角,教您如何保护关键工作负载。您将学习如何与各种团队合作,在 Kubernetes 新世界中有效实施安全措施。

合规团队

企业的合规团队负责确保组织中的运营和流程符合组织采用的合规标准的要求。您将了解如何在基于 Kubernetes 的平台中实施各种合规要求以及如何监控持续的合规性。请注意,我们不会详细介绍各种合规要求和标准,但我们将为您提供策略、示例和工具,以帮助您满足合规性要求。

运营团队

运营团队是负责构建和维护应用程序的开发人员/工具/运维工程师团队。他们也被称为 DevOps 或站点可靠性工程师(SREs)。他们确保应用程序被接入并符合所需的服务水平协议(SLAs)。在本书中,您将了解如何在安全 Kubernetes 集群中扮演角色以及与安全团队的合作。我们将介绍"左移安全"的概念,即安全需要在应用程序开发生命周期的早期阶段发生。在 Kubernetes 平台中,可观察性意味着通过查看平台数据推断有关集群操作的详细信息的能力。这是监视分布式应用程序的现代方式,您将学习如何实施可观察性及其对安全性的重要性。

您将学到什么

在本书中,您将学习如何在实施 Kubernetes 战略时考虑安全性,从构建应用程序到构建基础设施再到托管应用程序再到部署应用程序再到运行应用程序。我们将为每个步骤提供安全最佳实践,并附有示例和工具,帮助您保护您的 Kubernetes 平台。我们将介绍如何实施审计、合规性以及像加密这样的其他企业安全控制。

您还将学习如何使用工具和示例来实施可观察性,并展示其与安全性和故障排除的相关性。这种对您的 Kubernetes 平台增强的可见性将驱动与您独特情况相关的可操作见解。

在本书的结尾,您将能够为您的 Kubernetes 集群实施这些安全和可观察性最佳实践。

本书中使用的约定

本书中使用以下排版约定:

Italic

表示新术语、网址、电子邮件地址、文件名和文件扩展名。

Constant width

用于程序清单,以及在段落中引用程序元素,如变量或函数名、数据库、数据类型、环境变量、语句和关键字。

Constant width bold

显示用户应按照字面意思键入的命令或其他文本。

Constant width italic

显示应由用户提供的值或由上下文确定的值替换的文本。

注意

此元素表示一般注释。

使用代码示例

补充材料(代码示例、练习等)可在 https://github.com/tigera/k8s-security-observability-book 下载。

如果您有技术问题或在使用代码示例时遇到问题,请发送电子邮件至:bookquestions@oreilly.com

这本书旨在帮助您完成工作。一般情况下,如果本书提供了示例代码,您可以在程序和文档中使用它。除非您复制了本书的大部分代码,否则无需征得我们的许可。例如,编写一个使用本书多个代码片段的程序不需要许可。销售或分发 O’Reilly 书籍中的示例代码需要许可。引用本书回答问题并引用示例代码不需要许可。将本书中大量示例代码整合到产品文档中需要许可。

我们感谢您的支持,但通常不需要署名。署名通常包括标题、作者、出版社和 ISBN 号。例如:“Kubernetes Security and Observability 由 Brendan Creane 和 Amit Gupta(O’Reilly)编写。版权所有 2022 年 O’Reilly Media,978-1-098-10711-6。”

如果您觉得使用代码示例超出了合理使用范围或上述许可,欢迎随时与我们联系:permissions@oreilly.com

O’Reilly Online Learning

注意

40 多年来,O’Reilly Media 一直为公司提供技术和商业培训、知识和见解,帮助它们取得成功。

我们独特的专家和创新者网络通过书籍、文章以及我们的在线学习平台分享他们的知识和专长。O’Reilly 的在线学习平台为您提供按需访问的现场培训课程、深入学习路径、交互式编码环境,以及来自 O’Reilly 和其他 200 多家出版商的广泛的文本和视频内容。获取更多信息,请访问:http://oreilly.com

如何联系我们

请将有关本书的评论和问题寄给出版社:

  • O’Reilly Media, Inc.

  • 1005 Gravenstein Highway North

  • CA 95472 Sebastopol

  • 800-998-9938(美国或加拿大)

  • 707-829-0515(国际或本地)

  • 707-829-0104(传真)

我们为这本书制作了一个网页,上面列出了勘误、示例和任何额外信息。您可以访问这个页面:https://oreil.ly/KSO

电子邮件 bookquestions@oreilly.com 以评论或询问有关此书的技术问题。

获取关于我们的书籍和课程的新闻和信息,请访问:http://oreilly.com

在 Facebook 上找到我们:http://facebook.com/oreilly

关注我们的 Twitter:http://twitter.com/oreillymedia

在 YouTube 关注我们:http://youtube.com/oreillymedia

致谢

写作本书是一次很棒的经历,没有几位人士的帮助、支持和指导是不可能的。首先,我们要感谢 Project Calico 的社区、开发人员和维护者,是你们的创新和对 Kubernetes 及其安全性和可观测性的贡献,使我们能够写出这本书。Tigera 令人惊叹的工程和安全研究团队已构建出产品来解决安全和可观测性的复杂挑战,这使我们能够清晰地理解用户面临的挑战。在我们撰写本书的过程中,这些都非常有帮助,以引导用户找到全面的安全和可观测性解决方案。

我们还要感谢那些提供意见和专业知识的审阅者。他们的评论和指导极大丰富了本书内容。特别感谢 Manish Sampat、Alex Pollitt、Virginia Wilson、Seth Vargo、Tim Mackey、Ian Lewis、Puja Absassi 和 Jose Ruiz——你们太棒了!

最后,我们要感谢所有为 Kubernetes 安全性和可观测性做出贡献的社区成员。看到这个领域的创新令人惊叹,我们很高兴能参与到 Kubernetes 安全性和可观测性中去。

第一章:安全性和可观察性策略

在本章中,我们将概述您如何为 Kubernetes 实施构建安全性和可观察性策略。后续章节将更详细地涵盖这些概念。在您的 Kubernetes 之旅的试点/预生产阶段,您需要考虑安全策略,因此如果您是安全团队的一部分,本章非常重要。如果您是网络、平台或应用团队的一部分,本章展示了您如何成为安全策略的一部分,并讨论了安全、平台和应用团队之间合作的重要性。

我们将涵盖以下概念,指导您制定安全性和可观察性策略:

  • 如何保护 Kubernetes 与传统安全方法有所不同

  • 在 Kubernetes 集群中部署应用程序(工作负载)的生命周期以及每个阶段的最佳实践

  • 您应该如何实施可观察性以帮助安全。

  • 知名的安全框架以及如何在安全策略中使用它们

Kubernetes 的安全性:一个新的不同世界

在本节中,我们将强调 Kubernetes 的不同之处以及为什么传统的安全方法在 Kubernetes 实施中不起作用。

随着工作负载转移到云端,Kubernetes 是最常见的编排器来管理它们。Kubernetes 受欢迎的原因在于其声明性特质:它抽象了基础设施细节,并允许用户指定他们想要运行的工作负载和期望的结果。应用团队不需要担心工作负载如何部署,工作负载在哪里运行,或者像网络这样的其他细节;他们只需要在 Kubernetes 中设置配置来部署他们的应用。

Kubernetes 通过管理工作负载的创建、关闭和重启来实现这种抽象。在典型的实现中,根据工作负载的要求,工作负载可以安排在网络中的任何可用资源(物理主机或虚拟机)上。工作负载运行的一组资源被称为Kubernetes 集群。Kubernetes 监视工作负载的状态(在 Kubernetes 中部署为 pod)并根据需要采取纠正措施(例如,重新启动无响应的节点)。它还管理所有必要的网络以便 pod 和主机之间进行通信。您可以通过从一组支持的网络插件中进行选择来决定网络技术。虽然有一些网络插件的配置选项,但您将无法直接控制网络行为(无论是用于 IP 地址分配还是在典型配置中节点被调度的情况)。

对于安全团队来说,Kubernetes 是一个全新的世界。他们传统的方法是构建一个“机器网络”,然后启用工作负载(应用程序)。在启用的过程中,需要分配 IP、根据需要更新网络设置,并定义和实施网络访问控制规则。完成这些步骤后,应用程序就可以供用户使用了。这个过程确保安全团队有很大的控制权,并且可以轻松地启用和保护应用程序。由于应用程序的 IP 分配、部署位置等方面都是静态的,因此应用程序也更容易保护。

在 Kubernetes 环境中,工作负载作为容器镜像构建,并使用配置文件(yaml)在 Kubernetes 集群中部署。这通常集成在开发过程中,大多数开发团队使用持续集成(CI)和持续交付(CD)来确保软件的快速可靠交付。这意味着安全团队对每个应用程序变更对集群安全的影响具有有限的可见性。在此过程中添加安全审查步骤是不可取的,因为唯一合理的地方是在提交代码时进行。此后的开发过程是自动化的,打断它会与 CI/CD 模型冲突。那么在这种环境下如何保护工作负载呢?

为了了解如何在 Kubernetes 中保护工作负载,了解部署工作负载的各个阶段是非常重要的。

在 Kubernetes 中部署工作负载:每个阶段的安全性

在前一节中,我们描述了使用 CI/CD 管道部署应用程序时面临的安全挑战。本节描述了 Kubernetes 集群中工作负载部署的生命周期,并解释了如何保护每个阶段。工作负载部署的三个阶段是构建、部署和运行时阶段。与传统的客户端-服务器应用程序不同,在 Kubernetes 部署中,应用程序是分布式的,并且 Kubernetes 集群网络作为正常操作的一部分被应用程序使用。由于这种配置,需要考虑以下几点:

  • 在构建工作负载和基础设施时,需要考虑安全最佳实践。这一点非常重要,因为 Kubernetes 中的应用程序是通过 CI/CD 管道部署的。

  • 在部署 Kubernetes 集群和应用程序时,需要考虑安全最佳实践。

  • 最后,应用程序使用基础设施和 Kubernetes 集群网络进行正常操作,需要考虑应用程序运行时的安全最佳实践。

图 1-1 描述了在 Kubernetes 环境中保护工作负载时需要考虑的各个阶段和方面。

图 1-1. 工作负载部署阶段及每个阶段的安全性

每个阶段下方的框描述了您需要考虑该阶段的各个安全方面:

  • 构建阶段是您为工作负载(应用程序)创建(构建)软件并构建基础设施组件(主机或虚拟机)以托管应用程序的阶段。此阶段是开发周期的一部分,在大多数情况下由开发团队负责。在此阶段,您需要考虑 CI/CD 管道的安全性,实施镜像库的安全性,扫描镜像中的漏洞,并加固主机操作系统。您需要确保实施最佳实践以保护镜像注册表,并避免影响镜像注册表中的镜像。通常通过保护对镜像注册表的访问来实现这一点,尽管许多用户拥有私有注册表,并且不允许来自公共注册表的镜像。最后,您需要考虑密钥管理的最佳实践;密钥类似于密码,允许访问集群中的资源。我们将在第三章中详细讨论这些主题。我们建议,在考虑此阶段的安全性时,您应与安全团队合作,以使此阶段的安全性与您的整体安全策略保持一致。

  • 接下来的阶段是部署阶段,在此阶段您设置运行 Kubernetes 部署的平台并部署工作负载。在此阶段,您需要考虑配置 Kubernetes 集群的安全最佳实践,以及为在 Kubernetes 集群内运行的应用程序提供外部访问的安全控制。您还需要考虑安全控制,例如限制对工作负载的访问(Pod 安全策略)、网络策略以控制应用程序对平台组件的访问以及基于角色的访问控制(RBAC)以访问资源(例如服务创建、命名空间创建和向 Pod 添加/更改标签)。在大多数企业中,平台团队负责此阶段。作为平台团队的一员,您需要与开发团队和安全团队合作,实施您的安全策略。

  • 最后一个阶段是运行时阶段,在此阶段您已部署了应用程序并且它正在运行。在此阶段,您需要考虑网络安全,涉及使用网络策略进行控制、威胁防御(使用技术检测和预防集群中的恶意活动)以及企业安全控制,如合规性、审计和加密。安全团队负责此部署阶段。作为安全团队的一员,在设计和实施运行时安全时,您需要与平台和开发团队合作。团队之间的协作(开发、平台和安全)对于建立有效的安全策略非常重要。我们建议您确保所有这些团队保持一致。

请注意,与传统的安全策略不同,传统安全策略在关键点(如边界)强制执行安全性,而在 Kubernetes 集群的情况下,您需要在每个阶段实施安全性。此外,所有参与的团队(应用程序、平台和安全团队)在实施安全性方面都起着非常重要的作用,因此成功实施策略的关键是团队之间的协作。记住,安全是一种共同责任。让我们探讨每个阶段及您可以使用的构建策略的技术。

构建时安全性:向左移动

本节将指导您通过示例了解构建时安全性的各个方面。

映像扫描

在这个阶段,您需要确保应用程序没有任何已公开的主要未修补问题,这些问题在国家漏洞数据库中被披露为常见的漏洞枚举 (CVEs),并且需要扫描应用程序代码和依赖项以查找利用程序和易受攻击的代码段。然后对构建和交付为容器的映像进行扫描,以检测未修补的严重或主要漏洞,这些漏洞被披露为 CVEs。通常通过将基础映像及其所有软件包与跟踪易受攻击软件包的数据库进行对比来执行此操作。为了实施扫描,您可以使用几种开源和商业工具。例如,Whitesource、Snyk、Trivy、Anchor,甚至像谷歌这样的云提供商都提供容器映像的扫描功能。我们建议您选择一个理解容器构建方式并不仅仅扫描主机操作系统,还扫描容器基础映像的扫描解决方案。考虑到 Kubernetes 部署的动态性质,保障 CI/CD 管道的安全非常重要;代码和映像扫描必须成为管道的一部分,并且必须检查从映像注册表交付的映像是否受到妥协。您需要确保对注册表的访问受到控制,以避免妥协。这个阶段的流行术语是“向左移动安全性”,也被称为“向左移动安全性”。

主机操作系统的加固

在这里,您必须确保被部署的应用程序仅限于在其部署的主机上具有所需的权限。为此,您应该使用支持控制以仅允许限制应用程序对系统调用和文件系统访问的必要权限的强化主机操作系统。这样可以有效地减轻与“特权升级”相关的攻击,即利用容器中部署的软件中的漏洞来访问主机操作系统。

减少攻击面:基础容器镜像

我们建议您审查容器镜像的组成,并最小化构成基础镜像的软件包,只包括您的应用程序运行所绝对必需的软件包。在基于 Dockerfile 的容器镜像中,您可以从一个父镜像开始,然后将您的应用程序添加到镜像中以创建一个容器镜像。例如,您可以使用 FROM scratch 指令在 Docker 中构建一个基础镜像,这将创建一个最小的镜像。然后,您可以添加您的应用程序和必需的软件包,这将让您完全控制容器镜像的组成,并且有助于 CVE 管理,因为您无需担心在容器镜像中不需要的软件包中修补 CVE。如果构建一个 scratch 镜像不可行,您可以考虑从 distroless 镜像(一个精简的 Linux 发行版镜像)或 Alpine 最小镜像作为容器的基础镜像开始。

这些技术将帮助您设计和实施构建时安全策略。作为开发团队的一部分,您将负责与平台和安全团队合作设计和实施构建时安全,以确保与整体安全策略一致。我们提醒不要相信“左移安全”可以成为您整个安全策略的全部。这是不正确的,是保护工作负载的一种幼稚方法。还有其他几个重要方面,如部署和运行时安全,也需要作为安全策略的一部分考虑进去。

部署时安全

保护工作负载的下一阶段是保护部署。为了实现这一点,您需要加固您部署工作负载的 Kubernetes 集群。您需要详细审查 Kubernetes 集群配置,以确保与安全最佳实践保持一致。首先,建立一个信任模型来评估集群的各个组件。信任模型是一个框架,您在其中审查威胁概况并定义响应机制。您应该利用诸如基于角色的访问控制(RBAC)、标签分类法、标签治理和准入控制等工具来设计和实施信任模型。这些是控制资源访问、在资源创建时应用控制和验证的机制。这些主题在第三章、第四章和第七章中有详细覆盖。集群中的另一个关键组件是 Kubernetes 数据存储和 Kubernetes API 服务器,在设计这些组件的信任模型时,您需要特别注意访问控制和数据安全等细节。我们建议您使用强密码、公钥基础设施(PKI)进行访问以及传输层安全(TLS)进行数据传输加密。有关如何保护 Kubernetes API 和数据存储的详细信息,请参阅第二章。

您应该将部署关键任务负载的 Kubernetes 集群视为一个实体,然后为该实体设计信任模型。这要求您审查边界的安全控制,由于 Kubernetes 部署架构的复杂性,这将是一个具有挑战性的任务;我们将在下一节中详细介绍这一点。目前,让我们假设当前部署在边界的产品,如 Web 访问控制网关和下一代防火墙,对 Kubernetes 架构并不了解。我们建议您通过与这些设备建立集成来解决这个问题,使它们了解 Kubernetes 集群的上下文,从而能够有效地在边界应用安全控制。这样一来,您可以创建一个非常有效的安全策略,其中边界安全设备与 Kubernetes 集群内实施的安全控制协同工作。例如,假设您需要使这些设备了解您的工作负载的身份(IP 地址、TCP/UDP 端口等)。这些设备可以有效地保护构成您 Kubernetes 集群的主机,但在大多数情况下,它们无法区分单个主机上运行的工作负载。如果您在云提供商环境中运行,您可以使用安全组,这些是允许对一组节点(例如 Amazon Web Services 中的 EC2 实例)进行访问控制的虚拟防火墙,这些节点承载工作负载。安全组比传统防火墙和安全网关更符合 Kubernetes 架构;然而,即使是安全组也不了解集群内运行工作负载的上下文。

总结一下,当您考虑部署时间安全性时,您需要为 Kubernetes 集群实施信任模型,并与保护集群的边界安全设备建立有效的集成。

运行时安全性

现在您已经制定了保护构建和部署阶段的策略,需要考虑运行时安全性。术语运行时安全性用于描述保护 Kubernetes 集群的各个方面,例如在运行软件的主机上,但任何保护主机和工作负载免受未经授权活动的配置(例如系统调用、文件访问)也被称为运行时安全性。第四章将详细介绍主机和工作负载的运行时安全性。在本节中,我们将专注于确保 Kubernetes 集群网络安全运行所需的安全最佳实践。Kubernetes 是一个编排器,可以在主机网络上部署工作负载和应用程序。您必须将网络安全性视为运行时安全性的一个非常重要的方面。

Kubernetes 承诺提升灵活性和更有效地利用计算资源,相比于静态分区和服务器或虚拟机的预配。它通过在集群中动态调度工作负载来实现这一点,考虑每个节点上的资源使用情况,并在平面网络上连接工作负载。默认情况下,当部署新工作负载时,相应的 Pod 可以被调度到集群中的任何节点,并使用 Pod IP 地址中的任何 IP 地址。如果稍后将 Pod 重新调度到其他地方,则通常会获得不同的 IP 地址。这意味着 Pod IP 地址需要视为临时的。Pod IP 地址或其在网络中的位置没有长期或特殊的意义。

现在考虑传统的网络安全方法。在企业网络中,网络安全通常使用安全设备(或设备的虚拟版本),如防火墙和路由器来实现。这些设备强制执行的规则通常基于网络的物理拓扑结构以及为不同类别的工作负载分配特定 IP 地址范围的组合。

由于 Kubernetes 基于平面网络,并且 Pod IP 地址没有任何特殊含义,很少有传统设备能够提供有意义的工作负载感知的网络安全,而是不得不将整个集群视为单个实体。此外,在同一节点上托管的两个 Pod 之间的东西流量甚至不会经过底层网络。因此,这些设备根本看不到这些流量,基本上只能限制于北向安全,即保护从外部源进入集群的流量以及从集群内部源到外部源的流量。

鉴于这一切,很明显 Kubernetes 需要一种新的网络安全方法。这种新方法需要涵盖广泛的考虑因素,包括:

  • 新方法来执行网络安全(允许哪些工作负载与哪些其他工作负载通信),不依赖于 IP 地址或网络拓扑的特殊含义,并且即使流量不经过底层网络也能工作;Kubernetes 网络策略旨在满足这些需求。

  • 新工具帮助管理支持新开发流程和微服务愿景的网络策略,以增加组织的灵活性,例如策略推荐、策略影响预览和策略分阶段。

  • 新方法来监视和可视化网络流量,涵盖集群范围的整体视图(例如如何轻松查看整体网络和集群的网络安全状态)以及针对微服务序列进行深入分析以帮助排除故障或诊断应用程序问题的有针对性拓扑视图。

  • 实施入侵检测和威胁防御的新方法,包括违规策略警报、网络异常检测和集成威胁源。

  • 新的修复工作流程,以便在法医调查期间可以快速安全地隔离潜在受损的工作负载。

  • 审计配置和策略变更以符合合规要求的新机制。

  • 审计配置和策略变更的新机制,以及 Kubernetes 感知的网络流日志以满足合规性要求(因为传统的网络流日志是基于 IP 的,在 Kubernetes 上下文中长期意义不大)。

我们将审查一个典型的企业 Kubernetes 部署示例,以了解这些挑战。 图 1-2 是多云环境中 Kubernetes 和微服务的常见部署模型的表示。多云环境是指企业在多个云服务提供商(如亚马逊网络服务、谷歌云等)上部署 Kubernetes 的情况。混合云环境指企业在至少一个云服务提供商的环境中部署 Kubernetes,并且在其数据中心内部署 Kubernetes。大多数企业都有双云战略,并将在亚马逊网络服务(AWS)、微软 Azure 或谷歌云上运行集群;更多企业还在其数据中心运行一些传统应用程序。数据中心的工作负载可能位于安全网关后,该网关通过过滤进入的流量来保护外围。在这些 Kubernetes 部署中运行的微服务也可能对以下一种或多种依赖有所依赖:

  • 其他云服务,如 AWS RDS 或 Azure DB

  • Twilio 等第三方 API 终端

  • SaaS 服务,如 Salesforce 或 Zuora

  • 数据库或在数据中心内运行的传统应用程序

数据中心的工作负载可能位于安全网关后,该网关通过过滤进入的流量来保护外围。

可观察性 在 Kubernetes 中是通过收集的度量标准获取有关 Kubernetes 状态的可行见解的能力(稍后详述)。虽然可观察性还有其他应用,比如监视和故障排除,但在网络安全的背景下也很重要。将应用于流日志的可观察性概念与其他 Kubernetes 元数据(Pod 标签、策略、命名空间等)相关联,用于监视(然后保护)Kubernetes 集群中的 Pod 之间的通信,通过比较 IP 地址与已知恶意 IP 地址来检测恶意活动,并使用基于机器学习的技术来检测恶意活动。这些主题将在下一节中详细介绍。正如您在 图 1-2 中所见,Kubernetes 的部署由于每个集群中的数据孤立以及将一个集群中的工作负载与另一个集群或外部服务的工作负载关联起来可能导致的可见性损失而面临挑战。

图 1-2. 企业中 Kubernetes 部署示例

如图 1-2 所示,微服务应用程序的足迹通常延伸到虚拟私有云(VPC)边界之外,保护这些应用程序需要一种不同于传统周界安全方法的方法。它是网络安全控制、可观察性、威胁防御和企业安全控制的组合。接下来我们将详细介绍每一个。

网络安全控制

云提供商提供的本地安全控制(例如 AWS 安全组或 Azure 网络安全组)或位于 VPC 或数据中心周边的安全网关(例如下一代防火墙)并不理解 Kubernetes 集群内微服务的身份。例如,您不能使用安全组规则或防火墙策略来过滤与 Kubernetes pod 或服务之间的流量。此外,从 pod 发送的流量到达云提供商的网络或第三方防火墙时(根据云提供商的架构),流量会应用源网络地址转换(SNAT)。换句话说,从节点上所有工作负载的流量的源 IP 地址都设置为节点 IP,因此,最多只能具有节点级别(节点的 IP 地址)的允许/拒绝策略粒度。

Kubernetes 工作负载具有高度动态和短暂的特性。假设开发人员提交了一个特定工作负载的新检入。自动化的 CI/CD 工作流将启动,构建一个新版本的 pod(容器),并开始在 Kubernetes 集群中部署这个新版本的工作负载。Kubernetes orchestrator 将进行滚动升级并部署工作负载的新实例。所有这些都是自动化进行的,没有手动或带外工作流重新配置新部署工作负载的空间。

你需要一种新的安全架构来保护在多云或混合云基础设施中运行的工作负载。就像你在 Kubernetes 集群中部署工作负载一样,工作负载的安全性必须以代码形式定义,采用声明性模型。安全控制必须能够跨 Kubernetes 发行版、云、基础设施和/或网络进行移植。这些安全控制必须随着工作负载一起移动,因此,如果工作负载的新版本在 Amazon Elastic Kubernetes Service(EKS)的 VPC 上部署,而不是在本地集群上部署,您可以放心,与服务相关的安全控制将无缝执行,而无需重新设计任何网络拓扑、超出带外配置的安全组或 VPC/周界防火墙。

网络安全控制通过使用 Kubernetes 原生的网络策略解决方案实施,提供精细的访问控制。有几种众所周知的网络策略实现(例如 Calico,Weave Net,Kube-router,Antrea)可供选择使用。除了在第 3/第 4 层(TCP/IP)应用策略外,我们建议您查看支持应用层策略(例如 HTTP/HTTPS)的解决方案。我们还建议选择基于流行的代理 Envoy 的解决方案,因为它广泛用于应用层策略。Kubernetes 支持将应用程序部署为微服务(服务应用程序功能的小组件),分布在节点网络上。微服务之间的通信依赖于应用程序协议如 HTTP。因此,有必要实施可以通过应用层策略实现的精细应用程序控制。例如,在三层应用程序中,前端微服务可能只允许与后端数据库微服务使用基于 HTTP GET 的请求(读访问),而不允许与后端数据库微服务使用 HTTP POST(写访问)。所有这些请求最终可以使用相同的 TCP 连接,因此必须添加支持所述应用层控制的策略引擎是至关重要的。

企业安全控制

现在您已经定义了网络访问控制和可观察性策略,应考虑其他重要且在企业中普遍存在的安全控制措施。在数据传输中加密是安全和合规的关键要求。有几种选择可以考虑使用传统方法进行加密,比如在您的工作负载中使用基于 TLS 的加密;双向 TLS,它是服务网格平台的一部分;或者基于 VPN 的方法,比如提供基于加密密钥的 VPN 的 Wireguard。

我们建议您利用观察策略的数据收集来生成所需的报告,以帮助满足 PCI、HIPAA、GDPR 和 SOC 2 等标准的合规要求。您还应考虑确保持续合规性的能力,可以利用 Kubernetes 的声明性特性来协助设计和实施持续合规性。例如,您可以通过使用 Pod 的合规性状态来响应未通过合规性检查的 Pod,触发必要的操作以纠正情况(触发镜像更新)。

威胁防御

Kubernetes 集群中的威胁防御是查看集群中的恶意活动并防御其的能力。恶意活动允许对手未经授权访问并操纵或窃取 Kubernetes 集群中的数据。恶意活动可以采用多种形式,例如利用不安全的配置或利用应用程序流量或应用程序代码中的漏洞。

在构建威胁防御策略时,您必须考虑入侵检测和预防两个方面。入侵检测的关键在于可观察性;您需要回顾收集的数据以扫描已知威胁。在 Kubernetes 部署中,由于您需要检查的数据量很大,数据收集非常具有挑战性。我们经常听到这样的问题:“我需要一个 Kubernetes 集群来收集数据以保护 Kubernetes 集群吗?”答案是“不需要”。我们建议您将可观察性策略与入侵检测保持一致,并利用智能聚合来收集和检查数据。例如,您可以考虑使用一种工具,将数据聚合为“类似”pod 组,这些 pod 在给定目标端口和协议上相互通信,而不是使用传统的五元组(源 IP、源端口、目标 IP、目标端口、协议)聚合方法。这种方法将有助于显著减少收集的数据量,而不会牺牲效果。请记住,运行相同容器镜像并以相同方式部署的多个 pod 将为交易生成相同的网络流量。您可能会问,“如果只有一个实例被感染怎么办?如何检测到?”这是一个很好的问题。有几种方法可以解决。您可以选择一个支持基于各种指标(如连接、字节和数据包)收集的机器学习的工具来检测异常工作负载。另一种方法是使用工具,作为收集的一部分检测和匹配来自知名威胁源的已知恶意 IP 和域名,或记录未聚合的由策略拒绝的网络流量。这些都是帮助您构建策略的简单技术。请注意,威胁防御技术在不断发展,您需要与安全研究团队合作,帮助理解您的应用程序并构建威胁模型,以实施您的威胁防御策略。

可观察性

可观察性对于监控和保障像 Kubernetes 这样的分布式系统非常有用。Kubernetes 抽象了很多细节,在监视这样的系统时,您不能单独收集和独立基线化和监视各个指标(如单个网络流,pod 创建/销毁事件或一个节点上的 CPU 峰值)。需要的是一种在 Kubernetes 上下文中监视这些指标的方法。例如,一个与服务或部署相关联的 pod 被重新启动,并作为其对等体的不同二进制运行,或者与部署中的其他 pod 的活动(网络,文件系统,内核系统调用)不同。当考虑由多个服务(微服务)组成的应用时,情况变得更加复杂,这些服务又由多个 pod 支持。

可观察性在故障排除和监控 Kubernetes 工作负载的安全性方面非常有用。例如,在 Kubernetes 服务的上下文中,可观察性将允许您执行以下操作:

  • 将您的 Kubernetes 集群可视化为服务图,显示 Pod 如何与服务相关联及服务之间的通信流量。

  • 在服务图上叠加应用程序(第 7 层)和网络流量(第 3/第 4 层)作为独立层,这将使您能够轻松确定应用程序和底层网络的流量模式和流量负载。

  • 查看部署有 Pod 的节点的元数据(例如 CPU、内存或主机操作系统详情)。

  • 查看与 Pod 操作、流量负载、应用延迟(例如 HTTP 持续时间)、网络延迟(网络往返时间)或 Pod 操作(例如 RBAC 策略、服务账户或容器重启)相关的指标。

  • 查看给定服务(支持该服务的 Pod)的 DNS 活动(DNS 响应代码、延迟、负载)。

  • 追踪跨多个服务进行通信的用户事务;这也被称为分布式跟踪

  • 查看给定服务与外部实体的网络通信。

  • 查看与给定服务相关的 Pod 和资源的 Kubernetes 活动日志(例如审计日志)。

我们将在后续章节详细介绍可观测性的细节及其如何帮助安全性。在本讨论中,我们将简要描述如何将可观测性作为安全策略的一部分使用。

网络流量可见性

如前所述,需要一个解决方案,它提供以服务级别汇总的网络流量,并提供如命名空间、标签、服务账户或网络策略等上下文,以充分监视集群中应用的活动和应用的访问控制。例如,报告 IP1 与 IP2 在端口 8080 上通信与报告标记为“前端”和“后端”的 Pod 在 Kubernetes 集群中的特定端口或流量模式之间的通信有显著差异。此报告将允许您审查来自外部实体的通信,并应用基于 IP 地址的威胁源来检测来自已知恶意 IP 地址的活动,甚至来自意外地理位置的流量。我们将在第十一章中详细介绍这些概念。

DNS 活动日志

域名系统(DNS)是将域名转换为 IP 地址的系统。在您的 Kubernetes 集群中,审查 DNS 活动日志至关重要,以检测意外活动,例如查询已知恶意域的情况、DNS 响应代码如 NXDOMAIN,以及 DNS 查询中字节和数据包的意外增加。我们将在第十一章中详细介绍这些概念。

应用程序流量可见性

我们建议您审查应用程序流量,以寻找异常活动,如意外的响应代码和罕见或已知的恶意 HTTP 头部(用户代理,查询参数)。在 Kubernetes 部署中,HTTP 是最常用的协议,因此重要的是与您的安全研究团队合作,监控 HTTP 流量以检测恶意流量。如果您使用其他应用程序协议(例如 Kafka、MySQL),同样需要进行相同的操作。

Kubernetes 活动日志

除了网络活动日志外,您还必须监视 Kubernetes 活动日志以检测恶意活动。例如,审查资源访问拒绝日志以及服务账户创建/修改。审查命名空间创建/修改日志以寻找意外活动。并审查记录对 Kubernetes API 请求的审计日志。

机器学习/异常检测

机器学习是一种技术,系统能够通过数据在一段时间内推导出模式。输出结果是一个机器学习模型,然后可以用于预测和基于预测检测实际数据中的偏差。我们建议您考虑将基于机器学习的异常检测应用于各种指标,以检测异常活动。一种简单有效的方法是对单独的指标应用称为基线设定的机器学习技术。这样您就不需要担心为每个指标应用规则和阈值;系统会为您执行这些操作并将偏差报告为异常。将机器学习技术应用于网络流量是一个相对新的领域,并且正在获得安全团队的关注。我们将在第六章详细讨论这个主题。

对于 Kubernetes 的可观测性策略,您可以选择许多解决方案(如 Datadog、Calico Enterprise、来自 Google、AWS、Azure 的基于云提供商的解决方案)。

安全框架

最后,我们希望让您了解安全框架,这些框架为行业提供了安全最佳实践的共同方法和术语。安全框架是理解攻击技术和最佳实践以防御和减轻攻击的良好途径。您应该使用它们来构建和验证您的安全策略。请注意,这些框架可能不特定于 Kubernetes,但它们提供了有关对手在攻击中使用的技术的见解,安全研究人员需要审查并确定它们是否与 Kubernetes 相关。我们将回顾两个知名框架——MITRE 和 Kubernetes 威胁矩阵。

MITRE

MITRE 是基于对网络攻击的实际观察而建立的对手战术和技术知识库。企业版 MITRE ATT&CK® 矩阵非常有用,因为它为每个网络安全杀伤链的阶段提供了分类的战术和技术。杀伤链描述了网络攻击的各个阶段,对于构建有效的防御措施非常有用。MITRE 还提供了针对 AWS、Google Cloud 和 Microsoft Azure 等云环境的攻击矩阵。

图 1-3 描述了适用于 AWS 的 MITRE ATT&CK® 矩阵。我们建议您查看攻击矩阵中描述的每个阶段,以便构建您的威胁模型,保护您的 Kubernetes 集群。

图 1-3. AWS 云环境攻击矩阵

Kubernetes 的威胁矩阵

另一个框架是一种威胁矩阵,它是对通用 MITRE 攻击矩阵的 Kubernetes 特定应用。这是微软团队基于安全研究和实际攻击发布的。这是另一个优秀的资源,用于构建和验证您的安全策略。

图 1-4 提供了与您的 Kubernetes 集群相关的各个阶段。它们映射到我们在本章讨论的各个阶段。例如,您应该考虑初始访问阶段中注册表中的受损镜像,特权升级阶段中的访问云资源,以及构建、部署和运行时安全性的侧向移动阶段中的集群内部网络。

图 1-4. Kubernetes 的威胁矩阵

安全性和可观察性

在像 Kubernetes 这样的动态环境中,通过同时考虑安全性和可观察性,可以实现应用程序的安全部署。例如,你需要“观察”你的集群,找到实施控制以保护集群的最佳方式。作为编排引擎,Kubernetes 具有强大的采用率,因为它具有声明性的特性,允许用户指定更高级别的结果。Kubernetes 还具有内置功能,以确保您的集群按照规范运行。它通过监视各种属性并采取行动(例如,重新启动 pod)来实现这一点,如果属性偏离指定值一段时间,就会采取行动。Kubernetes 的这些方面使得实施确保集群安全所需的可见性和控制变得困难。你实施的控制需要与 Kubernetes 运营保持一致。因此,在考虑向 Kubernetes 添加任何控制之前,了解上下文非常重要——例如,你不能通过应用不允许其与其他任何元素通信的策略来隔离一个 pod。Kubernetes 将检测到 pod 无法与其他元素(例如,API 服务器)通信,确定 pod 未按规定操作,然后重新启动并在集群的其他位置启动 pod。

你需要做的第一件事是先了解 pod 的运行方式,了解其预期操作是什么,然后应用控制或检测意外事件。之后,你需要确定意外事件是运营问题还是安全问题,然后应用所需的补救措施。为了做到这一点,可观察性和安全性需要并重:你观察以了解预期情况并应用控制以确保预期操作,然后观察以检测意外事件并分析它们,然后添加必要的控制以补救由事件引起的任何问题。因此,在考虑保护你的集群时,你需要一种全面的安全和可观察性方法。

结论

到目前为止,你应该对 Kubernetes 安全性和可观察性的高层概述有了了解。这些是支撑整本书的基本概念。简而言之:

  • Kubernetes 的安全性与传统安全性非常不同,并且在工作负载部署的所有阶段——构建、部署和运行时都需要一种全面的安全性和可观察性方法。

  • Kubernetes 是声明性的,并抽象了工作负载操作的细节,这意味着工作负载可以在节点网络的任何地方运行。此外,工作负载可能是短暂的,它们会在不同节点上被销毁并重新创建。保护这样的声明式分布式系统需要你在所有阶段考虑安全性。

  • 我们希望你理解在设计和实施全面安全方法时,应用程序、平台和安全团队之间合作的重要性。

  • MITRE和 Kubernetes 的威胁矩阵是广泛被安全团队采纳的两个安全框架。

重要的是要将所有内容整合在一起,因为成功的安全和可观察性策略是一体化的。在接下来的章节中,我们将涵盖基础设施安全。

第二章:基础设施安全

许多 Kubernetes 配置在默认情况下是不安全的。在本章中,我们将探讨如何在基础设施级别保护 Kubernetes。通过加固主机使托管 Kubernetes 的服务器或虚拟机更加安全,加固集群以保护 Kubernetes 控制平面组件,并进行网络安全设置以将集群与周围基础设施集成,可以使其更加安全。请注意,本章讨论的概念适用于自托管 Kubernetes 集群以及托管 Kubernetes 集群。

主机加固

这涵盖了操作系统的选择,避免在主机上运行非必要进程以及基于主机的防火墙设置。

集群加固

这涵盖了一系列配置和策略设置,用于加固控制平面,包括配置 TLS 证书,锁定 Kubernetes 数据存储,加密静态密钥,凭证轮换,以及用户认证和访问控制。

网络安全

这涵盖了安全地将集群与周围基础设施集成,特别是允许集群与周围基础设施之间的网络交互,用于控制平面、主机和工作负载流量。

让我们详细看看每个方面的细节,并探讨构建安全基础设施以保护您的 Kubernetes 集群所需的内容。

主机加固

安全的主机是安全 Kubernetes 集群的重要基石。当您考虑主机时,它是在构成 Kubernetes 集群的工作负载背景下。现在我们将探讨确保主机具有强大安全姿态的技术。

操作系统的选择

许多企业在其所有基础设施上标准化使用单一操作系统,这意味着可能已经为您做出了选择。但是,如果有选择操作系统的灵活性,那么考虑专为容器设计的现代不可变 Linux 发行版是值得的。这些发行版有以下优势:

  • 它们通常具有更新的内核,包括最新的漏洞修复以及最新技术(如 eBPF)的实现,这些技术可以被 Kubernetes 网络和安全监控工具利用。

  • 它们设计为不可变,这在安全方面带来了额外的好处。在此上下文中,不可变性意味着根文件系统被锁定,应用程序无法更改。只能使用容器安装应用程序。这将应用程序与根文件系统隔离,并显著减少了恶意应用程序破坏主机的能力。

  • 它们通常包括自动更新到较新版本的能力,上游版本已准备好快速发布以解决安全漏洞。

设计用于容器的现代不可变 Linux 发行版的两个流行示例是 Flatcar Container Linux(最初基于 CoreOS Container Linux)和 Bottlerocket(最初由亚马逊创建和维护)。

无论您选择哪种操作系统,监视上游安全公告以便了解新发现的安全漏洞,并确保您有适当的流程来更新您的集群到更新版本以解决关键的安全漏洞,是一个良好的实践。根据您对这些漏洞的评估,您将需要决定是否将集群升级到操作系统的新版本。在考虑操作系统选择时,您还必须考虑来自主机操作系统的共享库,并了解它们对将部署在主机上的容器的影响。

另一个安全最佳实践是确保应用开发人员不依赖特定版本的操作系统或内核,因为这样将无法根据需要更新主机操作系统。

非必要进程

每个运行的主机进程都是黑客的潜在攻击向量。从安全的角度来看,最好是删除默认情况下可能正在运行的任何非必要进程。如果一个进程对于成功运行 Kubernetes、管理您的主机或保护您的主机没有必要,则最好不要运行该进程。如何禁用进程将取决于您的特定设置(例如,通过 systemd 配置更改或从 /etc/init.d/ 中删除初始化脚本)。

如果您正在使用针对容器优化的不可变 Linux 发行版,则已经消除了非必要进程,您只能作为容器运行额外的进程/应用程序。

防火墙主机化

为了进一步锁定托管 Kubernetes 的服务器或虚拟机,可以为主机本身配置本地防火墙规则,以限制允许与主机交互的 IP 地址范围和端口。

根据您的操作系统,可以使用传统的 Linux 管理工具,例如 iptables 规则或 firewalld 配置来实现这一点。重要的是确保这些规则与 Kubernetes 控制平面和您计划使用的 Kubernetes 网络插件兼容,以免阻塞 Kubernetes 控制平面、Pod 网络或 Pod 网络控制平面。正确配置这些规则并随时间保持其更新可能是一个耗时的过程。此外,如果使用不可变的 Linux 发行版,您可能无法直接使用这些工具。

幸运的是,一些 Kubernetes 网络插件可以帮助解决这个问题。例如,几个 Kubernetes 网络插件,如 Weave Net、Kube-router 和 Calico,包括应用网络策略的能力。你应该审查这些插件,并选择一个也支持将网络策略应用于主机本身(而不仅仅是 Kubernetes Pod)。这样可以大大简化集群中主机的安全性,并且基本上与操作系统无关,包括与不可变 Linux 发行版一起使用。

始终研究最新的最佳实践

随着安全研究社区确定新的漏洞或攻击向量,安全最佳实践随时间而演变。许多这些都有详细的在线文档,并且可以免费获取。

例如,互联网中心安全性维护了免费的 PDF 指南,详细说明了安全地配置许多常见操作系统所需的行动。称为 CIS 基准,它们是确保你覆盖了保护主机操作系统所需的许多重要行动的极好资源。你可以在他们的网站上找到 最新的 CIS 基准列表。请注意,还有针对 Kubernetes 的特定基准,我们将在本章后面讨论。

集群加固

Kubernetes 默认情况下是不安全的。因此除了加固组成集群的主机外,加固集群本身也非常重要。这可以通过 Kubernetes 组件和配置管理、身份验证和基于角色的访问控制(RBAC),以及保持集群更新到最新的 Kubernetes 版本来实现,以确保集群具有最新的漏洞修复。

保护 Kubernetes 数据存储

Kubernetes 使用 etcd 作为其主要数据存储。这是存储所有集群配置和期望状态的地方。访问 etcd 数据存储与在所有节点上拥有 root 登录权限基本上是等效的。如果恶意用户获取了对 etcd 数据存储的访问权限,几乎所有你在集群内实施的其他安全措施都将失效。他们将完全控制你的集群,包括在任何节点上以提升的权限运行任意容器的能力。

保护 etcd 的主要方法是使用 etcd 本身提供的安全功能。这些功能基于 x509 公钥基础设施(PKI),使用密钥和证书的组合。它们确保所有传输中的数据都使用 TLS 加密,并且所有访问都受到强密码的限制。最好为不同 etcd 实例之间的对等通信配置一组凭据(密钥对和证书),并为来自 Kubernetes API 的客户端通信配置另一组凭据。作为此配置的一部分,etcd 还必须配置证书颁发机构(CA)的详细信息,用于生成客户端凭据。

一旦正确配置了 etcd,只有拥有有效证书的客户端才能访问它。然后,您必须配置 Kubernetes API 服务器,使其能够使用客户端证书、密钥和证书颁发机构来访问 etcd。

您还可以使用网络级防火墙规则限制对 etcd 的访问,以便只能从 Kubernetes 控制节点(托管 Kubernetes API 服务器的节点)访问它。根据您的环境,可以使用传统防火墙、虚拟云防火墙或在 etcd 主机上设置规则(例如支持主机端点保护的网络策略实施)来阻止流量。这最好是作为深度防御策略的一部分,与使用 etcd 的安全功能一起使用,因为仅通过防火墙规则限制访问并不能解决加密 Kubernetes 敏感数据在传输中的安全需求。

除了为 Kubernetes 安全地访问 etcd,建议不要将 Kubernetes etcd 数据存储用于除 Kubernetes 之外的任何其他用途。换句话说,不要在数据存储中存储非 Kubernetes 数据,也不要让其他组件访问 etcd 集群。如果正在运行使用 etcd 作为数据存储的应用程序或基础架构(在集群内部或集群外部),最佳实践是为其设置单独的 etcd 集群。可以辩称的例外情况是,如果应用程序或基础架构具有足够的特权,以至于其数据存储的妥协也会导致 Kubernetes 的完全妥协。还非常重要的是要保持 etcd 的备份,并确保备份的安全性,以便能够从失败(例如升级失败或安全事件)中恢复。

保护 Kubernetes API 服务器的安全性

在 etcd 数据存储之上的另一层,需要保护的下一个关键是 Kubernetes API 服务器。与 etcd 一样,可以使用 x509 PKI 和 TLS 来实现安全性。如何以这种方式引导集群的详细步骤因您使用的 Kubernetes 安装方法而异,但大多数方法包括创建所需的密钥和证书并将其分发到其他 Kubernetes 集群组件的步骤。值得注意的是,某些安装方法可能为某些组件启用不安全的本地端口,因此重要的是熟悉每个组件的设置,以识别潜在的未安全流量,以便采取适当的安全措施。

在静止状态下加密 Kubernetes Secrets

Kubernetes 可以配置为加密存储在 etcd 中的敏感数据,例如 Kubernetes secrets。这样可以防止任何攻击者访问 etcd 或其离线副本(例如离线备份)时泄露这些机密。

默认情况下,Kubernetes 不会对存储的秘密进行加密,当启用加密时,它仅在将秘密写入 etcd 时进行加密。因此,在启用静态加密时,重写所有秘密(通过标准 kubectl apply 或 update 命令)以触发其在 etcd 中的加密至关重要。

Kubernetes 支持各种加密提供程序。根据加密最佳实践选择推荐的加密方法非常重要。主推的选择是基于 AES-CBC 和 PKCS #7 的加密。这使用 32 字节密钥提供非常强大的加密,并且相对较快。有两种不同的提供程序支持此加密:

  • 完全在 Kubernetes 中运行并使用本地配置密钥的本地提供程序。

  • 使用外部密钥管理服务(KMS)的 KMS 提供程序来管理密钥。

本地提供程序将其密钥存储在 API 服务器的本地磁盘上。因此,如果 API 服务器主机受到攻击,则所有您的秘密都会受到威胁。根据您的安全立场,这可能是可以接受的。

KMS 提供程序使用一种称为信封加密的技术。使用信封加密时,每个秘密都使用动态生成的数据加密密钥(DEK)进行加密。然后,DEK 使用由 KMS 提供的密钥加密密钥(KEK)进行加密,并将加密的 DEK 与 etcd 中加密的秘密一起存储。KEK 始终由 KMS 托管为中心的信任根,永远不会存储在集群中。大多数大型公共云提供商提供基于云的 KMS 服务,可用作 Kubernetes 中的 KMS 提供程序。对于本地集群,可以使用第三方解决方案,如 HashiCorp 的 Vault,作为集群的 KMS 提供程序。由于详细的实施方式有所不同,评估 KMS 如何验证 API 服务器以及 API 服务器主机的妥协是否可能威胁到您的秘密,因此仅能提供有限的优势,相比本地加密提供程序。

如果预计有异常高量的加密存储读/写操作,则使用 secretbox 加密提供程序可能会更快。但是,secretbox 是一种较新的标准,在撰写时的审查较少。因此,在需要高水平审查的环境中,可能不被认为是可接受的。此外,KMS 提供程序尚不支持 secretbox,必须使用本地提供程序,将密钥存储在 API 服务器上。

加密 Kubernetes 秘密是最常见的必须在静态时加密要求,但请注意,如果需要,还可以配置 Kubernetes 以加密其他 Kubernetes 资源的存储。

值得注意的是,如果您有超出 Kubernetes secrets 能力范围的需求,可以使用第三方的秘密管理解决方案。其中一个这样的解决方案,已被提及作为 Kubernetes 中信封加密的潜在 KMS 提供者,是 HashiCorp 的 Vault。除了为 Kubernetes 提供安全的秘密管理外,Vault 还可以在企业范围内更广泛地管理秘密,如果需要的话。Vault 还是早期 Kubernetes 版本中填补重要差距的非常受欢迎的选择,这些版本不支持加密处于静态状态的秘密。

频繁轮换凭据

频繁地轮换凭据使攻击者更难利用可能获得的任何被泄露的凭据。因此,设置任何 TLS 证书或其他凭据的短寿命并自动化其轮换是最佳实践。大多数身份验证提供程序可以控制签发证书或服务令牌的有效期多长时间,尽可能使用短的生命周期是最佳的,例如每天轮换密钥或如果特别敏感则更频繁。这需要包括用于外部集成或作为集群引导过程的任何服务令牌。

完全自动化凭据的轮换可能需要定制的 DevOps 开发工作,但通常与尝试定期手动轮换凭据相比,代表了一个很好的投资。

在对存储在静态状态下的 Kubernetes secrets 进行密钥轮换时(如前一节讨论的),本地提供程序支持多个密钥。第一个密钥始终用于加密任何新的秘密写入。对于解密,密钥按顺序尝试,直到成功解密秘密。由于密钥仅在写入时进行加密,因此重写所有秘密(通过标准的 kubectl apply 或更新命令)以触发使用最新密钥进行加密是很重要的。如果秘密的轮换是完全自动化的,则写入将作为此过程的一部分而发生,无需单独步骤。

当使用 KMS 提供者(而不是本地提供者)时,可以轻松地轮换 KEK 而无需重新加密所有秘密,这可以减少重新加密大量大小适当的秘密时的性能影响。

认证和 RBAC

在前面的部分中,我们主要关注在集群内部保障程序化/代码访问的安全性。同样重要的是要遵循最佳实践,以保障用户与集群的交互。这包括为每个用户创建单独的用户账户,并使用 Kubernetes RBAC 为用户授予他们执行角色所需的最小访问权限,遵循最小特权访问原则。通常最好使用组和角色来实现这一点,而不是为个别用户分配 RBAC 权限。这样做可以更轻松地随着时间调整不同用户组的权限,同时减少定期审查/审核集群中用户权限是否正确和最新的工作量。

Kubernetes 对于用户有限的内置认证能力,但可以与大多数外部企业认证提供者集成,如公共云提供商的 IAM 系统或本地认证服务,可以直接或通过第三方项目(如最初由 CoreOS 创建的 Dex)。通常建议与其中一个外部认证提供者集成,而不是使用 Kubernetes 基本认证或服务账户令牌,因为外部认证提供者通常具有更友好的凭据轮换支持,包括指定密码强度和轮换频率时间段的能力。

限制云元数据 API 访问

大多数公共云提供了一个可从每个主机/VM 实例本地访问的元数据 API。这些 API 提供对实例的云凭据、IAM 权限和实例可能具有的其他潜在敏感信息的访问。默认情况下,这些 API 可由运行在实例上的 Kubernetes Pod 访问。任何被攻击的 Pod 都可以使用这些凭据提升其在集群内的预期特权级别,或者访问实例可能具有权限访问的其他云提供商托管服务。

解决这个安全问题的最佳实践是:

  • 根据云提供商的推荐机制提供任何必需的 Pod IAM 凭据。例如,Amazon EKS 允许您为服务账户分配唯一的 IAM 角色,Microsoft Azure 的 AKS 允许您为 Pod 分配托管标识,Google Cloud 的 GKE 允许您通过工作负载标识分配 IAM 权限。

  • 限制每个实例的云权限至最低限度,以减少从实例访问元数据 API 的任何被攻击影响。

  • Use network policies to block pod access to the metadata API. This can be done with per-namespace Kubernetes network policies, or preferably with extensions to Kubernetes network policies such as those offered by Calico, which enable a single network policy to apply across the whole of the cluster (without the need to create a new Kubernetes network policy each time a new namespace is added to the cluster). This topic is covered in more depth in the Default Deny and Default App Policy section of Chapter 7.

Enable Auditing

Kubernetes auditing provides a log of all actions within the cluster with configurable scope and levels of detail. Enabling Kubernetes audit logging and archiving audit logs on a secure service is recommended as an important source of forensic details in the event of needing to analyze a security breach.

The forensic review of the audit log can help answer questions such as:

  • What happened, when, and where in the cluster?

  • Who or what initiated it and from where?

In addition, Kubernetes audit logs can be actively monitored to alert on suspicious activity using your own custom tooling or third-party solutions. There are many enterprise products that you can use to monitor Kubernetes audit logs and generate alerts based on configurable match criteria.

The details of what events are captured in Kubernetes audit logs are controlled using policy. The policy determines which events are recorded, for which resources, and with what level of detail.

Each action being performed can generate a series of events, defined as stages:

RequestReceived

Generated as soon as the audit handler receives the request

ResponseStarted

Generated when the response headers are sent, but before the response body is sent (generated only for long-running requests such as watches)

ResponseComplete

Generated when the response body has been completed

Panic

Generated when a panic occurs

The level of detail recorded for each event can be one of the following:

None

Does not log the event at all

Metadata

Logs the request metadata (user, timestamp, resource, verb, etc.) but not the request details or response body

Request

Logs event metadata and the request body but not the response body

RequestResponse

Logs the full details of the event, including the metadata and request and response bodies

Kubernetes’ audit policy is very flexible and well documented in the main Kubernetes documentation. Included here are just a couple of simple examples to illustrate.

To log all requests at the metadata level:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

To omit the RequestReceived stage and to log pod changes at the RequestResponse level and configmap and secrets changes at the metadata level:

apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
omitStages:
  - "RequestReceived"
rules:
  - level: RequestResponse
    resources:
    - group: ""
      resources: ["pods"]
  - level: Metadata
    resources:
    - group: "" # core API group
      resources: ["secrets", "configmaps"]

第二个示例说明了审计日志中敏感数据的重要考虑因素。根据对审计日志访问安全性的要求,确保审计策略不记录机密或其他敏感数据可能是至关重要的。就像对静止状态的加密密码的做法一样,通常建议始终从审计日志中排除敏感细节。

限制对 alpha 或 beta 功能的访问

每个 Kubernetes 发布版本都包含 alpha 和 beta 功能。可以通过为各个 Kubernetes 组件指定功能门标志来控制它们的启用。由于这些功能正在开发中,它们可能存在限制或错误,从而导致安全漏洞。因此,良好的做法是确保所有不打算使用的 alpha 和 beta 功能都被禁用。

Alpha 功能通常(但并非总是)默认禁用。它们可能存在错误,并且该功能的支持可能会在不向后兼容的情况下发生根本性变化,或在未来的版本中被删除。一般建议仅将其用于测试集群,而不是生产集群。

Beta 功能通常默认启用。它们在版本发布之间可能会以不向后兼容的方式更改,但通常被认为经过合理测试且安全可用。但与任何新功能一样,它们天生更有可能存在漏洞,因为它们的使用较少且审核较少。

评估 alpha 或 beta 功能可能提供的价值,对比其带来的安全风险,以及在发布版本之间功能非向后兼容变更可能带来的操作风险是非常重要的。

频繁升级 Kubernetes

在任何大型软件项目中,随着时间的推移,新的漏洞是不可避免的。Kubernetes 开发者社区在及时响应新发现的漏洞方面表现良好。严重漏洞通常在封禁期间修复,这意味着漏洞的知识不会在开发者有时间制定修复方案之前公开。随着时间的推移,较旧的 Kubernetes 版本中已公开已知漏洞的数量增加,这可能会增加较旧集群的安全风险。

为减少集群被妥协的风险,定期升级集群非常重要,并确保能够紧急升级以应对发现的严重漏洞。

所有 Kubernetes 安全更新和漏洞报告(一旦有任何封禁结束)通过公开且免费加入的 kubernetes-announce 邮件组。强烈建议任何希望跟踪已知漏洞以最小化安全风险的人加入此组。

使用托管的 Kubernetes 服务

减少执行本章建议所需工作的一种方式是使用主要的公共云托管 Kubernetes 服务之一,例如 EKS、AKS、GKE 或 IKS。使用这些服务将安全性从完全由您自己负责减少到共享责任模型。共享责任意味着服务默认包含了许多安全元素,或者可以轻松配置以支持这些安全元素,但您仍然需要自己负责某些元素,以确保整体安全。

具体细节因您使用的公共云服务而异,但毫无疑问,所有这些服务都会做大量的重活,这降低了与自行安装和管理集群相比所需的安全工作量。此外,公共云提供商和第三方提供了大量资源,详细说明了作为共享责任模型的一部分,您需要采取哪些措施来全面确保您的集群安全。例如,其中一个资源就是 CIS 基准,接下来将讨论它。

CIS 基准

正如本章前面讨论的那样,CIS 维护了免费的 PDF 指南,提供了对许多常见操作系统进行安全配置指导。这些指南称为 CIS 基准,可帮助您进行主机硬化,是一种无价的资源。

除了帮助进行主机硬化外,还有针对 Kubernetes 本身的 CIS 基准,包括对许多流行的托管 Kubernetes 服务的配置指导,这可以帮助您实施本章指南中的大部分内容。例如,GKE CIS 基准包括指导如何确保集群配置了节点的自动升级,并且使用 Google Groups 管理 Kubernetes 身份验证和 RBAC。这些指南是高度推荐的资源,以获取关于如何确保 Kubernetes 集群安全所需步骤的最新实用建议。

除了指南本身外,还有第三方工具可用来评估运行中的集群与许多这些基准的状态。一个流行的工具是 kube-bench,这是一个开源项目,最初由 Aqua Security 团队创建。或者,如果您更喜欢一个更成熟的解决方案,那么许多企业产品在其集群管理仪表板中内置了 CIS 基准和其他安全合规工具和警报功能。在理想情况下,这些工具会定期自动运行,有助于验证集群的安全姿态,并确保在集群创建时可能采取的谨慎安全措施在管理和更新集群的过程中不会意外丢失或被破坏。

网络安全

当在集群与集群范围之外的周围基础设施安全地集成时,网络安全是首要考虑因素。有两个方面需要考虑:如何保护集群免受集群外部的攻击,以及如何保护集群内部任何受损元素对集群外部基础设施的影响。这适用于集群工作负载级别(即 Kubernetes pod)和集群基础设施级别(即 Kubernetes 控制平面以及运行 Kubernetes 集群的主机)。

首先要考虑的是集群是否需要从公共互联网访问,直接(例如,一个或多个节点具有公共 IP 地址)或间接(例如,通过可从互联网访问的负载均衡器或类似设备)。如果集群可以从互联网访问,那么黑客的攻击或探测活动数量将大幅增加。因此,如果集群不需要强制要求从互联网访问,强烈建议在可路由级别上不允许任何访问(即确保互联网上的数据包无法到达集群)。在企业内部环境中,这可能涉及选择用于集群的 IP 地址范围及其在企业网络中的可路由性。如果使用公共云托管的 Kubernetes 服务,您可能会发现这些设置仅在集群创建时可以设置。例如,在 GKE 中,是否可以从互联网访问 Kubernetes 控制平面可以在集群创建时设置。

集群内的网络策略是下一道防线。网络策略可用于限制集群与集群外部基础设施之间的工作负载和主机通信。它具有两大优势:既能意识到工作负载(即限制构成单个微服务的一组 pod 的通信能力),又能在平台上不可知(即可以在任何环境中使用相同的技术和策略语言,无论是企业内部环境还是公共云中)。网络策略将在专门章节中深入讨论。

最后,强烈建议使用周边防火墙,或其云等效物,如安全组,以限制流向/流出集群的流量。在大多数情况下,这些防火墙不了解 Kubernetes 工作负载,因此无法理解单个 pod,并且通常仅能以集群整体的方式进行粒度控制。尽管存在这些限制,它们作为防御策略的一部分仍然具有价值,但仅靠它们本身可能不足以满足任何注重安全的企业需求。

如果需要更强的周边防御,有策略和第三方工具可以使周边防火墙或其云等效物更加有效:

  • 一种方法是将集群中的少数特定节点指定为具有特定访问级别的节点,而这种访问级别不授予集群中的其他节点。然后可以使用 Kubernetes 的污点(taints),确保只有需要该特殊访问级别的工作负载被调度到这些节点上。这样一来,可以基于特定节点的 IP 地址设置外围防火墙规则,允许所需的访问,而集群中的所有其他节点则被拒绝在集群外访问。

  • 在本地环境中,一些 Kubernetes 网络插件允许您使用可路由的 Pod IP 地址(非覆盖网络),并控制支持特定微服务的一组 Pod 使用的 IP 地址范围。这使得外围防火墙可以根据 IP 地址范围执行类似于传统非 Kubernetes 工作负载的操作。例如,您需要选择一个在本地环境中支持可路由的非覆盖网络,并具有灵活 IP 地址管理能力以促进此类方法的网络插件。

  • 在不实际将 Pod IP 地址路由到集群外部不可行的任何环境中(例如使用覆盖网络时),前述方法的变体非常有用。在这种情况下,从 Pod 发出的网络流量似乎来自于节点的 IP 地址,因为它使用源网络地址转换(SNAT)。为了解决这个问题,可以使用支持出口 NAT 网关的 Kubernetes 网络插件。一些 Kubernetes 网络插件支持出口 NAT 网关功能,允许更改此行为,使一组 Pod 的出口流量通过集群内执行 SNAT 的特定网关路由,因此流量似乎来自于网关,而不是托管 Pod 的节点。根据使用的网络插件,网关可以分配给特定的 IP 地址范围或特定节点,从而允许外围防火墙规则比将整个集群视为单个实体更为选择性地执行。支持此功能的几个选项包括:Red Hat 的 OpenShift SDN、Nirmata 和 Calico 都支持出口网关功能。

  • 最终,一些防火墙支持一些插件或第三方工具,使防火墙能够更加了解 Kubernetes 工作负载,例如通过自动填充防火墙中的 IP 地址列表,其中包括 pod 的 IP 地址(或者承载特定 pod 的节点的节点 IP 地址)。或者在云环境中,有自动编程规则允许安全组选择性地对进出 Kubernetes pod 的流量进行操作,而不仅仅是在节点级别操作。这种集成对于你的集群非常重要,可以帮助补充防火墙提供的安全保障。市场上有几种工具可以实现这种类型的集成。选择一个支持与主要防火墙供应商的集成并且对 Kubernetes 本地化的工具非常重要。

在前面的部分,我们讨论了网络安全的重要性,以及如何利用网络安全保护来自集群外部来源的流量访问你的 Kubernetes 集群,以及如何控制从集群内部源发往集群外部主机的访问。

结论

在本章中,我们讨论了以下关键概念,你应该使用这些概念来确保你的 Kubernetes 集群有一个安全的基础设施。

  • 你需要确保主机运行的操作系统是安全的,没有关键性漏洞。

  • 你需要在主机上部署访问控制以控制对主机操作系统的访问,并部署用于进出主机的网络流量的控制。

  • 你需要确保你的 Kubernetes 集群有一个安全的配置;保护数据存储和 API 服务器对于确保你拥有安全的集群配置至关重要。

  • 最后,你需要部署网络安全来控制源自集群内部 pod 并且要传输到集群内部 pod 的网络流量。

第三章:工作负载部署控制

在 Manoj Ahuje 的贡献下,

Tigera 的高级威胁情报研究工程师

一旦您决定了基础设施安全策略,下一个步骤是工作负载部署控制。在本章中,我们将探讨镜像构建和扫描策略,CI/CD(将镜像扫描集成到构建中),以及 Kubernetes 基于角色的访问控制(RBAC),这是一种广泛使用的授权系统,允许您根据用户角色定义访问控制,以及应用程序的密钥管理。

镜像构建和扫描

在本节中,我们将探讨镜像构建和扫描的最佳实践。这些包括选择基础映像以减少攻击面,使用 scratch 映像和镜像硬化最佳实践以防止对手入侵。镜像扫描涉及选择镜像扫描解决方案的细节,隐私问题,以及容器威胁分析解决方案的概述。

基础映像的选择

如在前一章节讨论的,您可以选择像 Bottlerocket 这样的现代 Linux 发行版作为容器的基础映像。传统 Linux 发行版的最小版本,如 Ubuntu、Red Hat 和 Alpine,也是可用的。

虽然使用最小映像作为起点是个不错的开始,但是最小映像方法并不能阻止发现存在于操作系统中的 OS 包的漏洞。在这种情况下,distroless 或 scratch 映像会是更好的选择。这些类型的映像仅包含应用程序及其特定的运行时依赖项。

这里是 distroless 或 scratch 映像的好处:

  • 这种策略显著减少了大小、攻击面和漏洞,从而提高了安全性。

  • Distroless 映像已经可以用于生产环境。Kubernetes 本身就使用 distroless 映像来构建各种组件,如 kublet、scheduler 等。

  • 如果您要为应用程序选择一个基础映像,可以使用多阶段 Dockerfile 来构建一个基础映像。第一阶段涉及构建您的应用程序。第二阶段涉及将运行时依赖和应用程序移到基础映像。

distroless 映像项目的最受欢迎的例子是来自 Google 的 distroless,它为 Java、Python 或 C++ 等各种运行时提供映像。

一个空白镜像从 Dockerfile 指令 FROM:scratch 开始,表示一个空文件系统。在 Dockerfile 中的下一条指令创建了容器镜像的第一个文件系统层。在这里,第一个文件系统层需要与应用程序和依赖项一起编译。因为在容器外部构建应用程序是不生产和不直观的,Docker 引入了多阶段构建。通过多阶段构建,Dockerfile 允许多个 FROM 指令。每个 FROM 指令创建一个独立的阶段,之前阶段的文件系统产物可以在后续阶段的构建中复制。这个机制使开发者能够在较早的阶段(构建器镜像)中构建和编译应用程序,所有依赖项都可用,并最终仅复制运行生产应用程序所需的文件系统产物到后续阶段。构建的最后阶段可以是一个空白镜像,只需要在结果镜像中存在应用程序二进制文件和依赖项。

下面是一个基于空白镜像的 bash 脚本示例,目标是在容器内运行一个脚本。在这里,你可以创建一个两阶段 Dockerfile,其中第二阶段是一个仅包含脚本依赖项的空白镜像。

你可以使用这个模板来容器化甚至是使用 Node.js、Python 和 Go 构建的复杂应用程序。Go 还提供了一个选项,可以将所有的运行时库编译到二进制文件中。在下面的示例中,你可以使用 Alpine 作为基础镜像,为运行脚本的容器构建一个空白镜像:

# use alpine 3 as base image
FROM alpine:3 as builder

# upgrade all alpine packages
RUN apk update && apk upgrade

# add your script into container fs
ADD your_init_script.sh your_init_script.sh
RUN chmod u+x your_init_script.sh

# stage 2
FROM scratch

# shell
COPY --from=builder /bin/sh /bin/sh

# dependent linux shared libraries
COPY --from=builder /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1

ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:
/sbin:/bin

ENTRYPOINT ["./your_init_script.sh"]
In this example, only two files are copied into scratch images (second stage). 
Hence, the alpine base image containing more than five thousand files is minimized 
to two files, reducing the attack surface significantly.

现在我们已经回顾了如何为你的容器选择基础镜像,让我们探讨一下容器镜像的强化。

容器镜像强化

容器镜像强化是构建镜像以减少安全弱点和攻击面的过程。同时,它被用来为容器内的应用程序添加防御层,以确保安全运行。

如果你使用一个未强化的容器镜像,可能会容易受到滥用、信息泄露或者更容易提升到容器主机的特权攻击。你应该利用以下工具和最佳实践来为你的应用程序构建强化的容器镜像:

  • 只使用来自受信任来源的基础镜像,比如官方 Ubuntu 和 Red Hat 发行渠道,并且使用发布信息双重检查镜像哈希,因为很容易在镜像中嵌入恶意代码,比如加密货币挖矿程序,并将这些镜像发布在像 Docker Hub 这样的仓库中。

  • 将基础镜像最小化,仅包含应用程序的运行时依赖项。

  • 遵循最小权限访问原则,并以最低所需权限运行容器。例如,除非需要 root 权限,否则应将容器作为非 root 用户运行。这使得攻击者难以逃离容器,并提供了对漏洞的保护,例如 CVE-2020-15257,其中 root 用户能够逃离容器。

  • 不要使用 Docker 镜像的标签;而是在 Dockerfile 中固定基础镜像版本(例如,ubuntu:20.08)。可变的标签如 latestmaster 不断更新以增加功能和修复问题,这可能导致在 CI/CD 流水线的镜像扫描中出现问题。此外,它们可能导致应用程序稳定性问题(其中底层依赖库被更新/移除或更改)。

  • 将 Docker 镜像层压缩为单个层。使用 Docker 或 buildah 等工具构建的容器镜像通常具有多个层。这些层显示了开发历史,有时会泄露敏感信息。压缩现有层的最佳方法是使用多阶段构建;还有一个实验性的 Docker 功能(即 Docker API 1.25+ 中提供的选项 --squash)。

  • 使用容器镜像签名来信任该镜像。原生的 Kubernetes 并不具备容器镜像验证功能。可以使用 Docker Notary 对镜像进行签名,并通过 Kubernetes 准入控制器验证镜像签名,以确定镜像是否被恶意行为者篡改(例如,在镜像位于注册表时对其进行更改)。

在接下来的部分,我们将审查容器镜像扫描。

容器镜像扫描解决方案

容器镜像扫描工具检查容器文件系统以获取元数据,了解镜像中是否存在易受攻击的组件。市场上有许多开源和商业企业解决方案可供选择,用于此目的。它们具有 CI/CD 集成和丰富的扫描功能。您选择的解决方案应该能够回答一些基本问题:

  • 镜像扫描器能否为您选择的基础镜像中的操作系统包进行扫描?

  • 它能否扫描您的应用程序依赖项(是否理解应用程序使用的语言,如 Go、Python、Node.js)?

  • 镜像扫描器能否检测文件系统中存在的敏感文件(如证书、密码)?

  • 误报率是多少?

  • 它可以扫描二进制文件(.elf 或 .exe)吗?

  • 扫描解决方案将收集哪些数据?扫描解决方案是否将您的镜像上传到其 SaaS 服务,还是仅收集包元数据?由于数据暴露风险,了解这一点非常重要。

  • 收集的数据将存储在哪里?在本地还是云端 SaaS?请查阅并选择符合安全/合规团队指南的选项。

  • 镜像扫描器是否与您的 CI/CD 系统集成?

大多数扫描器从文件系统收集元数据,并尝试与从国家漏洞数据库或私人情报来源收集的漏洞信息匹配,以确定漏洞的存在。请注意,扫描中可能会出现假阳性和假阴性。对于已确认存在漏洞的镜像,应用程序和安全团队需要共同分析 CVE 对运营的影响和风险。修复步骤涉及在更新可用时实施解决方法和修补镜像。

许多公共云提供商和容器注册服务提供商提供容器扫描服务。然而,它们支持的操作系统版本有限,并且大多数不扫描应用程序依赖关系。在这方面,开源世界提供了更多选择。一些可以扫描应用程序依赖关系的著名开源工具包括Anchore,允许用户定义策略,以及Trivy,易于集成到 CI 中。

隐私问题

产品中的安全漏洞及相关信息是高度机密的数据,如果落入错误的手中,可能对组织构成重大责任。因此,在选择任何解决方案之前,最好验证扫描解决方案收集了哪些数据以及这些数据存储在哪里(例如,在企业内部的本地或作为 SaaS 服务的一部分在云端)。如果购买商业解决方案,请检查合同以了解在数据泄露情况下的损害条款。通常,这些条款可以帮助您了解组织对数据安全的严肃态度。如果使用开源解决方案,请阅读文档以了解数据泄露风险。

容器威胁分析

除了传统的镜像扫描,使用基于沙箱的解决方案进行容器威胁分析的领域正在日益流行。这是一个相对较新的领域,我们建议您关注。这些基于沙箱的解决方案可以运行 Docker 镜像,并监视容器系统调用、进程、内存、网络流量(HTTP、DNS、SSL、TCP)以及容器的整体行为,利用机器学习和其他技术来检测任何恶意活动。此外,它们还可以扫描容器文件系统以检查漏洞和恶意二进制文件。这些技术可用于检测高级持续性威胁(APTs)和恶意软件。

CI/CD

在本节中,我们将讨论将镜像扫描解决方案集成到您的 CI/CD 流水线中的各种策略,保护 CI/CD 流水线的最佳实践,以及实施 CI/CD 和漏洞扫描的组织政策技术。

连续集成(CI)是一种开发实践,每个开发者的检入都会通过自动化构建进行验证,使团队能够早期检测问题。而连续部署(CD)是 CI 的延伸,一旦通过所有发布检查,变更就会发布给下游消费者。

在 CI/CD 的目标中,将安全性集成到开发和发布过程的每个步骤中是关键,这是 DevOps 流程中左移策略的一个重要部分。通过将图像扫描集成到您的 CI/CD 流水线中(见图 3-1),开发团队可以在开发者提交到代码库后立即获得检查结果。这种方法的主要优势在于可以在构建时检测到新的安全漏洞或威胁。一旦发现问题,所有利益相关者和 DevOps 团队通常会通过 CI 作业失败收到通知。然后各团队可以立即开始进行修复工作。

Figure 3-1. 将图像扫描集成到 CI/CD 过程中

如图 3-1 所示,扫描应该在开发周期的每个步骤中进行集成,从开发者检入到持续交付到生产。本节的其余部分将专注于集成中的 CI 部分,以展示图像扫描过程的细粒度。可以将以下策略应用于图 3-1 中显示的每个步骤(即代码、构建[CI]和 CD 流水线)。

选择特定的 CI/CD 构建基础设施是次要的,可以根据更广泛的需求进行选择。流行的 CI/CD 提供者包括但不限于JenkinsSemaphore,和CircleCI。有四种主要方式将图像扫描集成到您的 CI/CD 流水线和构建基础设施中,如图 3-2 所示。

Figure 3-2. 将图像扫描集成为图像注册表的一部分

通过注册表扫描服务扫描图像

如图 3-2 所示,在这种方法中,一旦开发者提交了检入,CI 即构建并将图像推送到注册表。然后图像会定期被注册表内集成的服务扫描。这种方法有许多缺点;注册表提供商通常仅限于扫描操作系统软件包层(例如 GCR 和 Quay)。由于它们显示的信息有限,使用漏洞白名单、分类和跟踪时间表来解决各种问题可能非常麻烦。此外,大多数情况下没有选项可以编写符合您组织需求的策略。

当镜像扫描器发现问题时,可能已经使用 CD 消耗了镜像。注册表不会跟踪消耗了哪些镜像及其部署者。使用这些镜像的用户可能面临妥协风险,却不知道注册表扫描服务识别出的漏洞。漏洞修复仅在通知后进行,可以是向开发团队和其他利益相关者发出的警报或电子邮件。修复可能是从上游提供商的简单更新到复杂的代码和配置修复。这种修复工作往往滞后于开发过程。

图 3-3 显示了镜像作为 CI 过程的一部分进行构建和扫描的方式,但无视扫描器的判定将镜像推送到内部注册表。如果您是一个敏捷的 DevOps 团队,您可能每天或每小时在多个分支上提交检查后立即构建和推送镜像至内部注册表。在这种情况下,如果镜像中存在漏洞,您不希望每个构建都作为 CI 的一部分失败。相反,您会收到有关漏洞的通知,开发人员可以在 CD 启动之前修复该漏洞。

图 3-3. 镜像作为构建过程的一部分进行扫描

构建后扫描镜像

这种方法的缺点在于,即使每个镜像在构建后立即进行扫描,也没有机制强制开发人员立即修复发现的问题。此外,即使检测到漏洞,镜像也可从注册表内提供给内部使用,因此可能成为组织中的薄弱环节,可能导致妥协。

如图 3-4 所示,开发人员提交检查时会启动 CI 作业,构建并随后扫描镜像。扫描完成后,会评估镜像扫描器的判定。若通过,则将镜像推送至内部注册表以供使用。若未通过,则开发人员需立即修复问题,因为他们无法使用包含更改的最新构建。

图 3-4. 镜像内联扫描作为 CI/CD 过程的一部分

内联镜像扫描

最初阶段,这种管道可能难以管理,这取决于您组织的规模和速度,但一旦掌握,组织就能更好地控制其安全姿态。同样的管道设计可以应用于您的 CD 作业,以确保在发布/部署时您的应用程序最为安全。

Kubernetes 准入控制器可以拦截来自 Kubernetes API 的 Pod 创建请求(参见图 3-5)。该机制可以作为最后一分钟的检查,通过触发 CI 作业来扫描正在部署到集群上的镜像。根据扫描的结果,准入控制器可以允许或驳回该 Pod。

图 3-5. 作为 Pod 创建的一部分的镜像扫描

Kubernetes 准入控制器

通常需要自定义或现成的准入控制器,它能够与准入服务器通信,并对执行的扫描给出判断。从容错的角度来看,值得注意的是,如果准入服务器因某些原因失败,则可能影响整个集群中的 Pod 创建。在准入服务器失败的情况下,您希望准入控制器“故障开放”(即继续进行 Pod 创建)还是“故障关闭”(即阻止所有 Pod 创建)是组织的决定;权衡安全风险与容错风险。

由于准入控制器是最后一分钟的检查,通常开发团队在查看正在扫描和拒绝的内容之前是不知道发现的安全漏洞的。因此建议将此方法与早期方法结合使用,作为您防御策略的一部分。

保护 CI/CD 流水线

CI/CD 流水线的自主性质和最少人为干预使得 CI/CD 成为攻击者的一个吸引目标。此外,开发环境可能过于宽容,对安全关注不足。以下是保护您的 CI/CD 流水线的最佳实践。

CI/CD 环境的零信任策略

所有与您的 CI/CD 流水线的连接,无论是入站还是出站,都需要根据您的威胁模型进行零信任策略的严格审查。这将确保对 CI/CD 流水线的出站和入站访问通过安全策略进行管理。

安全的机密管理

审查您的 CI/CD 流水线所需的每个机密,并确保仅在需要时调用密码、访问令牌和加密密钥。机密管理的设计需要考虑对机密的细粒度访问、机密使用和更改日志功能、自动机密轮换以及停用和废弃。我们将在本章的后续部分详细讨论机密管理,以帮助您选择合适的策略。

访问控制

对 CI/CD 资源的严格访问控制和用户责任的分离是确保安全的关键,无论是基于角色、时间还是任务的方法。访问控制需要对流水线的访问进行分段,以便在受到威胁时大幅减少爆炸半径。此外,使用默认启用的两因素认证机制。

审计和监控

对 CI/CD 资源的访问需要持续审核和监控,以确定过度访问、用户离开组织或更改工作角色时的访问停用、滥用或可疑用户行为。

组织策略

解决 CI/CD 流水线和扫描带来的挑战,需要全局组织策略。该策略需指出对 CI/CD 资源的访问要求、用户职责的分离、秘密管理、日志记录和监控要求以及审计策略。

初始时,漏洞扫描可能会使团队不堪重负。因此,开发、DevOps 和安全团队需要明确的指导方针,用于产品漏洞发现和评估、风险、修复和根据组织的威胁模型关闭问题的时间表。

漏洞扫描解决方案可以具有 codified 策略,根据扫描结果和组织的风险容忍度,可以接受或拒绝镜像。

构建有效策略的过程需要迭代,并基于持续反馈以实现量身定制的策略,根据您的行业、规模、工作流程和合规要求,平衡安全性和性能。

秘密管理

一个秘密可以是用于认证和授权用户、组或实体的任何内容。它可以是用户名/密码、API 令牌或 TLS 证书。当应用程序或微服务迁移到 Kubernetes 时,开发人员需要做出的早期设计选择是在哪里存储这些秘密,以及如何检索并在应用程序中根据需要使其可用,而不会 compromis 安全性。以下是实现此目标的主要方法。

etcd 用于存储秘密

应用程序迁移到 Kubernetes 的常见情景是将秘密以 Base64 编码格式存储在 etcd 中作为键值对。Etcd 是 Kubernetes 部署中支持的数据存储。这些秘密可以作为卷挂载或来自 Kubernetes 部署规范中的环境变量在容器内部提供。由于环境变量存储在内存中,与存储在容器文件系统上的卷挂载相比,提取秘密更加困难。对 etcd 的访问由 Kubernetes RBAC 支持,带来了所需的安全性和灵活性。

Etcd 提供了强大的并发原语、可线性读取和用于规模化管理秘密的 API。这种方法的缺点是秘密以明文(Base64 编码)存储,并且在没有配置 etcd 使用 TLS 加密通信的情况下,检索和发送也是以明文进行的。在 第二章 中,您学习了在数据静止状态下加密数据的策略,其中秘密可以在存储在 etcd 中时进行加密。

此外,在 etcd 中存储的秘密既不是有版本控制的,也不能在删除后恢复,而且 etcd 的访问没有经过审计,因此任何有权限访问 etcd 的人都可以访问所有的秘密。由于 etcd 是 Kubernetes 的数据存储,因此无法满足组织更广泛的秘密管理需求。

秘密管理服务

要解决组织的加密和秘密管理需求,可以利用云提供商的秘密管理服务。所有主要的公共云提供商都提供秘密管理服务。

最流行的云提供商的秘密管理服务有AWS Secrets ManagerGoogle Secret ManagerAzure Key Vault

一个显著的第三方示例是HashiCorp Vault,可以用作集中式秘密管理器。它提供了许多功能来满足组织的端到端秘密管理需求(密钥管理、加密、轮换、PKI、存储、复制、吊销、日志记录、监控、审计等)。例如,当 Vault 初始化时,初始密钥可以被加密并存储到 Cloud KMS 中,这样操作员就不必处理明文密钥。

Kubernetes 秘密存储 CSI 驱动程序

容器存储接口(CSI)驱动程序整合了外部秘密存储,如 Azure、GCP、AWS 和 HashiCorp Vault,通过 CSI 将它们集成到 Kubernetes 中,自版本 1.13 以来已普遍可用。

简而言之,CSI 驱动程序使用卷属性与您的秘密存储服务进行身份验证,并无缝地将所需的秘密挂载到 Pod 中。这种方法避免了使用 Kubernetes etcd 数据存储,并允许您有效地扩展和管理组织的秘密。

秘密管理最佳实践

在管理 Kubernetes 中的秘密时,以下是需要考虑的最佳实践。

避免秘密扩散

秘密管理的主要目标是避免秘密扩散,即应用程序的秘密分散在配置文件、YAML 和 Git 存储库等地方。这通常表明组织缺乏秘密管理工作流程。减少秘密扩散的唯一方法是建立一个集中式秘密管理策略,其中凭证可以安全地存储和检索,并由整个组织在适当的授权、日志记录和监控机制下使用。

使用反亲和性规则

理想情况下,秘密管理解决方案应该是少量专用虚拟机或专用主机上的单一进程。由于可能需要在 Kubernetes 上作为微服务运行此解决方案,因此它将作为运行在专用 Pod 中的进程。但问题在于这些 Pod 应该在哪些节点上运行。在此,反亲和性有助于将 Pod 分布在所需节点上,这些节点被分类为运行秘密管理解决方案。

数据加密(传输和静态)

默认情况下,Kubernetes 不安全地存储和传输秘密。配置或使用能够在传输中使用端到端 TLS 加密的解决方案至关重要。同时,要有机制来以加密形式存储秘密。请参阅第二章了解如何实现此目标的选项。

使用自动化秘密轮换

组织对于不同秘密的轮换采用不同的时间框架,但随着自动化秘密轮换的出现,您可以每天甚至每小时进行轮换。云秘密管理服务和外部第三方解决方案都可以帮助实现自动化的秘密轮换和管理。

临时或动态秘密

临时或动态秘密是临时生成的、按需生成的秘密,通常有短暂的生命周期,并在该时间间隔后被销毁。这些秘密可以根据应用程序的类别或运维团队的需要提供。如果攻击者发现秘密(例如通过调试日志、应用程序代码泄露或意外在 GitHub 上公开),这些秘密将在短时间窗口内被更改,从而保护应用程序。此外,它们还可以帮助追踪基础设施中的攻击者行踪,因为很容易确定攻击者发现秘密的时间范围。HashiCorp Vault 和 CyberArk Conjur 是一些提供此类功能的第三方秘密提供者。

启用审计日志

在你的秘密管理解决方案中具有审计日志,可以查看组织中秘密及其使用的可见性。审计日志对于确定意图或无意中的妥协、攻击的影响范围以及相关的取证步骤至关重要。

将秘密存储在容器内存中

当容器化应用程序接收到秘密时,不要将秘密存储在磁盘上(或在主机中可用的 volumeMount 中)。相反,将其存储在内存中,以便在妥协的情况下,这些秘密不易于攻击者获取。

零秘密问题

许多机密管理解决方案采用信封加密,其中 DEK 受 KEK 保护。KEK 被视为零号秘密。如果攻击者破坏了 KEK,则可以解密 DEK,并随后解密 DEK 加密的数据。云提供商的 IAM 和 KMS 的组合可用于帮助保护零号秘密。(当然,这些实际上也有自己的零号秘密链,必须视为高度敏感。)

使用您的证书颁发机构

作为深度防御的一部分,可以使用自定义证书颁发机构 (CA) 实施端到端的 TLS 实现。在这里,组织可以选择使用自己的 CA 签署其证书。在这种情况下,服务只能通过提交组织签署的证书进行访问。

身份验证

一旦您准备好强化您的镜像、CI/CD 管道和机密管理策略,就是 Kubernetes 的认证和授权策略的时候了。Kubernetes 允许多种身份验证机制;在最简单的形式中,认证可以使用证书、令牌或基本身份验证(用户名和密码)完成。此外,可以使用 Webhook 验证持有者令牌,并集成外部 OpenID 提供商。

让我们更详细地看看 Kubernetes 中可用的每种身份验证方法。身份验证方法的配置超出本书的范围。

X509 客户端证书

有两种签署客户端证书的方法,以便用于与 Kubernetes API 进行身份验证。首先是通过 Kubernetes API 内部签署证书。这涉及客户端创建证书签署请求 (CSR)。管理员可以批准或拒绝 CSR。一旦批准,管理员可以提取并提供签署后的证书给请求的客户端或用户。由于这种方法需要手动干预,无法为大型组织提供规模化支持。

第二种方法是使用企业 PKI,它可以签署客户端提交的 CSR。此外,签署授权机构可以将签署后的证书发送回客户端。这种方法要求外部解决方案管理私钥。

持有者令牌

Kubernetes 服务账户使用持有者令牌与 Kubernetes API 进行身份验证。使用持有者令牌的最简单方式是创建一个新的服务账户。Kubernetes API 自动分配与服务账户关联的随机令牌,可以检索并用于验证该账户。

可以使用 Webhook 验证持有者令牌,这涉及使用选项 --authentication-token-webhook-config-file 配置 API,并包含远程 Webhook 服务的详细信息。

Kubernetes 内部使用引导和节点认证令牌初始化集群。此外,还可以使用静态令牌文件的较不安全选项,在配置 Kubernetes API 时使用 --token-auth-file 选项提供。

OIDC 令牌

OpenID Connect 协议 是通过扩展现有的 OAuth2 协议构建的。Kubernetes 不提供 OpenID Connect 身份提供者。您可以使用 Google 或 Azure 等身份提供者,或者使用 dexkeycloakUAA 运行自己的身份提供者。这些外部身份提供者可以根据需要轻松集成到您的认证工作流中,并支持原生身份提供者功能(例如,使用轻量目录访问协议的企业)。

认证代理

可以使用代理与 Kubernetes API 建立信任连接。Kubernetes API 可以通过请求头(如由认证代理设置的 X-Remote-User)识别用户,认证代理会代表 Kubernetes API 对用户进行身份验证。认证代理可以根据工作流程需要进行用户身份验证。

匿名请求

如果对 Kubernetes API 的请求未被任何配置的认证方法拒绝,则将其视为匿名请求(即没有承载令牌的请求)。

对于 Kubernetes 版本 1.6 及以上,需要注意,默认情况下启用了匿名访问,但不包括 AlwaysAllow 授权模式。在配置 Kubernetes API 时,可以通过添加选项 --anonymous-auth=false 来禁用匿名访问。

用户冒充

这是一种微妙的认证机制,通过在请求到 Kubernetes API 中设置额外的头部信息,具有对 Kubernetes 的某些访问权限的用户可以冒充另一个用户。

此机制允许 Kubernetes API 根据被冒充用户的权限和上下文处理请求。此外,Kubernetes API 还可以记录谁冒充了谁以及来自请求的其他相关细节,这在监控和审计过程中可能很有用。

授权

本节将涵盖可用的授权方法、Kubernetes 中的 RBAC、命名空间 RBAC 和注意事项。

Kubernetes 具有多种授权机制,如节点、ABAC、RBAC 和 AlwaysDeny/AlwaysAllow,其中 RBAC 是 Kubernetes 的行业标准。

节点

节点授权由 Kubernetes 在内部使用。它是一种专用授权模式,专门授权 kubelet 发出的 API 请求。它使 kubelet 可以进行读取、写入和与认证相关的操作。为了成功发出请求,kubelet 必须使用标识其属于 system:nodes 组的凭据。

ABAC

Kubernetes 将属性基于访问控制(ABAC)定义为“一种通过将属性组合在一起来授予用户访问权限的访问控制范式”。可以通过在 Kubernetes API 配置中提供 .json 文件,并使用 --authorization-policy-file--authorization-mode=ABAC 选项来启用 ABAC。在调用 Kubernetes API 之前,需要存在 .json 文件。

AlwaysDeny/AlwaysAllow

AlwaysDeny 或 AlwaysAllow 授权模式通常用于开发环境,其中需要允许或拒绝对 Kubernetes API 的所有请求。在配置 Kubernetes API 时,可以使用选项 --authorization-mode=AlwaysDeny/AlwaysAllow 启用 AlwaysDeny 或 AlwaysAllow 模式。这种模式被认为是不安全的,因此不建议在生产环境中使用。

RBAC

基于角色的访问控制是 Kubernetes 中最安全和推荐的授权机制。这是一种根据集群中用户角色限制系统访问的方法。它允许组织强制执行最小权限原则。Kubernetes RBAC 遵循声明性的性质,具有明确的权限(操作)、API 对象(资源)和主体(用户、组或 ServiceAccounts)在授权请求中声明。在 Kubernetes 中应用 RBAC 是一个两步过程。第一步是创建一个 Role 或 ClusterRole。后者是一个全局对象,而前者是一个命名空间对象。一个 Role 或 ClusterRole 由动词、资源和主体组成,提供了对资源的能力(动词),如 图 3-6 所示。第二步是创建一个 ClusterRoleBinding,将第一步中定义的权限分配给用户或组。

让我们举一个例子,假设一个 dev-admins 组需要对集群中的所有秘密进行读取访问。第一步是创建一个 ClusterRole secret-reader,允许通过各种操作(get、list)读取秘密,第二步是将其绑定到一个主体(即用户、组或 ServiceAccounts)以提供访问权限。在这种情况下,组 dev-admins 允许组用户全局读取秘密。

图 3-6 是一个示例,展示了如何创建一个允许您定义对资源访问权限的 ClusterRole。右侧的示例展示了如何将 ClusterRole 绑定到一组用户。

图 3-6. Kubernetes 角色和角色绑定

图 3-7 显示了 RBAC 的概述;您可以根据需要使用任何操作、资源或主体的组合。

除了这些资源外,还有 Kubernetes 的非资源,如 /healths 或 /version API,如果需要的话也可以使用 RBAC 进行控制。

图 3-7. Kubernetes 资源的 RBAC

命名空间 RBAC

在之前的示例中,您看到了一个在集群中全局应用的 RBAC。可以将类似的 RBAC 应用于资源所在的命名空间,使命名空间内的资源受到此 RBAC 策略的约束。应使用命名空间资源 Role 和 RoleBinding 来配置命名空间策略。

在使用命名空间 RBAC 时,有一些注意事项需要注意:

  • 角色和角色绑定是唯一的命名空间资源。

  • ClusterRoleBindings(全局资源)不能与 Role(命名空间资源)一起使用。

  • 角色绑定(命名空间资源)不能与全局资源集群角色一起使用。

  • 只有集群角色可以进行聚合。

提权缓解

Kubernetes RBAC 减少了攻击者通过编辑角色或角色绑定来提升自己权限的能力。这种行为在 Kubernetes 的 API 级别上强制执行,即使 RBAC 授权器未被使用时也适用:

  • 如果用户 user-no-secret 没有能力在整个集群中列出秘密,他们将无法创建包含该权限的集群角色或集群角色绑定。

  • 对于用户 user-no-secret 要获取秘密权限列表,他们将需要以下其中一种:

    • 授予他们一个允许他们创建/更新角色、集群角色、角色绑定或集群角色绑定的角色。

    • 为他们在这些资源上提供显式的提权权限。

结论

在本章中,我们涵盖了以下关键概念,这些概念将帮助您理解安全工具和构建部署工作负载的最佳实践:

  • 仅使用 Docker 提供的基础镜像是不够的,因为它们只是为您的容器提供。您必须花时间确保您的容器镜像经过硬化并考虑了安全性。就像在软件开发中一样,构建时发现漏洞比部署后发现漏洞要便宜得多。

  • 有几种方法可以将图像扫描添加到您的 CI/CD 过程中。我们探讨了像注册表扫描、构建时或内联扫描以及使用 Kubernetes 准入控制器等多种知名方法,帮助您将图像扫描添加到您的 CI/CD 流水线中。我们还讨论了保护 CI/CD 流水线并添加组织策略,有效地为您的组织制定工作流程。

  • 我们讨论了秘密管理的方法和最佳实践。

  • 最后,我们涵盖了可用的 Kubernetes 认证和授权机制。我们建议您使用 RBAC 来减少提权风险。

第四章:工作负载运行时安全性

与 Manoj Ahuje 的贡献,

Tigera 的高级威胁情报研究工程师

Kubernetes 的默认 Pod 提供机制存在广泛的攻击面,敌对方可以利用这一点来攻击集群或逃逸容器。本章将教您如何实施 Pod 安全策略(PSP),以限制 Pod 的攻击面,并学习如何监控进程(例如进程权限)、文件访问以及工作负载的运行时安全性。以下是我们将讨论的一些具体内容:

  • 我们将覆盖 PSP 的实现细节,例如 Pod 安全上下文,并解释 PSP 的限制。请注意,自 Kubernetes v1.21 起,PSP 已被弃用;然而,由于我们知道 PSP 被广泛使用,本章节将继续讨论此主题。

  • 我们将讨论过程监控,重点是需要 Kubernetes 本地监控来检测可疑活动。我们将覆盖使用内核安全特性如 seccomp、SELinux 和 AppArmor 来防止容器访问主机资源的运行时监控和强制执行。

  • 我们将同时讨论漏洞的检测和运行时防御、工作负载隔离以及爆炸半径的控制。

Pod 安全策略

Kubernetes 通过使用 PSP 提供了一种安全地引导 Pod 和容器的方法。它们是一个集群范围的资源,可以在将 Pod 允许并计划在集群中运行之前,检查一组条件。这通过一个 Kubernetes 准入控制器实现,该控制器会评估每个 Pod 创建请求,以确保其符合分配给 Pod 的 PSP 的规定。

请注意,自 Kubernetes 1.21 版本起,PSP 已被弃用,并计划在 1.25 版本中移除。尽管如此,它们在生产集群中被广泛使用,因此本节将帮助您了解它们的工作原理以及实施 PSP 的最佳实践。

PSP 允许您通过诸如 Pods 不应作为 root 运行Pods 不应使用主机网络、主机命名空间或以特权模式运行 等控制来强制执行规则。这些策略在 Pod 创建时生效。通过使用 PSP,您可以确保 Pod 以操作所需的最低特权创建,从而减少应用程序的攻击面。此外,该机制还帮助您符合 PCI、SOC 2 或 HIPAA 等各种标准的要求,这些标准要求使用最少特权访问原则。正如其名称所示,该原则要求任何进程、用户或者在我们的情况下,工作负载都被授予其运行所需的最低权限。

使用 Pod 安全策略

Kubernetes PSP 被推荐但通过可选的准入控制器实现。启用 PSP 的强制性可以通过启用一个准入控制器来实现。这意味着 Kubernetes API 服务器清单应在其--enable-admission-plugins 列表中具有一个 PodSecurityPolicy 插件。许多 Kubernetes 发行版不支持或默认禁用 PSP,因此在选择 Kubernetes 发行版时值得检查。

一旦启用了 PSP,将 PSP 应用到组而不是单个服务帐户是一个三步骤的过程,如图 4-1 所示。一个最佳实践是应用 PSP 到组。

图 4-1. 应用 PSP 的过程

第 1 步是创建一个 PSP。第 2 步是创建具有use动词的 ClusterRole,授权 Pod 部署控制器使用这些策略。然后第 3 步是创建 ClusterRoleBindings,用于对组(例如,system:authenticated 或 system:unauthenticated)或服务帐户强制执行策略。

一个良好的起点是来自 Kubernetes 项目的 PSP 模板:

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: |
    'docker/default,runtime/default'
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'runtime/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
  privileged: false
  # Required to prevent escalations to root.
  allowPrivilegeEscalation: false
  # This is redundant with non-root + disallow privilege escalation,
  # but we can provide it for defense in depth.
  requiredDropCapabilities:
    - ALL
  # Allow core volume types.
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    # Assume that persistentVolumes set up by the cluster admin are safe to use.
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    # Require the container to run without root privileges.
    rule: 'MustRunAsNonRoot'
  seLinux:
    # This policy assumes the nodes are using AppArmor rather than SELinux.
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  readOnlyRootFilesystem: false

在以下示例中,您可以使用 Kubernetes 基于角色的访问控制将此策略应用于所有经过身份验证的用户:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole  Policy
metadata:
  name: psp-restricted
rules:
- apiGroups:
  - policy
  resourceNames:
  - restricted
  resources:
  - podsecuritypolicies
  verbs:
  - use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: psp-restricted-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: psp-restricted
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind : Group
    name: system:authenticated

Pod 安全策略功能

让我们关注 PSP 提供的功能,您可以根据您的用例和内部威胁模型进行利用。您可以按照我们刚讨论过的示例 PSP 模板来构建您自己的 PSP。在此模板中,大部分 PSP 功能被利用来制定一种限制性策略。

要解释一项能力的影响,让我们看一个示例,在这个示例中,您可以看到为使用 privileged:true 和 privileged:false 创建的 Pod 授予的能力。Linux 实用程序capsh可以用来评估容器化根用户的权限。正如您在图 4-2 中所见,特权 Pod 在其 Linux 命名空间中具有大量能力,这意味着攻击者可以更广泛地攻击您的容器逃逸面。

图 4-2. 默认和特权 Pod 的 Pod 功能

表 4-1 总结了在Kubernetes PSP 文档中描述的 Pod 功能。

表 4-1. Pod 功能

Field Uses
privileged 允许容器获得访问主机挂载、文件系统以更改设置等功能。您可以使用命令capsh --print检查功能。
hostPID, hostIPC 允许容器访问主机命名空间,其中进程和以太网接口对其可见。
hostNetwork, hostPorts 允许容器访问主机网络和端口。
volumes 允许卷类型如 configMap、emtyDir 或 secret。
allowedHostPaths 允许列出可以被 hostPath 卷使用的主机路径的白名单(例如,/tmp)。
allowedFlexVolumes 允许特定的 FlexVolume 驱动程序(例如 azure/kv)。
fsGroup 设置拥有 pod 卷的 GID 或 GID 范围。
readOnlyRootFilesystem 将容器的根文件系统设置为只读。
runAsUser, runAsGroup, supplementalGroups 定义容器的 UID 和 GID。在此处,您可以指定非 root 用户或组。
allowPrivilegeEscalation, defaultAllowPrivilegeEscalation 通过进程限制特权升级。
defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities 根据需要添加或删除 Linux capabilities
SELinux 定义容器的 SELinux 上下文。
allowedProcMountTypes 容器允许的 proc 挂载类型。
forbiddenSysctls,allowedUnsafeSysctls 设置容器使用的 sysctl 配置文件。
annotations 设置容器使用的 AppArmor 和 seccomp 配置文件。

在使用 PSP 注释时,可以使用 AppArmor 和 seccomp 配置文件,其中可以使用运行时(Docker、CRI)的默认配置文件或者您在主机上加载的自定义配置文件。您将在 “进程监控” 中了解更多关于这些防御措施的信息。

Pod 安全上下文

与定义为整个集群的 PSPs 不同,pod securityContext 可以在创建部署或 pod 时定义运行时。以下是 pod securityContext 在操作中的简单示例,其中 pod 使用 root 用户 (uid=0) 创建,并且只允许四种功能:

kind: Pod
apiVersion: v1
metadata:
  name: attacker-privileged-test
  namespace: default
  labels:
    app: normal-app
spec:
  containers:
  - name: attacker-container
    image: alpine:latest
    args: ["sleep", "10000"]
    securityContext:
      runAsUser: 0
      capabilities:
        drop:
          - all
        add:
          - SYS_CHROOT
          - NET_BIND_SERVICE
          - SETGID
          - SETUID

此代码片段展示了如何通过指定安全上下文创建一个以 root 用户运行但只允许部分功能集的 pod。图 4-3 展示了可以运行的命令,以验证 pod 以 root 用户和受限功能集运行。

图 4-3. 允许的四种 pod 功能

Pod 安全上下文,如 图 4-3 所示,可以在不启用 PSPs 的情况下使用,但一旦启用 PSPs,您需要定义 securityContext 以确保正确创建 pod。由于 securityContext 具有 PSP 构造,所有 PSP 的功能都适用于 securityContext。

PSPs 的限制

PSPs 的一些限制包括:

  • PodSecurityPolicySpec 引用了 allowedCapabilities、privileged 或 hostNetwork。这些强制措施仅适用于基于 Linux 的运行时。

  • 如果您正在使用控制器(例如复制控制器)创建 pod,则值得检查这些控制器是否被授权使用 PSPs。

  • 一旦在整个集群启用了 PSPs 并且由于错误的 PSP 导致 pod 无法启动,那么排除问题将变得非常繁琐。此外,如果在生产集群中整个集群启用了 PSPs,您需要测试集群中的每个组件,包括像突变接入控制器和冲突判断这样的依赖项。

  • Azure Kubernetes Service (AKS) 已弃用对 PSP 的支持,并优先使用 OPA Gatekeeper 进行策略执行,以支持使用 OPA 引擎实现更灵活的策略。

  • PSP 已被弃用,并计划在 Kubernetes v1.25 中移除。

  • Kubernetes 可能存在 PSP 可被绕过的边缘情况(例如,TOB-K8S-038)。

现在您了解了 PSP、实施它们的最佳实践以及 PSP 的限制,让我们来看看进程监控。

进程监控

当您将工作负载容器化并在像 Kubernetes 这样的编排器上运行时,您需要考虑的层次很多,以监控容器内的进程。这些层次包括容器进程日志和工件、文件系统访问、网络连接、所需的系统调用、内核权限(特殊工作负载)、Kubernetes 工件和云基础设施工件。通常,您组织的安全姿态取决于您的解决方案在将这些各种日志上下文有机地结合起来方面的表现。这也是传统监控系统明显不足之处,需要 Kubernetes 的本地监控和可观察性的原因。传统解决方案,如终端点检测与响应(EDR)和终端点保护系统,在 Kubernetes 集群中使用时存在以下限制:

  • 他们不知道容器。

  • 他们不了解容器网络,通常从主机的角度看待活动,这可能导致对攻击者的侧向移动产生误判。

  • 他们对容器之间的流量一无所知,也看不到像 IPIP 或 VXLAN 这样的底层协议。

  • 他们不了解容器访问底层主机的进程权限和文件权限。

  • 他们不了解 Kubernetes 容器运行时接口(CRI)或其复杂性和安全问题,这可能导致容器能够访问主机上的资源。这也被称为权限提升

在接下来的几节中,我们将介绍您可以用于进程监控的各种技术。首先,我们将查看 Kubernetes 中可用的各种日志进行监控;然后,我们探讨 seccomp、SELinux 和 AppArmor 功能,这些功能允许您控制进程可以访问的内容(例如系统调用、文件系统等)。

Kubernetes 本地监控

如 图 4-4 所示,每一层到您的容器化应用程序进程都引入了监控和日志记录要求,以及与传统 IT 安全从业人员监控网络和应用程序不同的新攻击面。挑战在于减少这种监控开销,因为对于存储和计算资源来说这可能变得非常昂贵。有关度量收集及其高效执行的详细信息将在 第五章 中详细讨论。

图 4-4. Kubernetes 本地监控

要在每个层面构建防御,你在选择解决方案时应该考虑以下几个选项:

  • 能够阻止每个容器或 Kubernetes 编排创建容器生成的进程。

  • 监控每个容器进程使用的内核系统调用,并能够过滤、阻止和警报可疑调用,以防止容器访问主机资源。

  • 监控每个容器进程发起的每个网络连接(套接字),并能够强制执行网络策略。

  • 能够使用网络策略隔离容器(或运行该容器的节点),并将其暂停以调查可疑活动并在 Kubernetes 中收集取证数据。基于 Docker 的容器的 pause 命令 暂停容器中的进程以进行详细分析。请注意,暂停容器将导致其停止正常操作,应作为对事件(例如安全事件)的响应而使用。

  • 监控文件系统的读写操作,了解文件系统变更(二进制、软件包),并通过强制访问控制(MAC)进行额外隔离,以防止提权。

  • 监控 Kubernetes 审计日志,了解客户端发出的 Kubernetes API 请求及检测可疑活动。

  • 启用云服务提供商的基础设施日志记录,并能够在云服务提供商的基础设施中检测可疑活动。

有许多企业和开源解决方案(例如,Falco),利用各种工具和机制(如 ebpf、kprobes、ptrace、tracepoints 等)来瞄准各层级,帮助在各个层面构建防御。你应该查看它们的威胁模型,并选择满足其需求的解决方案。

在接下来的部分,你将看到 Kubernetes 提供的一些机制,通过将 Linux 防御措施与容器更紧密地结合,帮助你在各层面监控和减少攻击面。前一部分侧重于监控,以便检测意外(恶意)行为。以下机制允许你设置控件以防止意外(恶意)行为。

内核安全功能如 seccomp、AppArmor 和 SELinux 可以控制容器化应用程序所需的系统调用,为每个容器提供虚拟隔离和定制,以及使用 MAC 为访问资源(如卷或文件系统)提供访问,有效防止容器越界。仅使用默认设置的功能即可大幅减少集群中的攻击面。在接下来的部分中,你将深入了解每种防御措施及其在 Kubernetes 集群中的工作方式,以便选择适合你威胁模型的最佳选项。

Seccomp

Seccomp 是 Linux 内核的一个功能,可以在粒度基础上过滤容器执行的系统调用。Kubernetes 允许您通过像DockerpodmanCRI-O这样的运行时自动应用加载到节点上的 seccomp 配置文件。简单的 seccomp 配置文件包括一系列 syscalls 和在调用 syscalls 时要采取的适当操作。此操作将攻击面减少到仅允许的 syscalls,从而减少特权升级和容器逃逸的风险。

在下面的 seccomp 配置文件中,默认操作是SCMP_ACT_ERRNO,这会拒绝系统调用。但是对于 syscall chmod,默认的操作被覆盖为SCMP_ACT_ALLOW。通常,seccomp 配置文件由您的运行时加载到所有节点的/var/lib/kubelet/seccomp 目录中。您可以在同一位置添加自定义配置文件:

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "chmod",
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

要查找应用程序使用的系统调用,可以使用strace,如下例所示。例如,您可以列出curl实用程序使用的 syscalls 如下:

$ strace -c -S name curl -sS google.com

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  4.56    0.000242           6        43        43 access
  0.06    0.000003           3         1           arch_prctl
  1.28    0.000068          10         7           brk
  0.28    0.000015          15         1           clone
  4.62    0.000245           5        48           close
  1.38    0.000073          73         1         1 connect
  0.00    0.000000           0         1           execve
  0.36    0.000019          10         2           fcntl
  4.20    0.000223           5        48           fstat
  0.66    0.000035           3        11           futex
  0.23    0.000012          12         1           getpeername
  0.13    0.000007           7         1           getrandom
  0.19    0.000010          10         1           getsockname
  0.24    0.000013          13         1           getsockopt
  0.15    0.000008           4         2           ioctl
 13.96    0.000741           7       108           mmap
 11.94    0.000634           7        85           mprotect
  0.32    0.000017          17         1           munmap
 11.02    0.000585          13        45         1 openat
  0.11    0.000006           6         1           pipe
 19.50    0.001035         115         9           poll
  0.08    0.000004           4         1           prlimit64
  5.43    0.000288           6        45           read
  0.41    0.000022          22         1           recvfrom
 11.47    0.000609          17        36           rt_sigaction
  0.08    0.000004           4         1           rt_sigprocmask
  1.00    0.000053          53         1           sendto
  0.06    0.000003           3         1           set_robust_list
  0.04    0.000002           2         1           set_tid_address
  2.22    0.000118          30         4           setsockopt
  1.60    0.000085          43         2           socket
  0.08    0.000004           4         1         1 stat
  2.35    0.000125          21         6           write
------ ----------- ----------- --------- --------- ----------------
100.00    0.005308                   518        46 total

Kubernetes 运行时提供的默认 seccomp 配置文件包含大多数应用程序使用的常见 syscalls 列表。启用此功能可以禁止使用危险的系统调用,从而可以导致内核漏洞利用和容器逃逸。您可以参考默认 Docker 运行时 seccomp 配置文件

在撰写本文时,Docker/default 配置文件已弃用,建议您改用 runtime/default 作为 seccomp 配置文件。

表格 4-2 展示了通过 PSP 注释在 Kubernetes 中部署 seccomp 配置文件的选项。

表格 4-2. Seccomp 选项

描述
runtime/default 默认容器运行时配置文件
未限制 Kubernetes 中默认没有 seccomp 配置文件—此选项是默认的。
localhost/ 您在节点上的自定义配置文件,通常位于/var/lib/kubelet/seccomp 目录中

SELinux

在最近的过去,每个容器运行时的突破(容器逃逸或特权升级)都是某种文件系统的突破(即 CVE-2019-5736、CVE-2016-9962、CVE-2015-3627 等)。通过提供控制谁可以访问文件系统以及资源之间的交互(例如用户、文件、目录、内存、套接字等),SELinux 可以减轻这些问题。在云计算环境中,应用 SELinux 配置文件对工作负载进行更好的隔离,通过限制主机内核对文件系统的访问来减少攻击面。

SELinux 最初由国家安全局在 2000 年代初开发,主要用于基于 Red Hat 和 centOS 的发行版。SELinux 之所以有效,是因为它提供了 MAC,极大增强了传统的 Linux 自主访问控制(DAC)系统。

在传统的 Linux DAC 中,用户可以更改文件、目录和自己拥有的进程的权限。根用户可以访问所有内容。但是使用 SELinux(MAC)时,每个操作系统资源都由内核分配一个标签,并存储为扩展文件属性。这些标签用于在内核内评估 SELinux 策略,以允许任何交互。启用 SELinux 后,即使容器中的根用户也无法访问挂载卷中主机的文件,如果标签不准确的话。

SELinux 有三种模式:强制执行(Enforcing)、宽松(Permissive)和禁用(Disabled)。强制执行使 SELinux 策略生效,宽松提供警告,禁用则不使用 SELinux 策略。SELinux 策略本身可以进一步分为定向(Targeted)和严格(Strict),定向策略适用于特定进程,而严格策略适用于所有进程。

下面是主机上 Docker 二进制文件的 SELinux 标签,由 <user:role:type:level> 组成。在这里,您将看到类型,即 container_runtime_exec_t

$ ls -Z /usr/bin/docker*
-rwxr-xr-x. root root system_u:object_r:container_runtime_exec_t:s0
/usr/bin/docker
-rwxr-xr-x. root root system_u:object_r:container_runtime_exec_t:s0
/usr/bin/docker-current
-rwxr-xr-x. root root system_u:object_r:container_runtime_exec_t:s0
/usr/bin/docker-storage-setup

为了进一步增强 SELinux,使用多类别安全(MCS)允许用户为资源打上类别标签。因此,标有类别标签的文件只能被该类别的用户或进程访问。

一旦启用 SELinux,像 DockerpodmanCRI-O 这样的容器运行时会选择一个随机的 MCS 标签来运行容器。这些 MCS 标签由两个在 1 到 1023 之间的随机数组成,并以字符“c”(类别)和一个敏感级别(即 s0)为前缀。因此,完整的 MCS 标签看起来像 “s0:c1,c2”。如 图 4-5 所示,除非正确标记,否则容器无法访问主机或 Kubernetes 卷上的文件。这在资源交互之间提供了重要的隔离,防止许多针对逃逸容器的安全漏洞。

图 4-5. SELinux 强制执行文件系统访问权限

接下来是一个部署有 SELinux 配置文件的 pod 示例;除非在主机上标记为 so:c123,c456,否则此 pod 将无法访问任何主机卷挂载文件。即使您可以看到整个主机,文件系统也是以 pod 的方式挂载的:

apiVersion: v1
metadata:
  name: pod-se-linux-label
  namespace: default
  labels:
    app: normal-app
spec:
  containers:
  - name: app-container
    image: alpine:latest
    args: ["sleep", "10000"]
    securityContext:
      seLinuxOptions:
        level: "s0:c123,c456"
  volumes:
    - name: rootfs
      hostPath:
        path: /

表 4-3 列出了关于容器逃逸的 CVE,通过在主机上启用 SELinux 可以预防。虽然 SELinux 策略可能难以维护,但它们对于深度防御策略至关重要。Openshift,一个 Kubernetes 分发版,在其默认配置中启用了 SELinux,并使用定向策略;对于其他发行版,检查状态值得一试。

表 4-3. 与容器逃逸相关的 CVE

CVE 描述 SELinux 阻止
CVE-2019-5736 允许攻击者覆盖主机的 runc 二进制文件,从而获取主机 root 访问权限
CVE-2016-9962 RunC 执行漏洞
CVE-2015-3627 不安全的文件描述符利用

Kubernetes 通过 PSP 提供以下选项来强制执行 SELinux:

Value 描述
MustRunAs 需要像 图 4-5 中显示的那样配置 seLinuxOptions。
RunAsAny 在 PSP 中不提供默认设置(可以选择性地在 pod 和 deployments 上配置)

AppArmor

与 SELinux 类似,AppArmor 是为 Debian 和 Ubuntu 操作系统开发的。AppArmor 的工作方式类似于 SELinux,其中一个 AppArmor 配置文件定义了进程可以访问的内容。让我们看一个 AppArmor 配置文件的示例:

#include <tunables/global>
/{usr/,}bin/ping flags=(complain) {
  #include <abstractions/base>
  #include <abstractions/consoles>
  #include <abstractions/nameservice>

  capability net_raw,
  capability setuid,
  network inet raw,

  /bin/ping mixr,
  /etc/modules.conf r,

  # Site-specific additions and overrides. See local/README for details.
  #include <local/bin.ping>
}

这里的 ping 实用工具仅具备三个能力(即 net_raw、setuid 和对 /etc/modules.conf 的 inet 原始和读取访问权限)。有了这些权限,ping 实用程序无法修改或写入文件系统(密钥、二进制文件、设置、持久性),也不能加载任何模块,这减少了 ping 实用程序在受到威胁时执行任何恶意活动的攻击面。

默认情况下,像 DockerpodmanCRI-O 这样的 Kubernetes 运行时会提供一个 AppArmor 配置文件。Docker 的运行时配置文件可以参考 此处

由于 AppArmor 更加灵活且易于使用,我们建议为每个微服务单独创建一个策略。Kubernetes 通过 PSP 注解提供以下选项来强制执行这些策略:

Value 描述
runtime/default 运行时的默认策略
localhost/<profile_name> 应用于主机上加载的配置文件,通常位于目录 /sys/kernel/security/apparmor/profiles
unconfined 不加载任何配置文件

Sysctl

Kubernetes sysctl 允许您使用 sysctl 接口在集群中使用和配置内核参数。使用 sysctl 的一个示例是管理需要处理大量并发连接或需要特殊参数设置(例如 IPv6 转发)以有效运行的资源密集型工作负载的容器。在这种情况下,sysctl 提供了一种仅对这些工作负载修改内核行为而不影响集群其余部分的方法。

sysctl 可以将 sysctl 分为两个桶:安全和不安全。安全的 sysctl 只影响容器,但不安全的 sysctl 影响容器和运行在其上的节点。管理员可以自行决定如何设置这两个 sysctl 桶。

举例来说,如果一个容器化的 Web 服务器需要处理大量并发连接,并且需要将 net.core.somaxconn 值设置为高于内核默认值,可以如下设置:

apiVersion: v1
kind: Pod
metadata:
  name: sysctl-example
spec:
  securityContext:
    sysctls:
    - name: net.core.somaxconn
      value: "1024"

请注意,我们建议您使用节点亲和性来安排工作负载,以便在需要使用适用于节点的 sysctl 时使用。以下示例显示了 PSPs 如何允许或禁止 sysctl:

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: sysctl-psp
spec:
  allowedUnsafeSysctls:
  - kernel.msg*
  forbiddenSysctls:
  - kernel.shm_rmid_forced

结论

在本章中,我们讨论了定义和实施工作负载运行时安全性的工具和最佳实践。最重要的要点是:

  • Pod 安全策略是在工作负载创建时启用工作负载控制的一种绝佳方式。它们有一些限制,但可以有效使用。

  • 您需要选择一个在 Kubernetes 中本地支持的监控进程的解决方案,并根据您的工作负载威胁模型实施控制。

  • 我们建议您审查 Linux 内核中提供的各种安全选项,并根据您的用例利用适当的功能集。

第五章:观察性

在本章中,我们将讨论在 Kubernetes 部署背景下监控和可观察性之间的区别。我们将解释在您的 Kubernetes 集群中实施可观察性的最佳实践和工具。在下一章中,我们将介绍如何利用可观察性来保护您的集群。

可观察性最近在 Kubernetes 社区中成为讨论的话题,并引起了很多关注。我们首先要理解监控和可观察性之间的区别。然后,我们将看看为何在像 Kubernetes 这样的分布式应用中,可观察性对安全至关重要,并审查实现可观察性的工具和参考实现。虽然可观察性是一个广泛的话题,并适用于多个领域,但在本章中,我们将讨论重点放在 Kubernetes 上。让我们从监控和可观察性开始,并了解它们的区别。

监控

监控是系统中用于警示从正常范围偏离的已知一组测量。以下是在 Kubernetes 中您可以监视的数据类型示例:

  • Pod 日志

  • 网络流日志

  • 应用程序流日志

  • 审计日志

您可以监控的指标示例包括以下内容:

  • 每秒连接数

  • 每秒数据包数,每秒字节数

  • 应用程序(API)每秒请求数

  • CPU 和内存利用率

这些日志和指标可以帮助您识别已知故障,并提供有关症状的更多信息,以帮助您解决问题。

为了监视您的 Kubernetes 集群,您使用诸如轮询和正常运行时间检查的技术,具体取决于您需要为其集群维护的 SLA。以下是您可以监视 SLA 的示例指标:

  • 轮询应用程序/API 端点

  • 应用响应代码(例如 HTTP 或数据库错误代码)

  • 应用程序响应时间(例如 HTTP 持续时间,数据库事务时间)

  • 用于扩展用例的节点可用性

  • 节点上的内存/CPU/磁盘/IO 资源

监控的另一个重要部分是警报。您需要一个警报系统作为监控解决方案的一部分,为任何违反指定阈值的度量生成警报。像 Grafana、Prometheus、OpenMetrics、OpenTelemetry 和 Fluentd 这样的工具被用作监控工具,用于收集日志和指标,并为 Kubernetes 集群生成报告、仪表板和警报。Kubernetes 提供了与类似 Opsgenie、PagerDuty、Slack 和 JIRA 这样的工具集成的选项,用于警报转发和管理。

监控您的生产 Kubernetes 集群存在以下问题:

日志数据量

在类似 Kubernetes 的系统中,一个节点上有多个运行在主机上的 pod,每个 pod 都有其自己的日志、网络标识和资源。这意味着您需要收集来自应用程序操作、网络流量日志、Kubernetes 活动(审计)日志以及每个 pod 的应用程序流量日志。在非 Kubernetes 环境中,通常一个节点上运行一个应用程序,因此日志集合只是单一的一组日志,而不是每个运行在节点上的 pod 的一组日志。这增加了需要收集/检查的日志数据量。除了每个 pod 的日志外,您还需要收集 Kubernetes 的集群日志。通常这些也称为审计日志,提供了对 Kubernetes 集群活动的可见性。系统中的日志数量将使监视变得非常耗费资源且昂贵。您的日志收集集群不应该比运行应用程序的集群更昂贵!

监视分布式应用程序

在 Kubernetes 集群中,应用程序分布在 Kubernetes 集群网络中。一个需要多个 pod 的应用程序(例如,一个部署集或一个服务)将会有每个 pod 的日志需要检查,除了需要考虑 pod 集合的上下文(例如,扩展,错误处理等)。在生成应用程序警报之前,我们需要将多个 pod 视为一个组。请注意,目标是监视应用程序并为其生成警报,独立为应用程序的一部分生成警报并不会准确反映应用程序的状态。还有微服务应用程序的情况,其中单个应用程序部署为一组称为微服务的服务,每个微服务负责应用程序功能的一部分。在这种情况下,您需要将每个微服务作为一个实体进行监视(请注意,一个微服务是一个或多个 pod 的集合),然后理解哪些微服务影响了任何给定的应用程序事务。只有在这之后才能为应用程序报告警报。

Kubernetes 的声明性质

正如我们所涵盖的,Kubernetes 是声明式的,允许您精确指定如何在集群中创建和运行 pod。Kubernetes 允许您为内存、CPU、存储等指定资源限制,还可以创建自定义资源并为这些资源指定限制。调度器将查找具有所需资源的节点,并在节点上调度 pod。Kubernetes 还监视 pod 的使用情况,并将消耗超过分配资源的 pod 终止。此外,Kubernetes 提供详细的指标,可用于监控 pod 和集群状态。例如,您可以使用类似 Prometheus 的工具监控 pod 和集群状态,并使用这些指标,还可以使用所谓的 水平 Pod 自动伸缩器 自动扩展 pod 或其他集群资源。这意味着 Kubernetes 作为其操作的一部分正在监视并对集群进行更改,以维持根据配置的规范运行。在这种情况下,监控单个指标的警报可能是 Kubernetes 进行更改以适应集群负载的结果,也可能是一个真正的问题。您需要能够区分这两种情况,以准确监控您的应用程序。

现在我们了解了监控及其如何实施以及在使用监控时 Kubernetes 集群面临的挑战后,让我们来看看可观察性以及它如何帮助克服这些挑战。

可观察性

可观察性是指仅通过观察系统的外部输出就能理解系统内部状态的能力。可观察性工程,查理蒂·梅杰斯等人(O’Reilly) 是了解可观察性的绝佳资源。该书的第二章讨论了监控和可观察性,对本次讨论非常相关。

可观察性基于监控,并使您能够获取有关应用程序内部状态的见解。例如,在 Kubernetes 集群中,意外的 pod 重新启动事件可能对服务没有或只有很少的影响,因为在重新启动时,可能已经有足够的 pod 实例来处理负载。监控系统将生成警报,说明发生了意外的 pod 重新启动事件;可观察性系统将生成一个中等优先级事件,并说明发生了意外的 pod 重新启动事件,但如果在 pod 重新启动时没有应用程序错误等其他事件,则对系统没有影响。另一个例子是在应用程序层生成事件时(例如,HTTP 请求的持续时间大于正常值)。在这种情况下,可观察性系统将为应用程序响应时间下降的原因提供背景信息(例如,网络层问题、重传、由于资源或其他应用程序问题导致的应用程序 pod 重新启动、Kubernetes 基础设施问题,如 DNS 延迟或 API 服务器负载)。正如前面所解释的,可观察性系统可以查看影响应用程序状态的多个事件,并在考虑所有这些事件后报告应用程序状态。现在让我们看看如何在 Kubernetes 系统中实现可观察性。

可观察性在 Kubernetes 中的工作原理

Kubernetes 的声明性特性在实施可观察性系统中非常有帮助。我们建议您构建一个与 Kubernetes 本地化且能够理解集群操作的系统。例如,一个了解 Kubernetes 的系统将监视一个 pod(例如,重新启动、内存不足、网络活动等),但还将了解一个 pod 是否是独立实例或部署、副本集或服务的一部分。它还将知道 pod 对服务或部署的关键性有多大(例如,服务配置为可扩展性和高可用性的方式)。因此,当它报告与 pod 相关的任何事件时,它将提供所有这些上下文,并帮助您轻松地做出关于如何响应事件的决定。

另一个需要记住的是,在 Kubernetes 中,您可以将应用程序部署为 pod,这些 pod 是高级结构的一部分,例如部署或服务。为了理解为这些结构实施可观察性所带来的复杂性,我们将使用一个示例来解释它们。当您配置一个服务时,Kubernetes 管理与该服务关联的所有 pod,并确保将流量传递到服务的可用 pod。让我们来看一个来自 Kubernetes 文档的服务定义示例

`apiVersion`: v1
`kind`: Service
`metadata`:
  `name`: my-service
`spec`:
  `selector`:
    `app`: MyApp
  `ports`:
    - `protocol`: TCP
      `port`: 80
      `targetPort`: 9376

在这个例子中,所有标签为 MyApp 并监听 TCP 端口 9376 的 pod 都成为服务的一部分,所有发送到服务的流量都会被重定向到这些 pod 上。我们在第八章中详细讨论了这个概念。因此,在这种场景下,观测解决方案也应该能够提供服务级别的洞察力。仅仅监视一个 pod 是不够的。所需的是观测能够在服务中汇总所有 pod 的指标,并使用聚合信息进行更多的分析和警报。

现在让我们看一个在 Kubernetes 中的部署的例子。部署允许你管理 pod 和副本集(pod 的副本,通常用于扩展和高可用性)。以下是 Kubernetes 中部署的一个示例配置:

`apiVersion`: apps/v1
`kind`: Deployment
`metadata`:
  `name`: nginx-deployment
  `labels`:
    `app`: nginx
`spec`:
  `replicas`: 3
  `selector`:
    `matchLabels`:
      `app`: nginx
  `template`:
    `metadata`:
      `labels`:
        `app`: nginx
    `spec`:
      `containers`:
      - `name`: nginx
        `image`: nginx:1.14.2
        `ports`:
        - `containerPort`: 80

这个配置将创建一个 nginx 的部署,其中有三个副本 pod,具有配置的元数据和规范。Kubernetes 拥有一个部署控制器来确保部署中所有的 pod 和副本都是可用的。使用部署资源在 Kubernetes 中还可以实现滚动更新、自动扩展等多种其他好处。在这样的情况下,观测工具应该查看部署中所有 pod(副本)的活动作为一个整体(例如,部署中所有 pod 的流量、pod 的重新启动及其对部署的影响等)。仅监视和警报每个 pod 并不足以理解部署的运行情况。

在这两个例子中都清楚地表明,指标的收集需要在 Kubernetes 的上下文中进行。我们不应该在 pod 级别粒度上收集所有数据和指标,而是在适用时应该在部署或服务级别粒度上收集数据,以提供部署或服务状态的准确表现。请记住,Kubernetes 抽象了 pod 级别的细节,因此我们需要在更高的级别上进行测量和警报。在部署和服务级别进行数据聚合将减少你需要一直收集的日志数量,并解决与大量日志相关的成本问题。请注意,该工具需要在操作员需要分析问题时有能力深入了解并捕获 pod 级别的细节。我们将在本章后面讨论数据收集时详细介绍这一点。

现在我们已经了解了如何利用 Kubernetes 的声明性特性来帮助观测,并减少我们需要收集和生成相关警报的日志数据量,让我们来探讨 Kubernetes 的分布式特性及其对观测的影响。

在基于微服务的应用部署中,单个应用由多个部署在 Kubernetes 集群中的微服务组成。这意味着为了为用户服务一个单一事务,一个或多个服务需要相互交互,从而产生一个或多个子事务。一个典型的微服务应用示例是 Google 在线精品演示微服务应用。图 5-1 展示了该应用的架构。

图 5-1 展示了在线精品应用如何作为 Kubernetes 中的微服务部署。这里有 11 个微服务,每个负责应用的某个方面。我们鼓励您查看这个应用,因为我们将在本章后面使用它来演示如何实现可观测性。如果您查看结帐交易,用户会向前端服务发出请求,然后前端服务会向结帐服务发出请求。结帐服务需要与多个服务进行交互(例如 PaymentService、Shipping Service、CurrencyService、EmailService、ProductCatalog Service、CartService)来完成交易。因此,在这种情况下,如果我们的 HTTP 应用日志显示结帐流程 API 响应时间大于预期,我们需要审查每个子事务,并查看是否存在问题以及问题是什么(应用问题、网络问题等)。另一个使情况复杂化的因素是每个子事务都是异步的,而每个微服务同时提供几个独立的事务。在这种情况下,您需要使用一种称为 分布式跟踪 的技术来跟踪单个事务在一组微服务中的流动。分布式跟踪可以通过对应用程序或内核进行仪器化来实现。我们将在本章后面介绍分布式跟踪。

图 5-1. 谷歌微服务演示应用架构

现在我们理解了可观测性以及在 Kubernetes 集群中如何考虑它之后,让我们来看看 Kubernetes 可观测性工具的组件。图 5-2 展示了 Kubernetes 可观测性工具的各个组件的块图。

图 5-2. Kubernetes 可观测性工具的组件

图 5-2 显示了实现可观测性所需的以下组件:

遥测数据收集

如前所述,您的可观测性解决方案需要从集群中的各种传感器收集遥测数据。它需要是分布式的且本地化于 Kubernetes。它必须支持从 L3 到 L7 的所有层的传感器。它还需要收集有关 Kubernetes 基础设施(例如 DNS 和 API 服务器日志)以及 Kubernetes 活动(这些称为审计日志)的信息。正如描述的那样,这些信息必须在部署和服务的上下文中收集。

分析和可见性

在这一层面上,系统必须提供特定于 Kubernetes 操作的可视化(例如服务图、Kubernetes 平台视图、应用程序视图)。我们将涵盖一些本地化于 Kubernetes 的常见可视化。我们建议您选择一个利用机器学习技术进行基线化和报告异常的解决方案。最后,系统需要支持运营人员启用 pod 到 pod 的数据包捕获能力(请注意,这与在主机接口上启用数据包捕获不同,因为会丢失 pod 级别的可见性)。我们将在下一节中详细介绍这一点。

安全性和故障排除应用程序

您实施的可观测性系统必须支持分布式跟踪,如前一节所述,以帮助排除应用程序问题。我们还建议使用先进的机器学习技术来理解 Kubernetes 集群的行为并预测性能或安全问题。请注意,这是一个新领域,正在不断创新。

现在我们已经讨论了在 Kubernetes 集群中实施可观测性所需的内容,让我们详细审查每个组件。

实施 Kubernetes 的可观测性

在这一部分,我们将审查构建 Kubernetes 中有效观测系统所需的每个组件。

你应该将日志收集视为分布在你的集群中的一组传感器。您需要确保这些传感器是高效的,并且不会干扰系统操作(例如增加延迟)。我们将在本节后面涵盖收集方法,展示如何有效地收集指标。您应该考虑在整个堆栈的所有层部署传感器(或收集信息),如 图 5-2 所示。Kubernetes 审计日志是了解各种 Kubernetes 资源完整生命周期的重要信息源。除了审计日志外,Kubernetes 还提供了多种选项用于 监控。接下来,您需要关注流量流日志(Layer 3/Layer 4),以理解 Kubernetes 集群网络操作。考虑到 Kubernetes 的声明性特性,重要的是收集与应用程序流(例如 HTTP 或 MySQL)相关的日志,这些日志可以展示用户看到的应用程序行为可见性(例如响应时间、可用性等)。为了帮助故障排除,您还应该收集与 Kubernetes 集群基础设施相关的日志(例如 API 服务器、DNS)。一些高级故障排除系统还会收集由 pod 活动引起的 Linux 内核信息(例如,由 pod 发起的流的进程信息、套接字统计信息),并提供一种启用 pod 之间流量的数据包捕获(原始数据包)的方式。以下描述了您应该每个流收集的内容:

Kubernetes 审计日志

Kubernetes 提供了收集和监控活动的能力。这是一个优秀的 指南,解释了如何控制收集什么以及日志记录和警报机制。我们建议您仔细审查您需要收集和设置审计策略 —— 我们建议不要仅仅收集所有内容。例如,您应该记录 API 请求、用户名、RBAC、决策、请求动词、发出请求的客户端(用户代理)以及 API 请求的响应代码。我们将在可视化部分展示一个样本 Kubernetes 活动仪表板。

网络流量日志

网络流量日志(Layer 3/Layer 4)是理解 Kubernetes 集群网络操作的关键。通常包括五元组(源和目标 IP 地址/端口和端口)。收集与 pods 相关的 Kubernetes 元数据(源和目标命名空间、pod 名称、与 pods 相关的标签、运行 pods 的主机)以及每个流的聚合字节/数据包是非常重要的。请注意,这可能导致大量的流量数据,因为一个节点上可能有大量的 pods。在下一节关于集合时的聚合部分,我们将讨论如何解决这个问题。

DNS 流量日志

除了 API 服务器,DNS 服务器是 Kubernetes 集群的关键部分,被应用程序用于解析域名以连接其他服务/pod,作为正常运行的一部分。DNS 服务器的问题可能会影响集群中的多个应用程序。从客户端的角度收集信息非常重要。应记录由 pod 发出的 DNS 请求,包括请求计数、延迟、用于解析请求的 DNS 服务器、DNS 响应代码和响应。这些信息应与 Kubernetes 元数据(例如,命名空间、pod 名称、标签等)一起收集,因为这将有助于将 DNS 问题与服务关联并进一步进行故障排除。

应用程序日志

正如所解释的那样,在声明式系统如 Kubernetes 中,收集应用程序日志(HTTP、MySQL)非常重要。这些日志提供了用户体验的视图(例如,响应时间或可用性)。日志将是特定于应用程序的信息,但必须包括响应码(状态)、响应时间和其他特定于应用程序的上下文。例如,对于 HTTP 请求,应记录域(URL 的一部分)、用户代理、请求次数、HTTP 响应代码,并在某些情况下完整的 URL 路径。再次强调,日志应包括 Kubernetes 元数据(例如,命名空间、服务、标签、pod 名称等)。

处理信息和套接字统计

如前所述,这些统计信息不属于典型的可观测性实现的一部分,但我们建议您考虑收集这些统计信息,因为它们提供了 Kubernetes 集群操作的更全面视图。例如,如果您可以获取进程信息(运行在 pod 中的进程),这可以很好地与应用程序性能数据相关联(例如,将基于 Java 的应用程序中的内存使用情况或垃圾收集事件与响应时间和由进程发起的网络活动进行关联)。套接字统计是两个端点之间 TCP 流的详细信息(例如,网络往返时间、TCP 拥塞窗口、TCP 重传等)。当这些统计信息与 pod 关联时,可以查看底层网络对 pod 间通信的影响。

现在我们已经讨论了完整的可观测性解决方案所需收集的内容,让我们来看看可用于实施收集的工具和技术。图 5-3 是一个示例参考实现,展示了如何在你的 Kubernetes 集群中的节点上实现收集。

图 5-3 展示了你的 Kubernetes 集群中的一个节点,该节点部署了作为服务、部署和命名空间中的 pod,这与典型的 Kubernetes 集群中看到的情况相同。为了方便收集,如可观测性组件部分所示,添加了一些组件,并展示了一些用于方便收集的 Linux 内核补丁。让我们探讨每个组件的功能。

图 5-3. 节点上的参考实现收集

Linux 内核工具

Linux 内核提供了几个选项,可用于帮助数据收集。非常重要的是,您使用的工具利用这些工具,而不是专注于处理其他工具生成的原始日志:

eBPF 程序和 kprobes

eBPF 是扩展的伯克利数据包过滤器的缩写。它是一项令人兴奋的技术,可用于收集和可观察性。它最初设计用于数据包过滤,但后来扩展为允许将程序添加到内核中各种挂钩点,用作跟踪点。如果您正在使用基于 eBPF 的数据平面,管理数据包路径的 eBPF 程序还将提供数据包和流量信息。我们建议阅读 Brendan Gregg 的博客文章“Linux Extended BPF (eBPF) Tracing Tools” 以了解如何使用 eBPF 进行性能和跟踪。在本讨论的背景下,您可以将 eBPF 程序附加到内核探针(kprobe),这实质上是一个跟踪点,当注册了 kprobe 的函数执行时触发并执行程序。有关 kprobes 的内核文档 提供了更多细节。这是从 Linux 内核获取可观察性信息的绝佳方式。

NFLOG 和 conntrack

如果您使用标准的 Linux 网络数据平面(基于 iptables),有可用工具来跟踪数据包和网络流量。我们建议使用 NFLOG,这是一个与 iptables 结合使用的机制,用于记录数据包。您可以在 iptables 文档中查看详细信息;在高层次上,NFLOG 可以被设置为 iptables 规则的目标,并通过 netlink 套接字在多播地址上记录数据包,用户空间进程可以订阅并收集数据包。Conntrack 是另一个模块,与 iptables 结合使用,用于查询数据包或流的连接状态,并可用于更新流的统计信息。

我们建议您审查 Linux 内核提供的选项(例如 Net Filter),并在用于收集信息的传感器中利用它们。这非常重要,因为这将是一种高效的数据收集方式,由于 Linux 内核提供的这些选项都经过了高度优化。

可观察性组件

现在我们了解了如何从 Linux 内核收集数据,让我们看看如何在用户空间处理这些数据,以确保我们有一个有效的可观察性解决方案:

日志收集器

这是系统中非常重要的一个组件。该组件的目标是从 Kubernetes 集群中为从其他传感器收集的数据添加上下文,例如,将 Pod 元数据(名称、命名空间、标签等)添加到网络流的源和目标 IP 地址中。这是您可以将 Kubernetes 上下文添加到原始网络流日志的方式。同样,您收集的任何来自内核探测器的数据也可以通过添加相关的 Kubernetes 元数据来丰富。通过这种方式,您可以获得将内核中的活动与 Kubernetes 集群中的对象(例如 Pod、服务、部署)关联起来的日志数据。您能够从中推断出有关 Kubernetes 集群操作的见解。请注意,这个组件是您需要实现的,或者必须确保您选择的可观察性工具具有此功能。这是您可观察性实施中的关键部分。

Envoy(代理)

我们讨论了拥有一个应用特定数据集的重要性,并为此推荐您使用Envoy,这是一个广为人知的代理工具,用于分析应用协议并记录应用事务流(例如,在单个 HTTP 连接上的 HTTP 事务)。请注意,Envoy 可以作为侧车模式使用,附加到每个 Pod,并跟踪与 Pod 的数据包进出。它还可以部署为守护进程集(透明代理),您可以使用数据平面将流量重定向,通过运行在主机上的 Envoy 实例。我们强烈建议您使用后一种配置的 Envoy,因为使用侧车模式存在安全问题,并且可能会对应用程序造成干扰。在这次讨论的背景下,Envoy 守护进程集将是应用流日志的来源,提供给日志收集器。现在,日志收集器可以使用 Pod 元数据(名称、命名空间、标签、部署、服务、IP 地址)将这些数据与从内核接收的数据进行关联,并进一步丰富应用数据。

Fluentd

请注意,到目前为止讨论的数据收集是由集群中每个节点上的日志收集器处理的。您需要实现一种机制,将所有节点的数据发送到数据存储或安全信息和事件管理(SIEM)中,在那里可以被分析和可视化工具获取。Fluentd是将收集的数据发送到您选择的数据存储的绝佳选择。它提供了出色的集成,是一个本地支持 Kubernetes 的工具。还有其他选择可用,但我们建议您使用 Fluentd 来将收集的日志数据发送到数据存储。

Prometheus

我们已经讨论了如何收集流日志;现在我们需要一个组件来收集指标和警报。Prometheus,一个原生于 Kubernetes 的工具,是收集指标和警报的一个很好的选择。它部署为端点,这些端点抓取指标并将其发送到 Prometheus 服务器中的时间序列数据库进行分析和查询。您还可以为发送到 Prometheus 服务器的数据定义警报。它是一个广泛使用的选项,并且具有与仪表板和警报工具的集成。我们建议您考虑它作为集群的一个选项。

希望这次讨论使您了解如何为您的 Kubernetes 集群实现数据收集。现在让我们来看一下聚合和相关性。

聚合和相关性

在前一节中,我们涵盖了数据收集并讨论了如何从集群中的各种来源(API 服务器、网络流量、内核探测器、应用程序流量)收集数据。这非常有用,但如果我们将收集保持在 Pod 级别的粒度,我们仍然需要解决数据量的问题。另一个需要注意的是,如果我们保持来自不同来源的数据分开,然后在查询时关联数据,数据量的问题会成倍增加。可以说,尽可能保留尽可能多的原始数据是更好的选择,而且在收集后有高效的工具来查询和聚合数据(离线),为什么不采用这种方法呢?是的,这是一个有效的观点,但有几件事需要考虑。大量数据意味着数据的聚合和查询时间的连接将消耗大量资源(操作您的数据收集系统可能比操作您的 Kubernetes 集群更昂贵!)。此外,鉴于 Kubernetes 的临时性质(Pod 生命周期可能非常短暂),离线分析数据的延迟阻止了对收集到的数据报告问题的任何合理响应。在某些情况下,如果在收集时未进行相关性,则无法关联两个不同的收集。例如,您无法收集策略列表和流量列表,然后在离线模式下重新运行策略评估而不将策略与流量关联。

我们还讨论了 Kubernetes 的声明性质,以及部署(deployment)和服务(service)是比 Pod 更高级的构建。在这种情况下,我们建议您考虑在部署或服务级别聚合数据。这意味着服务的所有 Pod 的数据被聚合;您将默认收集部署之间和服务之间的数据。这将为您提供正确的粒度级别。您可以提供一个选项来减少聚合以收集 Pod 级别的数据,作为深入操作或响应事件的一部分。这样,您可以解决关于收集大量日志数据及相关处理成本的问题。此外,数据收集更符合 Kubernetes 本身,因为 Kubernetes 监控部署/服务作为一个单元,并进行调整,以确保部署/服务按照规范运行。

在数据收集部分,我们讨论了日志收集器组件,它从各种来源接收数据。它可以用作收集时数据关联的源,因此您无需在数据收集后进行任何额外的关联,并且您还将受益于不必为每个来源收集冗余数据。例如,如果内核中的 kprobe 收集五元组(IP 地址、端口、协议)的套接字数据,并且 NFLOG 为相同的五元组提供其他信息,如字节和数据包,日志收集器可以创建一个包含五元组、Kubernetes 元数据、网络流数据和套接字统计数据的单个日志。这将为收集和处理提供具有非常高上下文和低占用率的日志。

现在让我们回到 Google 在线精品示例,并看一看带有内核和网络流数据聚合和关联的日志样本。样本日志是使用前面描述的收集和聚合概念为应用程序的前端服务和货币服务之间的交易生成的,这是基于 gRPC 的交易:

{
   "_id": "YTBT5HkBf0waR4u9Z0U3",
   "_score": 3,
   "_type": "_doc",
   "start_time": 1623033303,
   "end_time": 1623033334,
   "source_ip": "10.57.209.32",
   "source_name": "frontend-6f794fbff7-58qrq",
   "source_name_aggr": "frontend-6f794fbff7-*",
   "source_namespace": "onlinebotique",
   "source_port": null,
   "source_type": "wep",
   "source_labels": [
       "app=frontend",
       "pod-template-hash=6f794fbff7"
   ],
   "dest_ip": "10.57.209.29",
   "dest_name": "currencyservice-7fd6c64-t2zvl",
   "dest_name_aggr": "currencyservice-7fd6c64-*",
   "dest_namespace": "onlinebotique",
   "dest_service_namespace": "onlinebotique",
   "dest_service_name": "currencyservice",
   "dest_service_port": "grpc",
   "dest_port": 7000,
   "dest_type": "wep",
   "dest_labels": [
       "app=currencyservice",
       "pod-template-hash=7fd6c64"
   ],
   "proto": "tcp",
   "action": "allow",
   "reporter": "src",
   "policies": [
       "1|platform|platform.allow-kube-dns|pass",
       "2|__PROFILE__|__PROFILE__.kns.hipstershop|allow",
       "0|security|security.pass|pass"
   ],
   "bytes_in": 68437,
   "bytes_out": 81760,
   "num_flows": 1,
   "num_flows_started": 0,
   "num_flows_completed": 0,
   "packets_in": 656,
   "packets_out": 861,
   "http_requests_allowed_in": 0,
   "http_requests_denied_in": 0,
   "process_name": "wrk:worker_0",
   "num_process_names": 1,
   "process_id": "26446",
   "num_process_ids": 1,
   "tcp_mean_send_congestion_window": 10,
   "tcp_min_send_congestion_window": 10,
   "tcp_mean_smooth_rtt": 9303,
   "tcp_max_smooth_rtt": 13537,
   "tcp_mean_min_rtt": 107,
   "tcp_max_min_rtt": 107,
   "tcp_mean_mss": 1408,
   "tcp_min_mss": 1408,
   "tcp_total_retransmissions": 0,
   "tcp_lost_packets": 0,
   "tcp_unrecovered_to": 0,
   "host": "gke-v2y0ly8k-logging-default-pool-e0c7499d-76z8",
   "@timestamp": 1623033334000
}

这是 Calico Enterprise 的流日志示例。有几点需要注意:它聚合了支持前端服务的所有 Pod(frontend-6f794fbff7-*)和属于 currencyservice 的所有 Pod(currencyservice-7fd6c64-*)的数据。来自 kprobe 和套接字统计的数据被聚合为每个指标的平均值、最小值和最大值,用于服务之间的数据。来自内核的进程 ID 和进程名称与其他数据相关联,我们还看到网络策略操作和影响流的网络策略与其他数据相关联。这是您在 Kubernetes 集群中进行数据收集的目标示例!

现在我们已经讨论了如何以 Kubernetes 本地方式收集、聚合和关联数据,让我们来探索数据的可视化。

可视化

有一些很棒的工具支持收集数据的可视化。例如,Prometheus 与 Grafana 的集成提供了非常好的仪表板来可视化数据。还有一些商业工具如 Datadog、New Relic 和 Calico Enterprise,支持数据的收集和可视化。我们将介绍一些对 Kubernetes 集群有用的常见可视化方式。

Service Graph

这是将您的 Kubernetes 集群表示为一个图表,显示 Kubernetes 集群中的服务及其之间的交互。如果我们回到 Google 微服务在线精品店的示例,图 5-4 显示了实施并表示为服务图的在线精品店应用程序。

图 5-4 是将在线精品店命名空间表示为服务图的可视化,其中节点表示服务和支持服务或部署的一组 Pod,无论是独立的还是作为部署的一部分。边缘显示了网络活动和策略操作。该图是交互式的,允许您选择一个服务(例如前端服务),并允许查看为该服务收集的详细日志。图 5-5 显示了所选服务(前端)的所有收集数据的汇总视图。

图 5-5 显示了前端服务的详细视图,作为一种钻取方式——它展示了来自所有来源的信息,因此非常容易分析服务的操作。

服务图是表示 Kubernetes 集群拓扑结构的一种非常常见的模式。有几种工具提供了这种视图,例如 Kiali、Datadog 和 Calico Enterprise。

图 5-4. 在线精品店应用程序的服务图表示

图 5-5. 前端微服务的详细视图

网络流量的可视化

图 5-6 显示了一种用于可视化流量的常见模式。这是基于环的可视化,其中每个环代表一个聚合级别。在 图 5-5 中显示的示例中,最外层的环代表一个命名空间及其内的所有流量。选择中间的环显示了一个服务的所有流量,选择最内层的环显示了支持该服务的 Pod 的所有流量。右侧面板是一个选择器,可以通过过滤和详细信息如选择的流量和策略操作来启用更细粒度的视图。这是在集群中可视化网络流量的一个绝佳方式。

图 5-6. 网络流量可视化

在本节中,我们介绍了一些常见的可视化模式,并尝试展示它们如何应用于 Kubernetes。请注意,有几种可应用于 Kubernetes 的可视化方法;这些只是示例,展示了如何表示在 Kubernetes 集群中收集的数据。

现在我们已经讲解了数据收集、聚合、关联和可视化,让我们探讨一些高级主题,利用收集的数据来洞察 Kubernetes 集群的运行情况。

分析和故障排除

在这一节中,我们将探讨分析应用程序,这些应用程序利用收集、聚合和关联组件来提供额外的洞察力。请注意,有许多应用程序可以构建以利用 Kubernetes 集群中的上下文丰富数据。我们提供一些应用程序作为示例。

分布式跟踪

我们之前解释过分布式跟踪,并讨论了它在基于微服务的架构中的重要性,在这种架构中,跟踪单个用户请求穿越需要在各种微服务之间发生的多个事务是至关重要的。有两种众所周知的实现分布式跟踪的方法,

仪器化事务请求头

在这种方法中,HTTP 头部被仪器化为一个请求 ID,并且请求 ID 在调用其他服务时通过头部保留。Envoy是一个非常流行的工具,用于实现分布式跟踪。它支持与其他知名应用程序跟踪器如 Lightstep 和 AWS X-Ray 的集成。如果您可以接受仪器化应用程序以在微服务之间的调用中添加和保留请求 ID,我们建议您使用 Envoy。

eBPF 和 kprobes

在使用 Envoy 描述的方法中,需要对应用程序流量进行更改。可以使用 eBPF 和 Linux 内核探针为服务间调用实现分布式跟踪。您可以将 eBPF 程序附加到内核中的 kprobes/uprobes 和其他跟踪点,并构建一个分布式跟踪应用程序。请注意,此类应用程序的详细实现超出了本书的范围,但我们想提到这是实现分布式跟踪的一个选项,以防您担心修改应用程序流量。

现在我们已经讲解了分布式跟踪,让我们看看如何在您的 Kubernetes 集群中实现数据包捕获。

数据包捕获

在你的 Kubernetes 集群中,我们建议您实施或选择一个支持 pod 之间原始数据包捕获的工具。该工具应支持基于选择器的数据包捕获(例如,pod 标签)和基于角色的访问控制,以便启用和查看数据包捕获。这是一个简单而非常有效的功能,可以作为响应事件(例如,应用程序延迟增加)的一种操作来分析原始数据包流,以理解问题并找到根本原因。为了实施原始数据包捕获,我们建议使用 libpcap,它支持在 Linux 系统上的接口上捕获数据包的能力。

结论

在本章中,我们讨论了什么是可观察性,以及如何为您的 Kubernetes 集群实施它。以下是本章的要点:

  • 监控需要成为您可观察性战略的一部分;仅仅进行监控是不够的。

  • 在实施可观察性解决方案时,利用 Kubernetes 的声明性特性非常重要。

  • 为实施 Kubernetes 集群的可观察性,关键组件包括日志收集、日志聚合与关联、可视化、分布式追踪和分析。

  • 您必须使用适用于 Kubernetes 的本地工具来实现您的可观察性。

  • 您应该使用 Linux 内核中提供的工具来驱动数据的高效收集和聚合(例如,NFLOG、基于 eBPF 的探针)。

第六章:观测性和安全性

本章将解释一个观测性平台如何帮助提升您的 Kubernetes 集群的安全性。我们将涵盖以下主题:

警报

在第五章中,我们介绍了实施日志收集的最佳实践。本章将重点讨论如何构建一个能够生成高保真警报的系统。我们还将讨论使用机器学习进行异常检测。

安全运营中心

我们将审查一个安全运营中心(SOC)的参考实现,以及观测性如何帮助您为 Kubernetes 集群构建 SOC。

行为分析

我们将介绍用户与实体行为分析(UEBA)的概念,以及如何在您的 Kubernetes 集群中实施它。

警报

在前一章中,我们讨论了如何为您的 Kubernetes 集群实施日志记录。一个有效的警报系统必须包括以下内容:

  • 系统应能够自动在各种日志数据源(例如 Kubernetes 活动日志、网络日志、应用程序日志、DNS 日志等)上运行查询。

  • 系统必须能够支持一个状态机,用于在指定持续时间内生成特定数目的阈值违规事件。系统还必须支持设置查询的时间段(称为回溯)。我们将在下一节中介绍一个示例。

  • 系统必须能够向外部安全、信息和事件管理(SIEM)导出可操作的警报,以便它们成为企业中的事件响应过程的一部分。

主要的云服务提供商提供的警报系统可以帮助您在云服务提供商环境中定义基于日志的警报。Google Cloud 提供了关于其警报能力的学习资源。Amazon Web Services(AWS)也有类似的警报能力。这些警报可以在云提供商的日志系统中收集的日志上定义规则,以基于阈值触发警报。例如,在给定时间段内 API 调用的数量可能表明可能存在拒绝服务(DoS)攻击。虽然这些系统适用于一般的日志记录和警报,但是对于 Kubernetes 的日志收集系统来说,像我们在第五章中讨论的那种本地化系统是必要的,以便在 Kubernetes 集群中检测基于安全性的事件,因为它在收集时相关数据,并且可以轻松地在一个日志源上定义警报。此外,一个适用于 Kubernetes 的警报系统将帮助您使用 Kubernetes 构建如部署、标签等的构造定义警报,因为它可以增强正确的上下文与查询网络活动服务的服务和 IP 的标签集。

图 6-1 和 6-2 展示了如何定义警报来检测您的 Kubernetes 集群中的横向移动的示例。

图 6-1. 配置警报操作

图 6-1 显示了如何配置警报的操作及元数据,如名称、描述、严重程度和时间段,以轮询数据和回溯期限。回溯期限指系统在查询数据时查看的时间跨度。您还可以定义在触发警报之前发生阈值违规的次数阈值。此外,能够配置输出格式也非常重要;在本例中,当报告警报时,数据将按流量来源(命名空间和部署)进行聚合。输出的警报数据有助于下游系统(例如 SIEM)管理警报。

图 6-2. 警报查询配置

图 6-2 展示了一个警报查询配置的示例。在查询中需要注意的是能够使用 Kubernetes 元数据(例如标签)与网络流活动(例如目的地、协议)和策略判断(例如操作)进行单一查询。这使您能够在定义有效的 Kubernetes 集群警报时具有极大的灵活性。请注意,这是如何考虑构建有效警报系统的代表性示例。除了云提供商的警报系统之外,还有多种其他工具如 Datadog、Sysdig 和 Calico Enterprise 提供了基于 Kubernetes 的警报系统。

我们之前介绍的警报系统非常擅长在系统具有可预测行为并且您可以轻松定义系统正常活动阈值时检测和报告警报。如果警报系统能够“学习”系统的行为并能够动态定义阈值,那将是非常有用的;这将有助于生成高保真度的警报,并减少由于阈值未随系统状态变化而产生的误报。让我们探讨一下机器学习如何解决这个问题。

机器学习

机器学习的基础知识及其工作原理超出了本书的范围。在本节中,我们将回顾一些机器学习的概念,这些概念将向我们展示它如何帮助学习给定指标的行为,并在偏离预期行为时发出警报。在我们进行这些回顾之前,让我们来看看机器学习中的高级技术:

监督学习

这是一种通过一段时间内标记测试数据来训练系统的技术。它允许系统利用所学来对新数据进行分类并预测结果。

无监督学习

这是一种使用算法来检测和分类未标记数据中模式的技术。请注意,有许多资源可以理解这些概念;其中一个例子是朱利安娜·德卢亚的“监督学习与无监督学习的区别是什么?”。考虑到 Kubernetes 集群中实体(例如 pod)的短暂性,以及我们检测来自该活动产生的数据中的异常的目标,我们建议使用无监督学习技术来检测异常。

基线设定

基线设定是一种在机器学习中用来连续预测给定度量标准(例如每秒连接数)的值并检测偏离的技术。CMU ML 的博客文章“3–Baselines”是理解基线设定如何工作以及可以使用基线设定构建哪些类型模型的好资源。正如博客中提到的,可以创建简单但非常有效的模型,以达到人类水平的性能。这正是我们在警报系统中想要的:系统应该自动定义阈值,并在偏离基线时发出警报。

现在我们了解了应该使用的高级技术之后,让我们看一些例子,这些例子可以帮助实现对 Kubernetes 集群的可观察性和安全性。在像 Kubernetes 这样的动态环境中,工作负载是短暂的,可以在不同节点上重新启动/调度,因此在大多数情况下,使用基于规则的引擎来检测异常是不切实际的。需要的是将异常检测引擎层叠在机器学习引擎之上,该引擎报告给定度量标准的基线偏离。

机器学习工作的例子

如何创建机器学习模型超出了本书的范围;您应该让数据科学团队为您的部署构建模型。像谷歌云这样的主要云服务提供商为 Kubernetes 工作负载提供了一个构建机器学习模型的服务,可以帮助数据科学团队实现合适的 ML 模型。以下是在您的集群中检测异常事件的一些有效的 ML 工作负载的示例:

IP 扫描检测

该工作负责查找您集群中向多个目标发送数据包的 pod。这可能表明攻击者已经控制了一个 pod,并正在收集有关其它可达目标的情报。该工作将 pod 与其复制集中的其他 pod,以及集群中的其他 pod 进行比较。

端口扫描检测

该工作负责查找您集群中向一个目标发送数据包的 pod。这可能表明攻击者已经控制了一个 pod,并正在收集有关其它可达目标的情报。该工作将 pod 与其复制集中的其他 pod,以及集群中的其他 pod 进行比较。

服务字节异常

该工作查找接收/发送异常高数据量的服务。这可能表明拒绝服务攻击、数据外泄或其他攻击。该工作查找相对于其副本集不寻常的服务,以及相对于集群其他部分不寻常的副本集。

进程重启异常

该工作查找具有过多进程重启的 Pod。这可能表明进程存在问题,如资源问题或攻击。该工作查找相对于其进程重启行为不寻常的 Pod。

DNS 延迟异常

该工作查找具有过高 DNS 请求延迟的客户端。这可能表明拒绝服务攻击。

L7 延迟异常

该工作查找具有过高 L7 请求延迟的 Pod。所有 HTTP 请求在此测量。此异常可能表明拒绝服务攻击或其他攻击。

HTTP 连接峰值异常

该工作查找接收过多 HTTP 入站连接的服务。这种异常可能表明拒绝服务攻击。

此列表提供了检测异常的作业示例。您可以使用Google Cloud 提供的此资源为您的 Kubernetes 集群构建机器学习作业。请注意,每个作业的描述依赖于一组与 Kubernetes 原生相关的上下文丰富的日志(例如,比较副本集中的 Pod 与其他 Pod,使用发送到/从服务的字节)。

现在我们已经讨论了如何构建有效的警报系统来检测和报告异常,让我们看一个基于 Kubernetes 集群的安全运营中心的示例实现。

安全运营中心

在本节中,我们将审查基于 Kubernetes 的 SaaS 服务的安全运营中心(SOC)的参考实现。SOC 用于检测和响应安全事件;我们将探讨在实施 SOC 时如何利用可观察性。请注意,这是一个示例,应用于指导您的实施。在生产中实施时,您应该使用这些概念,但需要设计和实施适合您用例的 SOC。图 6-3 展示了 Google Cloud 中托管的服务的 SOC 实现示例。

图 6-3. Google Cloud 中的 SOC 示例实现

图 6-3 显示了一组运行在 Google Cloud 中的 Kubernetes 集群,每个租户都有一个命名空间表示。每个租户集群可以部署在 Google Kubernetes Engine (GKE) 中,也可以作为 Google Cloud 中的上游 Kubernetes 集群。有一个入口表示外部实体如何访问服务。图中省略了工作负载部署和供应的详细信息,因为我们想重点关注如何确保服务的安全性。为了确保服务的安全性,您需要日志记录、监视和警报。这可以通过使用 Google Cloud 运营套件 实现,该套件提供支持日志记录、监视和警报的能力。如果您正在使用 GKE,Google Cloud 的博客 描述了如何利用这些服务来检测和管理 Kubernetes 集群的警报。如前所述,您需要利用机器学习进行基线制定,并提高警报的质量。Google 提供了一组称为 AI Hub 的机器学习服务。请注意,您仍然需要构建与您的 SaaS 服务相关且有效的机器学习模型(请参阅本章前面的示例 ML 作业)。然后,您可以使用众所周知的工具,如 OpsGenie,将警报路由到 SIEM、Slack、PagerDuty、JIRA 和其他工具进行警报管理。这些警报将触发由安全团队定义的修复工作流。请注意,我们以 Google Cloud 为例,但您可以使用前面提到的方法为 AWS 和 Azure 构建 SOC。这些云提供商也为用户提供了类似的一组服务。

如前所述的方法在您仅使用一个云提供商并且没有任何工作负载在本地或其他云提供商环境中时非常有效。此外,前面提到的所有服务都会增加部署成本,您还需要委派部分 DevOps/DevSecOps 资源来实施和管理这些服务。因此,我们建议您使用一种不特定于任何云提供商且可以跨云提供商/本地环境使用的工具来构建您的 SOC。

图 6-4 显示了如何替换一些特定于云提供商的组件,并使用基于 Kubernetes 的可观测性和安全平台创建 SOC。您可以自行构建这个平台,也可以选择使用产品,例如 Datadog、VMware 和 Calico Enterprise 提供的平台。选择产品时,请记住前一节涵盖的警报概念,并确保平台支持与您的修复/管理系统的集成。

现在我们已经回顾了如何构建一个对您的 Kubernetes 集群有效的 SOC,让我们再来看看如何将可观测性应用于保护您的 Kubernetes 集群。

图 6-4. 使用 Kubernetes 原生平台的 SOC

用户和实体行为分析

用户和实体行为分析(UEBA)是一个领域,在此领域中,您使用基于 ML 和 AI 的技术来分析用户或实体(如 pod、服务或部署)随时间的行为,并通过用户/实体的异常行为检测来检测异常行为。Microsoft Azure 作为其云平台的一部分提供 UEBA。Microsoft Azure 的博客文章,《在 Azure Sentinel 中使用用户和实体行为分析(UEBA)识别高级威胁》(https://oreil.ly/FDspV)是一篇优秀的资源,描述了如何在安全用例中使用 UEBA。请注意,实体的异常行为并不总是可疑行为;您需要将行为映射到如 MITRE 攻击框架或其他妥协指标的框架,以确认它是一个安全问题。

让我们举一个简单的例子,说明如何在 Kubernetes 中为实体(如服务)实施 UEBA。

图 6-5 展示了你的 Kubernetes 集群中的一个服务以及我们在分析服务行为时将考虑的各种交互。作为其正常操作的一部分,该服务将与 Kubernetes API 服务器和 Kubernetes 数据存储交互。此外,它将与入口资源交互,以与集群外的实体通信,并使用集群网络与集群内的其他实体交互。该服务还将使用集群中的 DNS 服务进行操作。

图 6-5. Kubernetes 服务行为分析

为了为服务建立一个档案,我们需要考虑服务的以下方面。这些在机器学习中被称为特征。

  • 服务组成(如 pod 的端点数、RBAC、策略)

  • 对服务的文件系统活动、进程信息和系统调用活动

  • 与服务相关联的服务账户

  • 服务的生命周期操作(例如创建、删除、缩放)

  • 向服务的流量(网络、应用程序)和从服务的流量

  • 服务中 pod 的 DNS 活动

图 6-5 中显示的 UEBA 引擎将从各种数据源(网络流日志、应用程序流日志、Kubernetes 审计日志、DNS 活动日志、进程信息、文件系统、系统调用活动日志)收集日志,并将它们存储在数据存储中。这些日志由分析引擎聚合和关联,以生成服务在各种特征上的关联日志。

机器学习引擎使用复杂模型来基线服务在各种特征上的行为。这是机器学习中的一个高级概念,其中模型考虑每个特征及其相互作用等因素来构建服务的配置文件。这最好由您的数据科学团队实施。然后,此配置文件用于预测异常并生成偏离的警报。有一个仪表板允许 SOC 操作员审查分析数据并用于取证或威胁狩猎。请注意,使用第五章描述的概念构建的安全和可观察性平台将有助于构建有效的 UEBA 系统。

UEBA 是一种高级技术,实施起来复杂,但却是快速查找集群中潜在脆弱实体的非常有效方式。这使得 SOC 运营非常高效和可扩展。一旦您的部署扩展到多个集群(50+),使用警报/仪表板手动审查以查找实际问题就不再实际。UEBA 将警告您有异常且需要立即关注的实体。

结论

当您考虑如何利用可观察性来帮助保护您的集群时,请考虑以下几点:

  • 您使用的警报系统必须是基于 Kubernetes 的,并且必须支持使用机器学习进行基线设置,以便您无需手动定义各种特征的阈值。

  • 建议您考虑选择一个基于 Kubernetes 的平台,可以跨云和本地部署,以建立您的 SOC。

  • UEBA 是一个高级概念,实施起来复杂,但在保护 Kubernetes 集群方面非常有效。

第七章:网络策略

在本章中,我们将描述网络策略并讨论其在保护 Kubernetes 集群中的重要性。我们将回顾各种网络策略的实现和支持网络策略实施的工具。我们还将介绍带有示例的网络策略最佳实践。

什么是网络策略?

网络策略是保护 Kubernetes 网络的主要工具。它允许您轻松地限制集群中的网络流量,使只有您希望流动的流量被允许。

要理解网络策略的重要性,让我们简要探讨一下在引入网络策略之前如何典型地实现网络安全。在企业网络的历史上,网络安全是通过设计网络设备(交换机、路由器、防火墙)的物理拓扑及其相关配置来实现的。物理拓扑定义了网络的安全边界。在虚拟化的第一阶段中,云中同样对网络和网络设备构造进行了虚拟化,并使用相同的技术来创建(虚拟)网络设备的特定网络拓扑,以提供网络安全。添加新的应用程序或服务通常需要更新网络拓扑和网络设备配置,以提供所需的安全性。

相比之下,Kubernetes 网络模型定义了一个“扁平”的网络,在这种网络中,默认情况下,每个 Pod 可以直接与集群中的所有其他 Pod 进行通信。这种方法极大地简化了网络设计,并允许新的工作负载在集群中的任何位置动态调度,而无需依赖于网络设计。

在这种模型中,与其通过网络拓扑边界来定义网络安全,不如使用独立于网络拓扑的网络策略来定义。网络策略进一步通过使用标签选择器作为其主要机制来定义哪些工作负载可以与哪些工作负载通信,而不是使用 IP 地址或 IP 地址范围。

可以将网络策略执行视为每个 Pod 都受其自身的专用虚拟防火墙保护,并根据已定义的网络策略自动编程和实时更新。图 7-1 展示了在 Pod 上使用其专用虚拟防火墙进行网络策略执行的情况。

图 7-1. 由虚拟防火墙保护的 Pod

为什么网络策略很重要?

在攻击者变得越来越复杂的时代,作为防线的网络安全比以往任何时候都更为重要。

虽然您可以(而且应该)使用防火墙来限制网络的边界流量(通常称为南北流量),但由于 Pod 调度的动态性和 Pod IP 地址,它们通常只能以集群整体的粒度进行 Kubernetes 流量的管控,而不能针对特定的 Pod 组进行细粒度管控。此外,一旦攻击者在边界内部获得小小的立足点,他们的大多数目标是横向移动(东西流量)以获取对更高价值目标的访问权限,而基于边界的防火墙无法对其进行管控。随着应用架构从单块到微服务的演变,东西流量的数量和因此横向移动的攻击面仍在继续增长。

另一方面,网络策略设计用于 Kubernetes 的动态特性,遵循标准的 Kubernetes 范式,使用标签选择器来定义 Pod 组,而不是 IP 地址。由于网络策略在集群内部执行,它可以保护南北和东西流量。

网络策略代表了网络安全的重要进步,不仅因为它处理了现代微服务的动态特性,而且因为它使开发和 DevOps 工程师能够轻松地自定义网络安全,而无需学习低级网络细节。网络策略使得定义意图变得容易,例如只有这个微服务能连接到数据库,将该意图编写为代码(通常是 .yaml 文件),并将网络策略的编写集成到 Git 工作流程和 CI/CD 过程中。

网络策略实现

Kubernetes 定义了一个标准的网络策略 API,因此您可以在任何集群上期望一组基本特性。但 Kubernetes 本身除了存储外不对网络策略进行任何操作。网络策略的执行委托给了网络插件,允许一系列实现。大多数网络插件支持 Kubernetes 网络策略的主要元素,尽管许多插件并未实现规范的每个功能。值得注意的是,大多数实现与网络插件特定的 Pod 网络实现耦合。然而,某些网络策略实现可以在多种不同的 Pod 网络插件上强制执行网络策略。图 7-2 显示存储在 Kubernetes 数据存储中的网络策略,由网络插件用于执行。

图 7-2. 由网络插件实施的存储在 Kubernetes 中的网络策略

有多种网络和网络策略实现可供选择,如图 7-3 所示。

图 7-3. 顶级网络技术实现的采纳情况

无论您选择哪种网络策略实现,我们建议出于以下原因选择其中一种:

  • 它实现了完整的 Kubernetes 网络策略规范。

  • 除了支持 Kubernetes 网络策略规范外,其自身的策略模型还提供额外的功能,可以与 Kubernetes 网络策略一起使用,支持额外的企业安全用例。

  • 一些网络插件,如 Weave Net、Kube-router 和 Calico,可以在其自身丰富的网络功能集之上强制执行网络策略,也可以在包括亚马逊的弹性 Kubernetes 服务(EKS)、Azure Kubernetes 服务(AKS)和谷歌 Kubernetes 引擎(GKE)在内的几个其他网络选项上强制执行。这使它们成为多云策略的特别强大选择,因为它使您可以从广泛的选项中选择最适合您环境的网络,并在所有环境中使用相同丰富的网络策略功能。

  • 网络策略可以应用于主机端点/接口,允许使用相同的灵活策略模型来保护 Kubernetes 节点或非集群主机/虚拟机。

  • 它支持在网络/基础设施层以及上层(包括支持 L5-L7 匹配标准的策略规则,如 HTTP 方法和路径)强制执行的网络策略。多个执行点有助于保护您的基础设施免受受损工作负载的影响,并保护您的工作负载免受受损基础设施的影响。它还避免了需要在应用和基础设施层分别进行安全配置的需要,或者需要学习每一层不同的策略模型。

  • 它需要是生产级的,这意味着它必须在任何大小的集群中表现出色,从单节点集群到数千节点集群。

  • 它提供企业增加新功能的能力,并作为企业级 Kubernetes 网络安全解决方案的基础组件。

网络策略最佳实践

在本节中,我们将探讨如何使用示例实现网络策略,并涵盖实施的最佳实践。以下示例使用 Calico 网络策略模式,该模式扩展了 Kubernetes 网络策略模式。我们之所以选择这些示例,是因为我们熟悉 Calico 网络策略,但这些最佳实践也可以与其他可用的网络策略模型一起实现。

入口和出口

当人们考虑网络安全时,第一个想法往往是如何保护工作负载免受南北向的外部攻击者的侵害。为了帮助防御此类攻击,您可以使用网络策略来限制可以从集群外部访问的任何 Pod 的入口流量。

然而,当攻击者成功找到漏洞时,他们通常会使用受损的工作负载作为横向移动的起点,探测您网络的其余部分,以利用额外的漏洞来访问更有价值的资源或提升权限以发动更强大的攻击或窃取敏感数据。

即使您在集群中的所有 Pod 上设置了限制入口流量的网络策略,横向移动可能会针对集群外保护较弱的资产。因此,最佳实践是始终为集群中的每个 Pod 定义入口和出口网络策略规则。

尽管这并不能保证攻击者无法找到额外的漏洞,但它确实大大减少了可用的攻击面,使攻击者的工作变得更加困难。此外,如果结合适当的策略违规警报,可以大大缩短确定工作负载已受损的时间。以 2020 年 IBM 数据泄露成本报告为例,IBM 报告企业平均需要 207 天才能发现一次数据泄露,然后需要额外的 73 天来进行控制!通过正确编写的网络策略和违规警报,可以防止或大大减少泄露,潜在地将其减少到几分钟甚至几秒钟,并且甚至可以自动响应以隔离可疑工作负载。

不仅仅是使命关键的工作负载

最佳实践已建议确保每个 Pod 都具有限制其入口和出口流量的网络策略。这意味着当您考虑如何保护使命关键的工作负载时,您确实需要保护所有工作负载。如果不这样做,那么一些看似不重要、无害的工作负载最终可能成为攻击您网络其余部分的基础,最终导致最关键工作负载的失败。

策略和标签模式

Kubernetes 标签和网络策略的一个优势是您可以以多种方式灵活使用它们。然而,结果是通常存在多种标记和编写策略的方式可以实现相同的特定目标。因此,另一个最佳实践是考虑使用一致的模式或设计模式标准化标记 Pod 和编写网络策略的方式。这可以使编写和理解每个网络策略的意图变得更加简单明了,特别是如果您的集群托管大量微服务时。

例如,您可以指定每个 Pod 都将具有标识其所属微服务的“app”标签,并且每个 Pod 将使用该 app 标签应用单一网络策略,该策略使用该 app 标签定义与其预期交互的微服务的入口和出口规则:

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: back-end-policy
  namespace: production
spec:
  selector: app == 'back-end'
  ingress:
  - action: Allow
    protocol: TCP
    source:
      selector: app == 'front-end'
    destination:
      ports:
        - 80
  egress:
  - action: Allow
    protocol: TCP
    destination:
      selector: app == 'database'
      ports:
        - 80

或者,您可以决定在策略规则中使用权限样式标签,而不是在其入口规则中列出允许访问每个服务的微服务。

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: database-policy
  namespace: production
spec:
  selector: app == 'database'
  ingress:
  - action: Allow
    protocol: TCP
    source:
      selector: database-client == 'true'
    destination:
      ports:
        - 80
  egress:
  - action: Deny

这可以使得单独的微服务团队能够编写自己的网络策略,而无需知道必须消费该服务的其他微服务的完整列表。

还有很多其他方法可以解决这个问题,这里没有对错之分。但是,在前期定义如何标记和定义网络策略的方法会使得长期来看生活变得更加轻松。

如果不确定哪种方法最适合您,那么“应用程序”方法是一个很好的起点。稍后总是可以扩展为包括为那些具有大量客户端的微服务定义权限样式标签的想法,如果维护策略规则变得耗时。

默认拒绝和默认应用策略

Kubernetes 网络策略规范允许所有入口 Pod 流量通过,除非存在一个或多个适用于该 Pod 的入口规则的网络策略,然后只有策略明确允许的入口流量才被允许。出口 Pod 流量同理。因此,如果忘记为新的微服务编写网络策略,它将保持不安全状态。如果忘记为该微服务编写入口和出口规则,那么它将部分保持不安全状态。

鉴于此,一个良好的做法是制定一个“默认拒绝策略”,防止未经其他网络策略明确允许的任何流量。通常的做法是制定一个适用于所有 Pod 的策略,包括入口和出口规则,但本身不明确允许任何流量。因此,如果没有其他明确允许流量的网络策略适用,那么流量将被拒绝:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  Namespace: my-namespace
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

由于 Kubernetes 网络策略是有命名空间的,前面提到的网络策略需要为每个命名空间重复,并且最好纳入集群中新命名空间配置的标准操作流程中。另外,一些网络策略实现超越了 Kubernetes 网络策略,提供了指定整个集群范围网络策略的能力(不限于单一命名空间)。下面的示例展示了如何创建一个策略,将整个集群切换到默认拒绝行为,包括将来创建的任何命名空间:

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: default-deny
spec:
  selector: all()
  types:
  - Ingress
  - Egress

然而,值得注意的是,这个策略适用于所有 Pod,而不仅仅是应用 Pod,包括 Kubernetes 的控制平面 Pod。如果在创建此类策略之前没有正确的网络策略或配置故障安全端口,可能会导致集群出现严重问题。

一个风险较低的最佳实践是定义一个仅适用于 Pod 的网络策略,排除控制平面 Pod。除了触发默认拒绝行为外,此策略还可以包括您希望应用于所有应用 Pod 的任何规则。例如,您可以包括一个允许所有应用 Pod 访问 kube-DNS 的规则。这有助于简化需要编写的每个微服务策略,使其能够专注于所需的每个微服务特定行为:

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: default-app-policy
spec:
  namespaceSelector: has(projectcalico.org/name) &&
projectcalico.org/name not in {"kube-system", "calico-system"}
  types:
  - Ingress
  - Egress
  egress:
    - action: Allow
      protocol: UDP
      destination:
        selector: k8s-app == "kube-dns"
        ports:
        - 53

由于此策略故意排除控制平面组件,为了保护控制平面,您可以为每个控制平面组件编写特定的策略。最好在集群创建时进行此类策略创建,而不是在集群托管工作负载之前,因为策略出错可能会使您的集群处于破损状态,可能导致重大的生产中断。此外,强烈建议在开始尝试为控制平面创建任何策略之前,始终确保已设置了您使用的网络插件的正确故障转移端口。

策略工具化

在这一节中,我们将探讨可用于有效向您的 Kubernetes 集群添加网络策略的工具。

开发过程和微服务的好处

与传统网络安全控制相比,网络策略的一个优点是定义网络策略不需要网络或防火墙专业知识。网络策略使用与其他 Kubernetes 资源相同的概念和范式。理论上,任何熟悉在 Kubernetes 中部署微服务的团队都可以轻松掌握网络策略。因此,网络策略代表了采用向左转移(shift-left)哲学的机会,其中网络安全在开发周期的早期定义,而不是在过程的后期定义。这是安全和开发团队合作以保护您的 Kubernetes 集群的绝佳机会。

与此同时,许多组织正在从单体应用架构转向微服务架构,其中一个目标通常是增加开发和组织的灵活性。在这种方法中,每个微服务通常由一个开发团队维护,该团队对微服务有显著的专业知识,但不一定了解微服务所属应用的整体。微服务的转移与网络策略的向左转移机会互补。负责开发微服务的团队通常很了解其消耗和依赖的其他微服务。他们可能也很了解哪些微服务消耗他们的微服务。

当与明确定义的、标准化的政策和标签模式方法结合时,这使他们在实施微服务网络策略时处于一个强势位置,作为微服务开发过程的一部分。在这种模型中,网络策略被视为内嵌并在开发过程中进行测试的代码,就像微服务代码的任何其他关键部分一样。

同样有效的方法是让开发团队专注于他们负责的微服务的内部,并将微服务的操作责任留给 DevOps 团队。然而,相同的理念仍然适用。这样的 DevOps 团队通常需要深入了解他们负责操作的微服务之间的依赖关系,以管理应用程序的运行和微服务的生命周期。网络安全可以由 DevOps 团队定义为代码,并在使用之前像他们开发的其他操作代码或脚本一样进行测试。

当然,今天的现实是,许多组织在实现微服务、敏捷和左移安全的天堂方面还有很长的路要走。网络安全可能在组织的流程中来得更晚,甚至作为已经投入生产的系统的事后想法。在这种情况下,定义网络策略可能会更具挑战性,错误的网络策略可能会对生产造成重大影响。好消息是,有一系列工具可帮助管理网络策略的生命周期,使这一过程更容易,包括政策推荐、政策影响预览和政策暂存/审核模式。

政策建议

政策推荐工具在那些负责网络安全的团队对他们需要保护的应用程序或微服务之间所有网络依赖关系没有很好、自信理解的情况下提供了极大的帮助。这些工具还帮助您正确开始编写网络策略,并使网络策略的创建比手工编写容易得多。

推荐工具通常的工作方式是分析每个微服务在一段时间内与其他微服务之间的网络流量。这意味着要获得推荐,微服务需要在生产中运行,或者在能够准确反映微服务与应用程序其余部分之间生产交互的暂存或测试环境中运行。

有许多可供选择的政策推荐工具,通常具有不同程度的复杂性、对 Kubernetes 的认识程度和政策模式方法。建议您使用集成到您的网络策略解决方案中的 Kubernetes 感知政策推荐引擎。

政策影响预览

政策影响预览工具在应用到集群之前提供了一种检查网络策略的方法。类似政策建议,通常通过分析一段时间内集群的历史网络流量来完成,计算新策略可能会影响哪些网络流。例如,识别以前允许但现在会被拒绝的流量,以及以前被拒绝但现在会被允许的流量。

政策影响预览在任何情况下都是极大的帮助,特别是在你并非完全依赖政策建议的场景下。例如,当你手动定义网络策略或者修改政策建议以与特定的政策和标签模式标准一致时,这将非常有用。即使团队对微服务的网络策略定义有很高的信心,了解微服务的网络依赖,政策影响预览也是无价的,它有助于捕捉任何意外错误,例如难以发现的拼写错误,这些错误可能会显著影响合法的网络流量。

政策影响预览工具比政策建议更少见。使用提供基于分析所收集的流日志数据的影响可视化表示的工具非常有用,无论在任何所需的时间段内。这将有助于减少由于不正确撰写的政策或由于操作员错误而导致的停机问题。

政策分阶段和审计模式

甚至比政策影响预览更少见但可能更有价值的是支持政策分阶段,有时称为政策审计模式。

政策分阶段允许应用于集群的网络策略变更,而不会影响网络流量。然后,分阶段的策略会记录其将与之交互的所有流的详细信息,而实际上不会影响任何流。在需要对个别政策进行历史数据的政策影响预览可能过于简单化的应用程序运行复杂性的情况下,这非常有用。例如,如果需要同时更新多个相互依赖的策略,或者希望使用实时而非历史网络流来监视策略影响。

为了使撰写有效网络策略的任务不再艰巨,你需要使用政策建议,然后将策略分阶段以了解其影响,然后再将其推广以供执行。这种基于历史网络流量的政策建议循环,随后是分阶段(将策略应用于当前和未来的网络流),然后进行所需的调整,最后实施策略,是确保策略变更能够确切达到预期效果的最佳方式。

结论

在本章中,我们讨论了网络策略的重要性以及各种网络策略的实施和工具,以帮助您进行实施。以下是网络策略的一些关键方面:

  • 网络策略应该用于保护 Kubernetes 网络,并且它与在集群外围实施的防火墙相辅相成。

  • 建议选择一个能够识别 Kubernetes 的实现,扩展基本的 Kubernetes 网络策略。

  • 有许多网络策略实施提供工具,帮助在 Kubernetes 集群中实施网络策略。

第八章:管理团队之间的信任

在前一章中,我们探讨了网络策略如何代表一种采用左移哲学来进行网络安全的机会,即由开发周期早期的团队定义安全,而不是由安全团队在过程晚期定义和维护安全。这种方法可以带来许多好处,但为了可行性,需要在涉及的团队之间建立相应的信任程度和责任划分。

在大多数组织中,将安全责任的 100%向左移动并不现实,其他团队(平台、网络和安全)不会完全摆脱安全责任。例如,尽管可能会将个体微服务安全的较低级别细节的责任向左移动,但安全团队仍可能负责确保您的 Kubernetes 部署具有符合内部和外部合规要求的安全姿态。

一些企业通过定义内部流程来处理这个问题,例如确保安全团队在应用安全更改之前审查所有安全更改。这种方法的缺点是可能会降低敏捷性,这与左移的动机之一相矛盾,即增加敏捷性。

幸运的是,在 Kubernetes 环境中可以设置各种类型的防护栏,从而减少对这些传统流程控制的需求。在本章中,我们将探讨一些这些能力以及它们如何用于控制在安全左移方法背景下从一个团队委托给另一个团队的信任程度。

基于角色的访问控制(RBAC)

Kubernetes 基于角色的访问控制(RBAC)是定义个别用户或用户组在 Kubernetes 集群中被允许做什么的主要工具。RBAC 权限是使用角色定义的,并通过角色绑定授予给用户或用户组。每个角色包括一系列资源(按资源类型指定,集群范围内,在命名空间内,甚至是特定的资源实例)以及每个资源的权限(例如,获取、列出、创建、更新、删除等)。

许多 Kubernetes 资源是有命名空间的,包括部署、守护进程集、Pod 和 Kubernetes 网络策略。这使得命名空间成为团队之间理想的信任边界。关于如何使用命名空间没有固定的规则,但一种常见的做法是为每个微服务使用一个命名空间。然后可以使用 RBAC 授予权限,以便团队能够管理对应微服务的命名空间中的资源。

如果安全已经向左移动,这通常包括管理适用于微服务的网络策略的权限,但不包括管理他们不负责的微服务适用的任何网络策略。

如果对入口和出口流量都遵循了默认拒绝式最佳实践,那么团队不能忘记编写网络策略,因为没有网络策略,微服务将无法正常工作。此外,由于其他团队已定义了覆盖其负责的微服务的入口和出口流量的等效网络策略,因此仅当两个团队都指定了允许该流量的网络策略时,两个微服务之间的流量才被允许。这进一步控制了每个团队委托的程度。

当然,根据安全左移的程度,定义网络策略的责任可能由不同的团队承担,而不是负责操作微服务的团队。同样,Kubernetes RBAC 可以用于轻松反映这种责任分担。

Kubernetes 网络策略的限制

使用 RBAC 与 Kubernetes 网络策略在左移环境中存在一些值得注意的限制:

  • 默认拒绝式策略需要在为命名空间提供服务时创建。负责为微服务定义网络策略的团队还可以修改或删除此默认策略。

  • 网络策略是基于 IP 的,不能使用完全合格的域名 (FQDN)。这在定义到集群外部资源的策略时可能是一个限制。

  • Kubernetes RBAC 控制资源的访问,但不限制资源的内容。在网络策略的上下文中尤为相关的是 pod 标签,因为它们被用作网络策略规则中识别其他微服务的主要机制。例如,如果一个团队为其微服务编写了一个网络策略,允许来自具有特定标签的 pod 的流量访问它,那么理论上任何有权限管理 pod 的团队都可以将该标签添加到其 pod 中,并获得对该微服务的访问权限。通过始终在策略规则中使用命名空间部门并且有选择地授予哪些团队修改命名空间标签的权限,可以减少这种暴露。

如果已定义了标准化的策略和标签模式,并且团队被信任遵循它们,那么这些限制更多是理论上的问题而不是实际问题。然而,对于某些组织来说,它们可能代表了真正影响其安全需求的问题。这些组织因此可能希望利用超出 Kubernetes RBAC 和 Kubernetes 网络策略之外的额外能力。特别是,他们可能考虑:

  • 更丰富的网络策略实现支持额外的网络策略类型、匹配条件和非命名空间网络策略,这些开放了更多的选项来分配责任和 RBAC 给团队。

  • 准入控制器可在资源内的每个字段级别强制执行控制,例如确保遵循标准化的网络策略和标签模式,包括限制团队使用特定标签。

我们现在将审查扩展 Kubernetes 网络策略的网络策略实现及如何使用它们来管理信任。

更丰富的网络策略实现

一些网络策略实现支持 Kubernetes 网络策略及其自定义网络策略资源,可与或取代 Kubernetes 网络策略使用。根据实现情况,这些可能会为如何分割责任和跨团队使用 RBAC 提供额外选项。有些供应商提供更丰富的网络策略实现,支持 Kubernetes 网络策略并添加更多功能(例如,Weave Net,Kube-router,Antrea,Calico)。我们鼓励您审查这些内容,并选择最符合您需求的最佳选项。在本节中,我们将通过 Calico 来看具体示例,因为它是最广泛部署的容器网络插件。

Calico 支持 Kubernetes 网络策略功能集,以及其自身的 Calico 网络策略资源,可与 Kubernetes 网络策略并存使用。Calico 网络策略有两种类型,均属于 projectcalico.org/v3 API 组:

NetworkPolicy

这些策略是命名空间作用域的(与 Kubernetes 网络策略类似)。

GlobalNetworkPolicy

这些策略适用于整个集群,与命名空间无关。

Calico 网络策略的两种类型均支持超出 Kubernetes 网络策略的共同功能集,包括:

  • 比 Kubernetes 网络策略具有更丰富的匹配条件集,例如能够匹配 Kubernetes 服务账户。

  • 明确允许、拒绝或记录策略规则中的操作,而不是隐式始终允许的 Kubernetes 网络策略操作。

  • 优先顺序排序以定义网络策略对同一工作负载应用时的评估顺序。(请注意,如果仅使用 Kubernetes 网络策略,或者 Calico 策略只包含允许操作,则评估顺序对策略结果没有影响。但是,一旦存在任何带有拒绝操作的策略规则,顺序就变得重要起来。)

我们想提到还有其他扩展 Kubernetes 网络策略的网络策略实现,如 Antrea,提供 ClusterNetworkPolicy(类似于 GlobalNetworkPolicy)。

以下示例展示了如何使用 Kubernetes RBAC 实现网络策略。在示例中,您可以基于分配给服务账号的标签来控制网络访问。在 Kubernetes 中,Pod 与其关联的服务账号相关联,因此可以通过服务账号来识别 Pod。您应使用 RBAC 来控制哪些用户可以为服务账号分配标签。示例中的网络策略使用分配给服务账号的标签来控制网络访问。具有 intern 服务账号的 Pod 只能与标记为role == intern的服务账号的 Pod 通信。

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: restrict-intern-access
  namespace: prod-engineering
spec:
  serviceAccountSelector: 'role == "intern"'
  ingress:
    - action: Allow
      source:
        serviceAccounts:
          selector: 'role == "intern"'
  egress:
    - action: Allow
      destination:
        serviceAccounts:
          selector: 'role == "intern"'

通过这种方式,您可以扩展 RBAC 的概念,该概念用于控制服务账号访问 Kubernetes 资源的网络访问。这是一个两步过程。RBAC 用于控制对服务账号的标签分配,而基于标签的服务账号选择器用于控制网络访问。可以利用这些额外的功能与 Kubernetes 网络策略结合使用,更清晰地分离高级集群运维或安全团队与各个微服务团队之间的责任。

例如,可能如下所示:

  • 将集群运维或安全团队的 RBAC 权限扩展至管理 Calico 网络策略,以覆盖整个集群范围,使其能够定义设定集群整体安全姿态的基本高级规则。例如,像在第七章讨论的默认拒绝式应用策略和限制集群出口到特定 Pod 的策略。

  • 将每个微服务团队授予 RBAC 权限,以在微服务的命名空间中定义 Kubernetes 网络策略,使他们能够为其负责的微服务定义自己的细粒度约束。

在基础的网络策略 RBAC 权限分配之上,集群运维或安全团队可以通过使用命名空间或服务账号标签来定义规则,而不是简化匹配 Pod 标签,为每个微服务团队分配不同的信任级别。例如,他们可以使用服务账号标签来定义限制集群出口到特定 Pod 的策略,并授权各个微服务团队使用但不编辑分配给其命名空间的任何服务账号。通过这种机制,一些微服务团队可能被授予权限选择性地允许其某些 Pod 的集群出口,而其他团队则没有同样的权限。

图 8-1 提供了这些思想如何结合的简化示例。

虽然这些功能相当强大,但在一些组织中,跨团队责任的分割可能更为复杂,特别是在团队层级更多的情况下。例如,合规团队、安全团队、集群运维团队和各个微服务团队可能具有不同层次的责任。为了更轻松地满足这些要求,一种方法是使用支持分层网络策略概念的网络策略实施。

图 8-1. 展示了使用网络策略实现信任边界的示例

有一些商业实现支持使用策略层级实现分层网络策略。类似的概念(分层命名空间和策略)也在 Kubernetes 社区中讨论。可以为每个层级的 RBAC 定义,以限制谁可以与该层级进行交互。在此模型中,网络策略被分层在多个层级中,按照定义的顺序进行评估,以匹配组织责任分割的需要。可以为每个层级的 RBAC 定义,以限制谁可以与该层级进行交互。每个层级的网络策略可以做出允许或拒绝决策(终止后续策略的评估),或将决策传递给层级结构中的下一个层级,以根据该层级中的策略进行评估。

图 8-2 提供了一个简化的示例,展示了如何使用这些功能来在组织内部的三个不同责任层之间分割责任。

图 8-2. 使用层级实现分层网络策略

准入控制器

我们已经讨论了定义和遵循标准化网络策略和标签模式的价值。早期讨论的团队之间责任分割的方法是围绕资源和命名空间级别的 RBAC,团队在被允许管理的资源和命名空间范围内具有自由度,因此不能保证所有团队都遵循了这些模式。

Kubernetes 本身没有内置能力以在这种粒度级别强制执行限制,但它支持准入控制器 API,允许将第三方准入控制器插入 Kubernetes API 机制,以在创建、更新和删除操作期间执行对象的语义验证。您还可以使用准入控制器,也称为变异准入控制器,用于修改已准入的对象。

例如,在实施网络策略的上下文中,准入控制器可以帮助以下操作:

  • 验证网络策略是否具有符合组织试图遵循的最佳实践的入口和出口规则。

  • 确保每个 Pod 都具有一组特定的标签,以符合组织定义的标签标准。

  • 限制不同组的用户对特定标签值的访问。

但是准入控制器的安全用例不仅限于网络策略。例如,Kubernetes 服务包括支持使用服务的 ExternalIP 字段关联的任意 IP 地址。如果没有一定程度的监管,这是一个非常强大的功能,可能会被具有 RBAC 权限创建和管理 Kubernetes 服务的任何人恶意使用,拦截 Pod 流量并将其重定向到 Kubernetes 服务。根据涉及团队内的信任水平,使用准入控制器对此进行监管可能是必要的。

根据组织的技能组合和具体需求,有几种准入控制器实现选项:

  • 如果存在专门满足组织需求的预先存在的第三方准入控制器,则使用它

  • 编写一个根据组织需求优化的自定义准入控制器

  • 使用具有丰富策略模型的通用准入控制器,可以映射到广泛的用例范围

对于许多场景来说,选择一个通用准入控制器可以在灵活性和编码复杂性之间取得良好的平衡。例如,您可以考虑 Kyverno,它具有专为 Kubernetes 设计的策略引擎,或者围绕 Open Policy Agent 构建的准入控制器,其策略模型使用 Rego 定义灵活的匹配和语言能力。

尽管准入控制器非常强大,但通常建议仅在确实需要时才实施它们。对于某些组织来说,以这种方式使用准入控制器可能过度,考虑到团队之间的责任和信任水平。对于其他组织来说,它们可能是满足内部合规要求的重要工具,并且使用它们的理由非常明确。

结论

Kubernetes 安全需要各个团队实施,并需要团队之间的协作。本章涵盖了以下关键概念:

  • 您应该使用 RBAC 和网络策略来定义边界,以帮助您管理跨团队的活动。

  • 您可以通过利用网络策略中的服务账户来扩展 RBAC 的概念,以控制网络访问,帮助您管理信任。

  • 准入控制器有助于控制访问并在各个团队之间实施信任边界。

  • 开发、平台和安全团队之间的协作对于实施安全非常重要。

第九章:将服务暴露给外部客户端

在早期的章节中,我们探讨了网络策略是保护 Kubernetes 的主要工具之一。这适用于集群内部的 pod-to-pod 流量(东西向流量)以及 pod 与集群外部实体(南北向流量)之间的流量。对于所有这些流量类型,最佳实践都是相同的:使用网络策略来限制允许的网络连接到最窄的需要范围,因此只有您期望和需要的特定应用程序或微服务的连接才被允许。

对于需要由集群外部客户端访问的 pods,这意味着限制连接:

  • 与相应微服务期望接收传入连接的特定端口(s)。

  • 从需要连接到微服务的特定客户端。

特定的微服务通常只在企业内部(无论是本地还是公共云)被有限数量的客户端消耗是常见的。在这种情况下,理想情况下 Kubernetes 网络策略规则应该限制传入连接到与客户端相关的 IP 地址或 IP 地址范围。即使微服务暴露给公共互联网(例如,为公共可访问的 SaaS 或网站暴露前端微服务),仍然有一些情况需要在一定程度上限制访问。例如,基于合规性原因可能需要阻止某些地理位置的访问,或者可能希望阻止已知的不良行为者或威胁源的访问。

不幸的是,实施这一最佳实践的方式需要考虑使用哪些网络插件和 Kubernetes 原语来将微服务暴露在集群外部。特别是,在某些情况下,原始客户端源 IP 地址会被保留到达 pod,这使得 Kubernetes 网络策略能够轻松地限制对特定客户端的访问。在其他情况下,客户端源 IP 地址可能会被与网络负载平衡相关的网络地址转换(NAT)或与应用层负载平衡相关的连接终止所模糊。

在本章中,我们将探讨在三种主要选项中暴露应用程序或微服务到集群外部时可用的不同客户端源 IP 行为:直接 pod 连接、Kubernetes 服务和 Kubernetes Ingress。

了解直接 pod 连接

Pods 直接被集群外的客户端访问而不是通过 Kubernetes 服务或 Kubernetes Ingress 访问相对较少见。然而,在某些场景下可能希望或需要这样做。例如,某些类型的分布式数据存储可能需要多个 pod,每个 pod 都有特定的 IP 地址,可以配置用于数据分发或客户端对等连接。

支持从集群外部直接连接到 pod IP 地址需要使用使 pod IP 地址在集群边界之外可路由的 pod 网络。通常意味着使用以下之一:

  • 公共云集群中的云提供者网络插件(例如,默认在 EKS 中使用的 Amazon VPC CNI 插件)

  • 可以使用 BGP 与本地企业网络集成的网络插件(例如,Kube-router、Calico CNI 插件)。

除了底层网络支持的连接性之外,客户端还需要一种方法来找出 pod IP 地址。这可以通过 DNS、客户端的显式配置或其他第三方服务发现机制来完成。

从安全角度来看,客户端直接到 pod 的连接非常简单:它们在整个过程中都保留着原始客户端源 IP 地址,这意味着可以轻松使用网络策略来限制特定 IP 地址的客户端访问或特定 IP 地址范围的访问。

请注意,在任何 pod IP 地址在集群边界之外可路由的集群中,确保遵循网络策略的最佳实践变得更为重要。如果没有网络策略,本应仅接收东西向连接的 pod 可能会被从集群外访问,而无需配置相应的可外部访问的 Kubernetes 服务类型或 Kubernetes Ingress。

理解 Kubernetes 服务

Kubernetes 服务通过 NodePort 或 LoadBalancer 类型的服务或通过显式配置服务的外部 IP,为从集群外部访问 pod 提供了一种便捷的机制。默认情况下,Kubernetes 服务由 kube-proxy 实现。kube-proxy 在集群中的每个节点上运行,并负责拦截到 Kubernetes 服务的连接,并跨支持相应服务的 pod 进行负载平衡。这种连接处理具有对源 IP 地址保留和不保留的明确定义行为,我们将在每种服务类型中进行详细讨论。

Cluster IP 服务

在我们探讨如何使用 Kubernetes 服务将 pod 暴露给外部客户端之前,值得了解的是,为集群内部发起的连接(即 pod 到 pod 的连接)提供服务发现和负载平衡的主要机制使用的是 Cluster IP 类型的 Kubernetes 服务。对于 Cluster IP 服务,kube-proxy 能够使用目标网络地址转换(DNAT)将连接映射到服务的 Cluster IP 到支持相应服务的 pod。连接返回数据包时会反向映射此映射。此映射不会更改源 IP 地址,如图 9-1 中所示。

图 9-1. Kubernetes 服务广告集群 IP 的网络路径

重要的是,目标 Pod 看到连接是从客户端 Pod 的 IP 地址发起的。这意味着适用于目标 Pod 的任何网络策略都按预期行为,不受连接通过服务的集群 IP 进行负载平衡的影响。此外,适用于客户端 Pod 的任何网络策略出口规则在从集群 IP 映射到目标 Pod 后进行评估。这意味着适用于客户端 Pod 的网络策略也按预期行为,与连接通过服务的集群 IP 进行负载平衡无关。(作为提醒,网络策略规则匹配 Pod 标签,而不是服务标签。)

节点端口服务

从集群外部访问服务的最基本方法是使用类型为 NodePort 的 Kubernetes 服务。节点端口是集群中每个节点上保留的端口,通过它可以访问每个服务支持的 Pod。在典型的 Kubernetes 部署中,kube-proxy 负责拦截对节点端口的连接,并在支持每个服务的 Pod 之间进行负载平衡。

在此过程的一部分中,NAT 用于将节点 IP 和节点端口的目标 IP 地址和端口映射到所选的后备 Pod 和服务端口。然而,与连接到集群 IP 不同,其中 NAT 仅映射目标 IP 地址,对于节点端口而言,源 IP 地址也从客户端 IP 映射到节点 IP。

如果源 IP 地址没有以这种方式映射,那么连接上的任何响应数据包都将直接流回到外部客户端,绕过原始入口节点上 kube-proxy 反向映射目标 IP 地址的能力。(执行 NAT 的节点具有反向 NAT 所需的连接跟踪状态。)结果,外部客户端将丢弃数据包,因为它无法识别这些数据包是否属于其与原始入口节点上节点端口建立的连接。

这个过程在图 9-2 中有所说明。

ksao_0902.png

图 9-2. 使用节点端口的 Kubernetes 服务的网络路径

由于 NAT 更改了源 IP 地址,适用于目标 Pod 的任何网络策略都不能匹配原始客户端 IP 地址。通常这意味着任何此类策略仅限于限制目标协议和端口,并且无法基于外部客户端的 IP 地址进行限制。这反过来意味着在这种配置下,很难像最佳实践那样限制需要连接到微服务的特定客户端的访问,无法轻松地通过 Kubernetes 网络策略来实现。

幸运的是,有多种解决方案可用于规避节点端口默认行为的限制:

  • 配置服务使用 externalTrafficPolicy:local

  • 使用支持节点端口感知网络策略扩展的网络插件

  • 使用替代实现的服务负载均衡,取代保留客户端源 IP 地址的 kube-proxy

我们将在本章后面详细介绍这些内容。但在此之前,为了完整地了解主流 Kubernetes 服务默认行为的情况,让我们看看类型为 LoadBalancer 的服务。

负载均衡器服务

类型为 LoadBalancer 的服务基于节点端口的行为,与外部网络负载均衡器集成。具体的网络负载均衡器类型取决于与您的集群集成的公共云提供商,或者在本地情况下,取决于集成的特定硬件负载均衡器。

默认情况下,可以通过网络负载均衡器上的特定 IP 地址从集群外部访问服务,该负载均衡器将默认均衡负载到服务的节点端口上的所有节点。

大多数网络负载均衡器位于网络中的某个点,连接的返回流量始终通过网络负载均衡器路由,因此它们可以仅使用 DNAT 实施负载均衡,网络负载均衡器不会改变客户端源 IP 地址,如 图 9-3 所示。

图 9-3. 类型为 LoadBalancer 的 Kubernetes 服务的网络路径

然而,由于网络负载均衡器正在负载均衡到服务的节点端口,并且 kube-proxy 的默认节点端口行为在其负载均衡实现中更改了源 IP 地址,目标 Pod 仍然无法匹配原始客户端源 IP 地址。就像使用原始节点端口服务一样,这反过来意味着在这种配置中很难通过 Kubernetes 网络策略实施限制连接到微服务的特定客户端的最佳做法。

幸运的是,可以与 NodePort 类型服务的默认行为限制相同的解决方案一起使用,与 LoadBalancer 类型服务一起使用:

  • 配置服务为 externalTrafficPolicy:local

  • 使用支持节点端口感知网络策略扩展的网络插件

  • 使用替代实现的服务负载均衡,取代保留客户端源 IP 地址的 kube-proxy

现在让我们分别看看这些。

externalTrafficPolicy:local

默认情况下,与服务关联的节点端口在集群中的每个节点上都可用,并且类型为 LoadBalancer 的服务将负载均衡到服务的节点端口,独立于实际托管服务的节点。可以通过配置服务为 externalTrafficPolicy:local 来更改此行为,该配置指定连接应仅负载均衡到本地节点上支持服务的 Pod。

当与负载均衡器类型的服务结合使用时,连接将仅定向到至少托管一个支持该服务的 pod 的节点。这样可以减少与 kube-proxy 正常节点端口处理相关的节点之间的潜在额外网络跳数。更重要的是,由于每个节点的 kube-proxy 仅对同一节点上的 pod 进行负载均衡,所以 kube-proxy 在负载均衡时无需执行源网络地址转换,这意味着客户端源 IP 地址可以一直保留到 pod。

网络流程示例见 图 9-4。

图 9-4. 利用优化路由到支持 pod 的节点的 Kubernetes 服务的网络路径

由于原始客户端源 IP 地址一直保留到支持的 pod,因此现在可以将应用于支持的 pod 的网络策略限制为仅允许访问服务的特定客户端 IP 地址或地址范围。

注意,并非所有负载均衡器都支持此操作模式。因此,重要的是检查特定公共云提供商是否支持此功能,或者如果在本地部署,检查与集群集成的特定硬件负载均衡器是否支持此功能。好消息是,大多数大型公共提供商都支持此模式。一些负载均衡器甚至可以更进一步,跳过 kube-proxy,直接向支持的 pod 进行负载均衡,而不使用节点端口。

网络策略扩展

一些 Kubernetes 网络插件提供了标准 Kubernetes 网络策略能力的扩展,可用于帮助安全地访问集群外的服务。

存在许多解决方案提供网络策略扩展(例如 Weave Net、Kuberouter、Calico)。让我们再次关注 Calico,因为这是我们的专业领域。Calico 支持主机端点,允许将网络策略应用于集群内的节点,而不仅仅是集群内的 pod。标准 Kubernetes 网络策略可以被认为是在每个 pod 网络前面提供虚拟防火墙,而 Calico 的主机端点扩展可以被认为是在每个节点/主机前面提供虚拟防火墙,如 图 9-5 所示。

此外,Calico 的网络策略扩展支持指定主机端点应用的策略规则是在 kube-proxy 负载均衡相关的 NAT 之前还是之后。这意味着它们可以用于限制哪些客户端能够连接到特定的节点端口,不受 kube-proxy 可能即将做出的负载均衡决策的影响。

图 9-5. 使用主机端点保护的虚拟防火墙

替代 kube-proxy

Kube-proxy 提供了 Kubernetes 服务的默认实现,并且在大多数集群中作为标准包含。然而,一些网络插件提供了替代的 Kubernetes 服务实现来取代 kube-proxy。

对于某些网络插件,由于插件实现的 pod 网络方式与 kube-proxy 的数据平面不兼容(kube-proxy 使用由 iptables 和/或 IPVS 控制的标准 Linux 网络管道),因此需要进行这种替代实现。对于其他网络插件,替代实现是可选的。例如,实现了 Linux eBPF 数据平面的 CNI 将选择替换 kube-proxy,采用其本地服务实现。

这些替代实现中的一些提供了超出 kube-proxy 行为的额外功能。从安全角度来看,其中一个相关的附加功能是在从外部客户端进行负载均衡时保留客户端源 IP 地址直到后端 pod。

例如,图 9-6 展示了基于 eBPF 的数据平面如何实现这种行为。

图 9-6. 基于 eBPF 实现的 Kubernetes 服务的网络路径

这允许原始客户端源 IP 地址一直保留到服务的打包 pod,适用于类型为 NodePort 或 LoadBalancer 的服务,无需网络负载均衡器支持 externalTrafficPolicy:local 或节点端口的节点选择。这反过来意味着应用于支持 pod 的网络策略能够限制只有特定客户端、IP 地址或地址范围能够访问服务。

除了安全考虑,这些替代的 Kubernetes 服务实现(如基于 eBPF 的数据平面)相比于 kube-proxy 的实现还提供其他优势,例如:

  • 在运行非常多服务时提升了性能,包括减少了首个数据包的延迟和减少了控制平面的 CPU 使用率

  • 直接服务器返回(DSR),用于减少返回流量的网络跳数

我们将更详细地查看 DSR,因为它确实具有一些安全方面的影响。

直接服务器返回

DSR 允许目标 Pod 的返回流量直接流回客户端,而不必经过原始入口节点。有几种网络插件可以用它们自己的实现替换 kube-proxy 的服务处理,支持 DSR。例如,包括本地服务处理的 eBPF 数据平面(可选地)可以使用 DSR 处理返回流量,如图 9-7 所示。

图 9-7. Kubernetes 服务的网络路径,具有直接服务器返回

消除返回流量的一个网络跳跃会减少:

  • 服务的整体延迟(因为每个网络跳跃都会引入延迟)

  • 原始入口节点的 CPU 负载(因为它不再处理返回流量)

  • 集群内部的东西向网络流量

对于特别网络密集或延迟敏感的应用程序,这可能是一个巨大的优势。然而,DSR 也有安全影响。特别是,底层网络可能需要配置相对放宽的反向路径过滤(RPF)设置。

RPF 是一种网络路由机制,阻止来自特定源 IP 地址的任何流量,如果没有相应的路由可以通过同一链路发送到该 IP 地址,则会阻止该流量。也就是说,如果路由器没有一条路由表明可以通过网络链路发送到特定的 IP 地址,则它不会允许来自该 IP 地址的流量通过网络链路。RPF 使攻击者更难“欺骗”IP 地址,即假装成不同于设备分配的 IP 地址。

在 DSR 和 Kubernetes 服务的背景下,图 9-7 展示了几个关键点:

  • 如果通过节点 1 上的节点端口访问服务,则来自节点 2 的返回流量将具有节点 1 的源 IP 地址。因此,底层网络必须配置放宽的 RPF 设置,否则网络将过滤返回流量,因为网络通常不会通过节点 2 的网络链路路由到节点 1。

  • 如果通过服务 IP 广告(例如,直接发送流量到服务的集群 IP、外部 IP 或负载均衡器 IP)访问服务,则来自节点 2 的返回流量将具有服务 IP 的源 IP 地址。在这种情况下,不需要放宽反向路径过滤(RPF),因为服务 IP 应该从集群中的所有节点广告,意味着网络将通过所有节点路由到服务 IP。我们将在本章后面更详细地讨论服务 IP 广告。

正如前面所解释的,DSR 是一种优秀的优化技术,您可以使用它,但需要审查您的用例,并确保您能够禁用 RPF 检查而感到舒适。

限制服务的外部 IP

到目前为止,在本章中,我们专注于服务类型和实现方式如何影响网络策略的使用,以限制只有特定的客户端 IP 地址或地址范围可以访问每个服务。

另一个重要的安全考虑是具有权限创建或配置 Kubernetes 服务的用户所拥有的权力。特别是,任何具有 RBAC 权限修改 Kubernetes 服务的用户实际上控制着该服务负载平衡到哪些 Pod。如果恶意使用,这可能意味着用户能够将本应发送到特定微服务的流量重定向到他们自己的恶意 Pod。

由于 Kubernetes 服务是命名空间资源,这很少会成为主流服务功能的真正安全问题。例如,已被授予在特定命名空间中定义服务权限的用户通常也有权限修改该命名空间中的 Pod。因此,对于标准服务功能(如处理集群 IP、节点端口或负载均衡器),在命名空间中定义和修改服务的权限实际上并不表示比在命名空间中定义或修改 Pod 的更高信任级别。

然而,还有一个显著的例外,即能够为服务指定外部 IP 的能力。服务定义中的 externalIP 字段允许用户将任意 IP 地址与服务关联起来。在集群中的任何节点上接收到对此 IP 地址的连接将被负载平衡到支持该服务的 Pod。

正常使用情况是提供一个面向 IP 的替代方案,用于外部客户端连接到服务。此用例通常需要在底层网络中进行特殊处理,以便将连接路由到集群中的节点上。这可以通过在底层网络中编程静态路由来实现,或者在支持 BGP 的网络中,使用 BGP 动态广播外部 IP。 (有关广告服务 IP 地址的更多详细信息,请参阅下一节。)

与主流服务功能类似,此用例在用户信任级别上相对温和。它允许用户为管理权限的命名空间中的 Pod 提供一种额外的访问方式,但不会干扰流向其他命名空间中的 Pod 的流量。

然而,与节点端口一样,从 Pod 到外部 IP 的连接也会被拦截并负载平衡到支持该服务的 Pod。由于 Kubernetes 不会对外部 IP 地址进行监控或尝试提供任何级别的验证,这意味着恶意用户实际上可以截取到任何 IP 地址的流量,而无需任何命名空间或其他范围限制。这对于恶意用户来说是一个极为强大的工具,同时也代表着相应的巨大安全风险。

如果您遵循最佳实践,即在整个集群中为入站和出站流量都采用默认拒绝的策略,那么这将极大地阻碍恶意用户试图访问应该在两个其他 Pod 之间的流量。然而,虽然网络策略将阻止它们访问流量,但服务负载平衡不会阻止将流量从其预期的目的地转移,这意味着恶意用户可以有效地阻止任何两个 Pod 之间的流量,即使他们自己无法接收到流量。

因此,除了遵循网络策略的最佳实践外,建议使用准入控制器来限制可以指定或修改外部 IP 字段的用户。对于允许指定外部 IP 地址的用户,还可能希望将 IP 地址值限制在被认为安全的特定 IP 地址范围内(即未用于任何其他目的的范围)。有关准入控制器的更多讨论,请参见第八章。

广告服务 IP

使用节点端口或网络负载均衡器的一种替代方法是通过 BGP 广告服务 IP 地址。这需要集群在支持 BGP 的底层网络上运行,通常意味着使用标准顶部机架路由器的本地部署。

例如,Calico 支持广告服务集群 IP、负载均衡器 IP 或配置了一个外部 IP 的服务。如果您不使用 Calico 作为您的网络插件,那么 MetalLB 提供了类似的功能,可以与各种不同的网络插件一起使用。

广告服务 IP 有效地允许底层网络路由器充当负载均衡器,而无需实际的网络负载均衡器。

广告服务 IP 的安全考虑与本章前面讨论的基于节点端口或负载均衡器的服务相当。在使用 kube-proxy 时,默认情况下会通过模糊示原始客户端 IP 地址,如图 9-8 所示。

图 9-8. 通过 BGP 广告集群 IP 的 Kubernetes 服务的网络路径

这种行为可以通过使用externalTrafficPolicy:local来更改,该选项(截至撰写本文时)由 kube-proxy 支持,适用于负载均衡器 IP 和外部 IP 地址,但不适用于集群 IP 地址。然而,应注意,当使用externalTrafficPolicy:local时,负载均衡的均匀性变为依赖于拓扑结构。为了规避这一问题,可以使用 Pod 反亲和性规则,以确保在拓扑结构中均匀分布支持 Pod,但这确实增加了部署服务的复杂性。

或者,可以使用具有本地服务处理的网络插件(替换 kube-proxy),支持源 IP 地址保留。由于其操作简单性和无需在网络拓扑中构建网络负载均衡器设备,这种组合对于本地部署非常有吸引力。

理解 Kubernetes Ingress

Kubernetes Ingress 构建在 Kubernetes 服务之上,提供应用层负载均衡,将特定域名或 URL 的 HTTP 和 HTTPS 请求映射到 Kubernetes 服务。如果多个微服务组成单个 Web 应用程序,Kubernetes Ingress 可以是一种方便的方式来通过单个外部接入点公开多个微服务。此外,它们可以用于在将流量负载均衡到后端微服务之前终止 SSL/TLS(用于接收来自外部客户端的 HTTPS 加密连接)。

Kubernetes Ingress 的实现细节取决于您使用的入口控制器。入口控制器负责监视 Kubernetes Ingress 资源,并配置一个或多个入口负载均衡器以实现所需的负载均衡行为。

与 Kubernetes 服务不同,后者在网络层(L3-L4)处理,入口负载均衡器在应用层(L5-L7)操作。入站连接在负载均衡器处终止,以便检查单独的 HTTP/HTTPS 请求。然后,请求通过从负载均衡器到选择的服务的分开连接进行转发。因此,应用于支持服务的 Pod 的网络策略将入口负载均衡器视为客户端源 IP 地址,而不是原始外部客户端 IP 地址。这意味着它们可以限制仅允许来自负载均衡器的连接,但不能限制对特定外部客户端的访问。

为了限制对特定外部客户端的访问,需要在应用负载均衡器内或应用负载均衡器前强制执行访问控制。如果选择基于 IP 的访问控制,则需要在流量转发到后端服务之前执行。如何执行这一点取决于您使用的具体入口控制器。

广义上说,有两种类型的入口解决方案:

In-cluster ingress

入口负载均衡由集群内的 Pod 执行。

外部入口

入口负载均衡由设备或云提供商的功能在集群外实现。

现在我们已经介绍了 Kubernetes Ingress,让我们来回顾一下入口解决方案。

集群内入口解决方案

集群内入口解决方案使用运行在集群内部 Pod 中的软件应用负载均衡器。有许多不同的入口控制器遵循此模式。例如,NGINX 入口控制器实例化和配置 NGINX Pod 以充当应用程序负载均衡器。

集群内 Ingress 解决方案的优势在于:

  • 您可以水平扩展您的 Ingress 解决方案,直至达到 Kubernetes 的极限。

  • 有许多不同的集群内 Ingress 控制器解决方案,因此您可以选择最适合您特定需求的 Ingress 控制器 — 例如,具有特定负载均衡算法、安全选项或可观测性功能。

要将您的 Ingress 流量发送到集群内的 Ingress Pod,通常将 Ingress Pod 作为 Kubernetes 服务外部公开,如 图 9-9 所示。

图 9-9. Kubernetes 集群中内部 Ingress 实现的示例

这意味着您可以使用任何标准的方式从集群外部访问服务。一个常见的方法是使用外部网络负载均衡器或服务 IP 广告,以及以下方法之一:

  • 一个具有本地 Kubernetes 服务处理的网络插件,始终保留原始客户端源 IP

  • externalTrafficPolicy:local(和 Pod 反亲和规则以确保在 Ingress Pod 之间均衡负载)以保留原始客户端源 IP

应用于入口 Pod 的网络策略可以像本章前面描述的那样限制对特定外部客户端的访问,并且支持通过 Ingress 暴露的任何微服务的 Pod 可以限制连接仅限于来自入口 Pod 的连接。

外部 Ingress 解决方案

外部 Ingress 解决方案使用集群外的应用程序负载均衡器,如 图 9-10 所示。

图 9-10. Kubernetes 集群中外部 Ingress 的示例

具体的细节和功能取决于您使用的 Ingress 控制器。大多数公共云提供商都有自己的 Ingress 控制器,可以自动化云提供商的应用程序负载均衡器的配置和管理,以提供 Ingress。

大多数应用程序负载均衡器支持将流量转发到所选服务后端 Pod 的基本操作模式,通过相应服务的节点端口。除了负载均衡到服务节点端口的基本方法外,一些云提供商支持应用程序负载均衡的第二模式,直接负载均衡到每个服务后端的 Pod,而不经过节点端口或其他 kube-proxy 服务处理,这样做的优势是消除了与节点端口负载均衡到不同节点上的 Pod 相关的潜在第二个网络跳转。

外部 Ingress 解决方案的主要优势是云提供商为您处理 Ingress 的操作复杂性。潜在的缺点如下:

  • 可用的功能集通常比集群内 Ingress 解决方案的丰富范围更有限。例如,如果您需要特定的负载均衡算法、安全控制或可观察性功能,则云服务提供商的实现可能或可能不支持这些功能。

  • 支持的服务最大数量(以及可能支持的服务后端的 Pod 数量)受限于特定云服务提供商的限制。例如,如果您在非常高的规模下运行,每个服务后端支持数百个 Pod,您可能会超出应用层负载均衡器在此模式下能够负载均衡的最大 IP 数量限制。在这种情况下,切换到集群内的 Ingress 解决方案可能更适合您。

  • 由于应用负载均衡器不托管在 Kubernetes 集群内,如果您需要限制对特定外部客户端的访问,则无法使用 Kubernetes 网络策略,而必须使用云服务提供商的特定机制。仍然可以按照本章开头提出的最佳实践进行,但这将依赖于云服务提供商的具体能力和 API,可能会引入一些额外的运维复杂性。

在这一节中,我们讨论了 Kubernetes Ingress 的工作原理以及现有的解决方案。我们建议您仔细查看这些部分,并决定是否适合您使用集群内的 Ingress 解决方案,或者应该选择外部的 Ingress 解决方案。

结论

在本章中,我们讨论了将 Kubernetes 服务暴露到集群外部的主题。以下是涵盖的关键概念:

  • Kubernetes 服务的概念,如直接 Pod 连接、广告服务 IP 和节点端口,是您可以利用的技术,以将 Kubernetes 服务暴露到集群外部。

  • 我们建议使用基于 eBPF 的数据平面来优化 Ingress 路径,将流量路由到托管服务后端的 Pod。

  • 由于其能够保留源 IP 到 Pod 的能力,eBPF 数据平面是默认 Kubernetes 服务实现 kube-proxy 的一个优秀替代方案。

  • Kubernetes Ingress 实现的选择将取决于您的用例。我们建议您考虑集群内的 Ingress 解决方案,因为它更符合 Kubernetes 的本地化特性,比使用外部 Ingress 解决方案更具控制性。

我们希望您能根据自己的用例利用这些概念,实施 Kubernetes 服务。

第十章:数据在传输中的加密

随着将关键任务工作负载转移到生产环境,您很可能需要加密传输中的数据。这对于满足某些类型数据的合规要求以及良好的安全实践非常重要。

数据在传输中的加密是许多合规标准(如 HIPAA、GDPR 和 PCI)规定的要求。具体要求略有不同;例如,PCI DSS(支付卡行业数据安全标准)有关在传输过程中加密持卡人数据的规定。根据具体的合规标准,您可能需要确保在 Kubernetes 中托管的应用程序或微服务之间传输的数据使用认可的强加密算法进行加密。

根据应用程序或微服务的架构,可能并非所有通过网络发送的数据都被分类为敏感数据,因此从理论上讲,您可能只需对传输中的数据子集进行严格加密。然而,从操作简易性和合规审计的角度来看,通常对微服务之间的所有数据进行加密是合理的选择,而不是尝试选择性加密。

即使您没有外部合规标准强制的强制要求,加密数据在传输中仍然可以是一种非常好的做法。没有加密,拥有网络访问权限的恶意行为者可以看到敏感信息。您如何评估这种风险可能会因您使用的是公共云还是本地/私有云基础架构以及您作为组织的内部安全流程而异。在大多数情况下,如果您处理敏感数据,那么您真的应该加密传输中的数据。

如果您提供的服务是由公共互联网上的客户访问的,则 Kubernetes 适用 HTTPS 的标准做法。根据您的微服务架构,这些 HTTPS 连接可以在目标微服务上终止,也可以由 Kubernetes Ingress 解决方案终止,无论是作为集群内 Ingress pods(例如使用 NGINX Ingress Controller 时)还是作为集群外应用程序负载均衡器(例如使用 AWS 负载均衡器控制器时)。请注意,如果使用集群外应用程序负载均衡器,则仍然需要确保从负载均衡器到目标微服务的连接使用 HTTPS,以避免未加密的网络跳跃。

在集群内部,有三种广泛的方法来加密传输中的数据:

  • 在您的应用程序/微服务代码中构建加密功能。

  • 使用基于 sidecar 或服务网格的加密,在无需对您的应用程序/微服务进行代码更改的情况下,在应用程序层加密。

  • 使用网络级加密,无需对您的应用程序/微服务进行代码更改。

现在我们将探讨每种方法的利弊。

将加密集成到您的代码中

大多数编程语言都有用于加密网络连接的库,因此理论上您可以选择在构建微服务时将加密集成到其中。例如,您可以使用 HTTPS SSL/TLS 甚至 mTLS(双向 TLS)来验证连接两端的身份。

然而,这种方法有许多缺点:

  • 在许多组织中,不同的微服务使用不同的编程语言构建,每个微服务开发团队都使用最适合其特定微服务和团队专长的语言。例如,前端 Web UI 微服务可能使用 Node.js 编写,中间层微服务可能使用 Python 或 Golang 编写。由于每种编程语言都有其可用于加密的库集合,这意味着实施工作量增加,可能需要每个微服务团队为其微服务实施加密,而不是能够跨所有微服务共享单一实现。

  • 建立在不使用单一共享加密实现的理念基础上,微服务的配置也是如此,特别是微服务读取用于加密所需凭证的方式。

  • 除了开发和维护所有这些代码所需的工作量之外,您拥有的实现越多,某个实现中存在导致安全漏洞的错误可能性就越大。

  • 旧版加密库通常存在已知的漏洞,在新版本中得到修复。一旦发布新版本以解决任何新发现的漏洞,这些漏洞就会变为公开知识。这进一步增加了针对利用漏洞的攻击数量。为了减少风险,尽快更新使用该库的任何微服务至关重要。如果运行多个微服务,这可能代表显著的开发和测试工作量,因为每个微服务的代码都需要单独更新和测试。此外,如果您的 CI/CD 过程中缺乏大量自动化,那么更新每个微服务版本到生产集群中可能会增加操作上的头疼。

  • 许多微服务基于第三方开源代码(部分或整体)。这通常意味着您只能使用第三方代码支持的特定加密选项,并且在许多情况下,您还会依赖于第三方代码支持的特定配置机制。您还会依赖于第三方代码的上游维护者,以确保开源项目保持更新,并在发现漏洞时进行修复。

  • 最后,重要的是要注意,在配置不同实现及其各种配置范例时,通常存在操作开销,特别是在提供加密设置和凭据时。

因此,总的来说,虽然可以在每个微服务中构建加密功能,但涉及的工作量以及由于代码或设计问题或过时的加密库而不知不觉地引入安全漏洞的风险,可能会使这种方法显得相当令人望而却步。

边车或服务网格加密

在应用层面对微服务之间的流量进行加密的另一种架构方法是使用边车设计模式。边车是一个容器,可以与实现微服务的主要容器一起包含在每个 Kubernetes Pod 中。边车拦截与微服务进行的连接,并代表微服务执行加密,而无需对微服务本身进行任何代码更改。边车可以显式地包含在 Pod 规范中,也可以在创建时通过准入控制器注入到 Pod 规范中。

相较于在每个微服务中构建加密功能,边车(sidecar)方法的优势在于可以在所有微服务中使用单一的加密实现,独立于微服务可能使用的编程语言。这意味着只需维护一个实现,就可以轻松更新漏洞修复或安全改进,从而在所有微服务中进行推广。

理论上,您可以开发自己的边车。但除非您有某些特定需求,通常最好使用已经可用的众多免费开源实现之一,这些实现经过了大量的安全审查和现场强化。

一个广受欢迎的例子是由 Lyft 团队最初开发的 Envoy 代理,通常用于使用 mTLS(双向 TLS)加密微服务流量。双向 TLS 意味着源和目标微服务在建立连接时都提供凭据,因此每个微服务都可以确保与预期的其他微服务进行通信。Envoy 拥有丰富的配置模型,但本身不提供控制或管理平面,因此您需要编写自己的自动化流程来配置 Envoy 以按照您希望的方式工作。

与其自己编写此自动化,另一种方法是使用遵循旁路模式的许多服务网格解决方案之一。例如,Istio 服务网格提供了一个打包解决方案,使用 Envoy 作为集成到 Istio 控制和管理平面的旁路。服务网格除了加密外,还提供许多功能,包括服务路由和可见性。虽然服务网格越来越受欢迎,但其更丰富的功能集被普遍认为可能引入操作复杂性,或者使得服务网格在螺丝和螺母层面更难理解,因为涉及更多运行部件。另一个缺点是与旁路设计模式相关的安全风险,其中旁路是每个应用程序 Pod 的一部分,管理旁路的复杂性增加(例如,一个 CVE 可能需要您更新旁路,这并非简单更新,因为它会影响所有应用程序)。

网络层加密

实现微服务中的加密或使用旁路模式通常被称为应用层加密。本质上,应用程序(微服务或旁路)处理所有加密,而网络只负责发送和接收数据包,完全不知道加密正在进行。

实施加密的另一种选择是在网络层内部实现加密。从应用程序的角度来看,它发送的是未加密数据,而网络层负责在数据传输到网络之前对数据包进行加密。

多年来在行业中广泛使用的网络层加密的主要标准之一是 IPsec。大多数 IPsec 实现支持广泛的加密算法,如具有不同密钥长度的 AES 加密。IPsec 通常与 IKE(Internet Key Exchange)配对,作为管理和传输 IPsec 所需的主机凭证(证书和密钥)的机制。有许多开源项目,如流行的强 Swan 解决方案,提供 IKE 实现,简化创建和管理 IPsec 网络。

一些企业选择使用强 Swan 等解决方案作为其首选的 IPsec 管理解决方案,然后在其上运行 Kubernetes。在这种情况下,Kubernetes 并不真正了解 IPsec。即使像强 Swan 这样的项目帮助简化和管理 IPsec 设置,许多人仍认为 IPsec 在整体运营角度上相当笨重且难以管理。

IPsec 的一个替代方案是 WireGuard。WireGuard 是一种较新的加密实现,旨在极其简单且快速,采用最先进的密码学。在架构上,它更为简单,初步测试表明,在各种情况下,它确实优于 IPsec。但需要注意的是,WireGuard 和 IPsec 都在继续开发中,尤其是随着加密算法的进展,它们的比较性能可能会发生变化。

对于大多数组织而言,与其自行设置和管理 IPsec 或 WireGuard,更为操作简单的方法是使用内置支持加密的 Kubernetes 网络插件。有各种 Kubernetes 网络插件支持不同类型的加密,具有不同的性能特征。

如果您运行网络密集型工作负载,则考虑加密的性能成本至关重要。无论您是在应用层还是在网络层进行加密,这个成本都会存在,但加密技术的选择对性能可能会产生显著影响。例如,图 10-1 显示了四种流行的 Kubernetes 网络插件的独立基准结果(这是在撰写本文时最新的基准测试结果,发布于 2020 年)。

使用支持加密的 Kubernetes 网络插件,通常在操作上要简单得多,动态部署的组件也更少,比采用服务网格要简单得多,并且比将加密集成到应用程序/微服务代码中所需的工作量也少得多。如果您采用服务网格的主要动机是通过加密来确保安全性,那么使用支持网络层加密的 Kubernetes 网络插件以及 Kubernetes 网络策略可能更容易管理和维护。请注意,我们在第五章中还涵盖了服务网格的其他方面,如可观察性。

图 10-1. Kubernetes 中加密的基准结果

结论

在本章中,我们介绍了实现数据传输加密的各种选项,以及在 Kubernetes 集群中实现加密的各种方法。我们希望这能帮助您选择最适合您用例的选项。以下是一些需要记住的事项:

  • 随着您将关键工作负载转移到生产环境,对于某些类型的数据,您需要实现数据传输加密。即使合规要求不要求对所有数据进行加密,我们也建议实现数据传输加密。

  • 我们介绍了实施加密的常见方法:应用层加密、基于 sidecar 的服务网格加密以及网络层加密。

  • 基于操作简易性和更好的性能,我们推荐网络层加密。

第十一章:威胁防御与入侵检测

在本章中,我们将探讨如何为您的 Kubernetes 集群实施威胁防御。我们已经在前几章中涵盖了 Kubernetes 部署的各个阶段(构建、部署、运行时)。本章重点讨论威胁防御,即运行时阶段的安全性。我们将涵盖以下概念,帮助您理解 Kubernetes 集群中的威胁防御以及其重要性。

  • Kubernetes 集群的威胁防御,包括为什么需要它以及它与传统安全的不同之处。

  • Kubernetes 的入侵检测

  • 高级威胁防御技术。

让我们详细探讨每一个阶段。我们从威胁防御及其重要性开始。

Kubernetes 的威胁防御(攻击阶段)

要理解威胁防御,一个很好的起点是审查网络安全杀伤链,它将攻击分解为几个阶段。然后用这些阶段来制定防御策略。网络安全杀伤链包括以下阶段:

侦察

对手探测目标并收集信息。

武器化

对手创建攻击方法,可以是新的漏洞,现有漏洞的变体或不安全配置的简单利用。

交付

对手创建一种方法,将漏洞或利用交付给目标或可用于攻击目标的位置。

利用

对手实施触发攻击的方法。

安装

对手安装恶意软件,通常包括创建与恶意软件通信的后门软件。

命令与控制

对手建立与恶意软件的通信渠道以控制软件。

目标动作

对手实现攻击的预期结果(例如窃取数据,加密数据等)。

几个组织已经适应了这一框架,以包含实际攻击和更多用例。Microsoft 在其博客文章 “在 Kubernetes 上使用更新的威胁矩阵保护容器化环境” 中描述了对 Kubernetes 的适应。让我们审视适用于 Kubernetes 的杀伤链阶段(威胁矩阵):

初始访问

对手利用您的 Kubernetes 部署中的各种公开接口(例如 Kubeflow),通过窃取的凭据、被入侵的镜像或其他应用程序漏洞。

执行

对手在您的集群中执行恶意命令或软件。这可以通过多种方式实现;一些已知的技术包括在集群中创建新容器,在任何 Pod 中作为 sidecar 运行额外容器,以及利用已知的应用程序漏洞执行恶意命令。

持久性

对手将努力将恶意软件持久化在 Kubernetes 集群中,以便以后访问。通常通过在主机上创建可写存储路径、利用定期运行恶意软件的 Kubernetes 定时作业(也称为 cron 作业),或在某些情况下,破坏 Kubernetes 准入控制器,以便篡改 API 服务器的请求来发动攻击。在此阶段,对手还将尝试建立与其控制服务器的后门通信渠道,以便控制恶意软件。这被称为命令与控制服务器(C&C 服务器)。

特权升级

对手通过利用集群中具有特权访问权限的资源来获得特权访问。例如,他们可能通过利用特权容器中的漏洞来运行恶意软件,从而获得特权访问。

防御逃避

对手通过清除日志或删除事件等技术来保持攻击不被检测,以便监测系统不会检测到恶意活动的存在。另一种使用的技术是利用一个由许多 Pod 支持的 Kubernetes 部署中仅一个 Pod 中的漏洞,并利用它进一步发动攻击。

凭据访问

对手致力于获取集群中的凭据(Kubernetes secrets)。如果您正在使用托管服务,云提供商将提供一个访问云资源的令牌,并且此令牌对某些特权的 Pod 和服务账号可访问。对手将利用冒充或特权升级来获取凭据访问权限,然后利用云资源访问进一步发动攻击。

探测

对手将尝试对集群网络进行侦察,以了解集群中正在运行的内容。这可以通过在 Linux 系统上使用像Nmap这样的网络映射工具,或者访问 Kubernetes 仪表板来实现。此阶段是攻击的重要前期阶段,对手可以在集群中移动以寻找他们所需的内容。

横向移动

当攻击达到这个阶段时,攻击已经相当先进,对手已经建立了存在;他们现在将利用安装的恶意软件访问网络中的其他 Pod 和资源。一些常见的技术包括欺骗其他 Pod 的 IP 地址或域名,并冒充其他 Pod 以绕过集群内部的分段规则。攻击者还会寻找集群内运行的其他应用程序,因为他们现在可以访问这些应用程序。在这个阶段,恶意软件正在与命令与控制服务器通信,以获取进一步攻击的指令。在这个阶段,攻击者依靠过载 DNS 或 HTTP 等众所周知的协议来发送命令与控制请求作为这些协议的一部分,这使得他们可以绕过基于周界的安全控制,因为流量看起来像是正常的 DNS 或 HTTP 请求。

影响

这是攻击的最终阶段,通常的结果是窃取敏感数据。这是通过一种称为数据外泄、勒索软件数据加密,甚至使用资源进行加密货币挖掘的技术来实现的。

威胁防御包括一组技术,帮助您防御这些阶段并使您能够抵御攻击。考虑到对手可能使用的所有这些阶段和技术可能会让人感到不知所措。我们要提到的是,虽然对手需要在大多数(如果不是全部)阶段中取得成功才能执行成功的攻击,但您只需要在任何一个阶段阻止它们就可以挫败攻击。所以胜算在您这边。了解这些阶段及其在 Kubernetes 中的应用是构建有效防御机制的第一步。对手始终在创新,因此您应关注所有阶段,并使用与每个阶段相关的工具和技术,以使自己有最大成功挫败攻击的机会。

在第二章中,我们讨论了基础设施安全,向您展示了如何为运行工作负载创建安全的基础设施。在第三章中,我们介绍了您可以使用的最佳实践和技术,以安全地部署工作负载。第四章涵盖了您可以应用于工作负载以保护工作负载运行环境的安全策略,而第六章涵盖了如何应用网络策略来实施工作负载的网络访问控制。我们建议您在这里描述的杀伤链阶段的背景下回顾这些章节。您会发现,这些技术在处理初始访问、特权提升、凭证访问、持久性和执行阶段时非常有效。

现在我们将描述您可以使用的工具和技术来保护杀伤链的其他阶段。需要注意的是 Kubernetes 是一个分布式系统,其集群网络对其操作至关重要;因此,保护网络是一种非常有效的技术。例如,如果对手无法利用集群网络进行发现、命令与控制、横向移动或数据外泄,那么成功的特权升级或应用程序漏洞的利用将变得无效。仅仅基于 IP 地址/端口的网络分割是不够的,因为对手会找到方法继续攻击,即使像网络分割这样的技术在保护您的集群时。例如,您需要允许 HTTP 流量到达您的服务和支持该服务的 Pod,以便攻击可以作为触发特权升级的 HTTP 标头的一部分。

现在我们已经介绍了威胁防御的概念,让我们来探讨入侵检测。

入侵检测

在本节中,我们将介绍入侵检测及其在 Kubernetes 集群中的应用。为了理解这一点,我们将审查入侵的各种方法及入侵检测系统的角色。

入侵检测系统

入侵检测系统(IDS)顾名思义是一个监控网络活动、检测异常模式并报告可疑行为的系统。这些系统还监控违反现有控制措施(如网络策略、主机强化)并报告这些违规行为。IDS 的响应操作包括以下内容:

警报

生成警报并发送到 SIEM 进行进一步分析和处理。

入侵预防

系统通过利用现有的控制措施(例如网络策略、Kubernetes Pod 安全策略、主机强化策略)并重定向攻击至专门用于分析此类攻击的金丝雀资源来采取行动以防止入侵。当 IDS 能够预防入侵时,它被称为入侵预防系统(IPS)。

一个良好的入侵检测系统应能通过跟踪系统的行为来关联一组相关的异常。我们建议您了解用户和实体行为分析(UEBA)及其在安全领域的应用。Microsoft Azure UEBA 是您审阅的一个优秀资源。请注意,对于 Kubernetes,实体行为分析是适用的。本书不涵盖其实施细节,但 UEBA 有助于减少警报数量并生成高保真度警报。请注意,警报数目的增加未必是好事;它们会使下游系统(例如 SIEM)对警报免疫。稍后我们将讨论如何利用机器学习系统生成高保真度警报。

现在我们将审查入侵检测方法及其在 Kubernetes 集群中的应用。

IP 地址和域名威胁信息源

如网络安全杀链中所述,对手通常会使用恶意软件联系一个由他们控制的服务器。这些服务器用于远程控制恶意软件,获取有关系统的信息,下载更多软件并进一步进行攻击。全球的安全研究团队审查攻击并通过 IP 地址/域名识别已知的 C&C 服务器。这些信息作为威胁信息源和妥协指标的一部分定期更新。有几个众所周知的威胁信息源,既有开源的也有商业的。STIX 是描述威胁情报的众所周知的标准,而TAXII 是提供情报的标准。您可以找到几个解析 STIX 和 TAXII 信息源并提供情报的开源引擎(例如 AlienVault)。Feodo trackerSnort 是提供 IP 地址阻塞列表的开源信息源的例子。

有时对手会使用 VPN(虚拟专用网络),这些是运行在物理网络上的覆盖网络,用于隐藏用户的位置。Tor 是另一个众所周知的用于此目的的覆盖网络。与用于 C&C 服务器的威胁信息源类似,已知的 VPN(https://oreil.ly/RGtxT)和 Tor 网络中的 IP(https://oreil.ly/VHGkZ)也有相关信息源。

现在我们将介绍如何在您的 Kubernetes 集群中使用这些信息源来实现 IDS/IPS。图 11-1 展示了在您的 Kubernetes 集群中应用可疑 IP 地址和域名的示例实现。

图 11-1. 使用威胁信息源实施 IDS/IPS

图 11-1 展示了如何在您的集群中实现对威胁信息源的支持,并描述了实现 IDS/IPS 能力的高级工作流程。该图展示了以下组件作为您 Kubernetes 集群的一部分。

威胁信息源控制器

此组件负责从配置的源(通常是一个 URL)检索威胁信息源。这可以通过多种方式实现。在本讨论中,我们假设这是一个观察配置资源的 pod(稍后的示例),并访问指定的 URL,并将威胁信息源数据存储在 Kubernetes 数据存储中以供其他组件使用。以下是威胁信息源控制器的配置示例:

apiVersion: projectcalico.org/v3
kind: GlobalThreatFeed
metadata:
  name: sample-global-threat-feed
spec:
  content: IPSet
  pull:
    http:
      url: https://an.example.threat.feed/blacklist
  globalNetworkSet:
    labels:
      security-action: block

在这个示例中,威胁信息源控制器被配置为从指定的 URL 拉取威胁信息,并将 IP 地址列表存储为名为 globalnetworkset 的自定义 Kubernetes 资源。这将由网络策略实施使用,以便基于此资源执行策略。以下是可以使用 globalnetworkset 资源定义的策略示例:

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: default.blockthreats
spec:
  tier: default
  selector: all()
  types:
  - Ingress
  ingress:
  - action: Deny
    source:
      selector: security-action == 'block'

网络策略引擎

这是在您的集群中实施网络策略的组件,用于定义阻止与威胁情报中 IP 地址之间的流量的策略。请注意,IP 地址列表可能包含大量 IP 地址,并且可能定期更改。因此,您选择的网络策略实施应基于此要求进行扩展。作为提示,我们建议您选择一个引擎,支持基于 iptables 数据平面的 ipsets 扩展,其中 IP 地址集的匹配被优化。如果您使用基于 eBPF 的数据平面,请确保实现支持 eBPF 映射,以实现与 ipsets 功能等效的功能。

日志处理引擎

该组件负责报告来自您的集群的流日志,其中包含与威胁情报中任何 IP 地址匹配的 IP 地址,并生成警报。请注意,由于流数据量巨大,这可能是一个资源密集型操作。解决此问题的一种方法是,当数据平面检测到匹配时,使网络策略引擎向流日志添加一个注释,以命名包含流日志中 IP 地址的情报。在线执行此操作非常高效,而不是在收集数据后进行匹配。

现在我们了解了 IDS 的各个组件,让我们逐步审查其操作:

  1. 威胁情报控制器定期轮询威胁情报。

  2. 威胁情报控制器处理该情报并在 Kubernetes 数据存储中为威胁情报创建 globalnetworkset 资源。

  3. 网络策略引擎和日志处理引擎读取威胁情报。

  4. 如果定义了网络策略,则网络策略引擎会为威胁情报实施网络策略。

  5. 日志引擎处理来自流日志数据存储的流日志,并为与威胁情报中 IP 地址匹配的流生成警报。

在第 4 步中,我们能够防止入侵,第 5 步是我们可以检测入侵的地方。

域名情报的特殊考虑事项

如前所述,威胁情报可以是 IP 地址或域名列表。对于域名情报,网络策略引擎必须支持基于域名的策略,并且日志处理引擎必须支持在流日志中捕获域名,并匹配来自情报中的域名的流日志中的域名。

请注意,所描述的技术能够检测并启用控制来防止恶意活动,因此使其实现成为入侵防护系统。

深度数据包检查

深度数据包检查(DPI)是一种入侵检测技术,其中网络流量被检查并与已知的恶意网络流量模式进行匹配。这要求超过第 3/第 4 层的数据包检查,并理解应用程序协议(例如,HTTP、MySQL 等)。与 IP 和域名的威胁数据源类似,还提供基于签名的数据源。OWASP Top 10SANs Top 25是用于检测数据包的网络和应用程序层中已知应用软件风险和软件漏洞的签名。当您考虑在您的集群中实施 DPI 时,您需要考虑一些因素。

首先,您应该在哪里实施 DPI?一种选择是在入口处实施,这是流量进入/退出集群的地点。在 Kubernetes 中,入口是一种资源,允许用户将服务暴露给集群外的客户端。这个主题在第八章中有深入探讨。在这次讨论中,我们假设服务作为 URL 暴露。因此,在这种情况下,您将在入口处实施 DPI(例如,负载均衡器或用于通过节点端口传输的节点)。这是一个不错的选择,但在这种情况下,限制在于您可以检测到恶意网络流量,但信息并不完整,因为检测早在周期的开始阶段。您将无法看到恶意流量的目标 Pod 是哪一个,并且必须查看所有可能的目标 Pod,然后将每个支持服务的 Pod 的活动相关联以理解攻击。另外,如果攻击使用另一种机制触发漏洞(例如,API 服务器、kube-proxy 漏洞或节点操作系统映像漏洞),则恶意流量源自集群内部,并且不会在入口处被检测到。因此,最好在集群内部为服务实施 DPI。

因此,如果您选择在每个服务内部实施 DPI 的选项,您需要考虑由于 Kubernetes 的分布式特性而产生的大量集群内流量。这带来了一个挑战,因为 DPI 需要进行数据包解析,可能会影响应用程序的延迟,并增加资源利用率。为了解决应用程序延迟的挑战,我们建议您将 DPI 视为检测恶意活动的机制,而不是防止攻击。这意味着 DPI 引擎不需要在线操作,可以处理流中每个数据包的副本;原始数据包流不受影响,因此对延迟敏感的应用程序没有影响。仍然存在由于数据包解析而引起的资源利用率问题。为了解决这个问题,我们建议您在 Kubernetes 集群中使用上下文来选择需要 DPI 的服务流量。这可以简单地通过标记那些关键且具有合规要求的服务并使用基于标签的选择器启用 DPI,或者您可以使用 DPI 作为对异常流量的响应动作,该异常流量由 SIEM 或您的日志和警报引擎识别。

另一个重要的考虑因素是 DPI 引擎需要流量是未加密的,因此,如果您在集群内部使用加密流量(例如 HTTPS),您需要在 DPI 引擎中实施解密,或者选择像 WireGuard 这样的加密技术,在出口流量之前实施 DPI,并在入口流量解密后实施 DPI。您应考虑像 Envoy 这样的代理,它允许流量被重定向至其进行解密、检查、加密,并发送至其目的地。

现在我们已经确认 DPI 需要作为 IDS 机制实施在集群内部,并需要有选择地启用以限制资源消耗,让我们来探讨一个 DPI 实施的示例参考架构,适用于您的 Kubernetes 集群。图 11-2 展示了一个参考实施。在我们审查参考实施之前,我们想介绍一些可以集成到您的集群中的著名 IDS 引擎。SnortSuricata 是一些可用的开源 IDS 引擎,但您可以根据您的用例选择任何适合的 IDS 引擎,包括实施您自己的 IDS 引擎。

图 11-2. 使用 Envoy 实现 DPI

图 11-2 显示了一个 Kubernetes 集群命名空间,其中包含几个微服务,这些微服务是应用程序的一部分。该图显示 Envoy 部署为每个节点的守护进程集。Envoy 是一个在 Kubernetes 集群中用于代理流量以进行分析和附加控制的众所周知的代理。我们建议您将 Envoy 用作透明代理,在此模式下,它终止要发送到支持服务的 pod 的连接,并在分析后将流量发送到支持服务的 pod。在第二个微服务的情况下,DPI 引擎作为 Envoy 的集成实施,Envoy 被配置为重定向发送到其自身的支持服务的流量。然后执行 DPI,Envoy 负责完成连接。Envoy 的透明模式意味着应用程序在数据包(例如 TCP/IP 头)中看不到任何差异。正如之前讨论的那样,如果 DPI 引擎检测到恶意流量,生成的警报将显示接收流量的 pod,并且可以轻松地检查该 pod 的活动并将其与恶意流量关联起来。在 图 11-2 中,以一个服务作为示例启用了流向此服务的 DPI。DPI 可以针对任何服务或一组 pod 启用;方法类似,但不同之处在于如何为流量重定向配置 Envoy。

我们建议您在 DPI 引擎中使用 Envoy,但也有其他集成 DPI 引擎到 Kubernetes 集群的方法。例如,如果您的集群运行 eBPF 数据平面,您可以在 BPF 程序中获取数据包的副本,并将其发送到 IDS 引擎进行分析。同样,如果您使用 VPP 作为数据平面,也可以集成 DPI 引擎以检查流量。

您可以选择适合您的选项,但重要的是考虑在您的 Kubernetes 集群中集成 DPI 引擎进行基于签名的恶意软件检测。接下来,让我们来审视日志记录和可见性及其在威胁防御策略中的作用。

日志记录和可见性

安全性的一个非常重要的部分是查看集群中活动的可见性(例如,pod 创建、Kubernetes 资源访问/更改、应用程序活动、网络活动)。通过在集群中启用日志记录来实现这一点。我们在 第五章 中详细介绍了日志收集和指标收集。在本节中,我们希望重申日志记录和可见性的以下关键方面:

  • 使用传统的五元组网络流量日志记录是不够的。您需要使用支持 Kubernetes 上下文丰富日志记录的工具,其中包括 pod、部署、副本集和服务之间的网络流量作为日志的一部分进行收集。

  • 在收集时,日志需要带有 Kubernetes 元数据,如标签、使用的策略、节点信息,甚至进程信息(容器中运行的进程)。这一点非常重要,因为 Kubernetes 的短暂性质,所有这些都在变化,当网络流量和 Kubernetes 元数据独立收集时,很难将恶意网络流量与 Kubernetes 元数据关联起来。

  • DNS 活动日志至关重要,并且必须像前面描述的网络流量日志一样带有 Kubernetes 元数据进行标注。

  • 基于应用协议的流量日志(例如 HTTP 头部、MySQL)同样至关重要,并且必须带有 Kubernetes 元数据进行收集。

  • 最后,Kubernetes 审计日志(活动日志)非常重要,必须进行收集,因为这些日志将帮助检测恶意用户的异常活动(例如重复被拒绝访问资源、创建服务账户等)。

对于实施日志收集,您可以选择使用多种工具和机制。云提供商提供了日志记录功能(例如 Google 的 Stackdriver、AWS 的 CloudWatch);您还可以选择使用诸如 Sysdig、Datadog 和 Calico Enterprise 等工具实施日志记录,这些工具提供了与 Kubernetes 上下文相关的日志记录功能。除了前述的日志收集外,您选择的工具还必须支持以下几个关键的简单功能,这些功能对您的 IDS 策略至关重要:

  • 工具必须支持警报功能,允许您查询日志并针对基于规则的异常设置警报(例如对于异常的 NXDOMAIN 请求、HTTP 或 DNS 协议中入站和出站流量不平衡、从特定命名空间的特定 Pod 到某些 Pod 的意外连接、过多的网络策略拒绝日志等)。

  • 工具必须支持使用基本机器学习技术对各种指标进行基线设定(例如对服务的连接数、HTTP 请求中的罕见用户代理进行检查),并将异常报告为警报。

  • 工具必须支持将日志和警报转发到外部 SIEM(如 Splunk、QRadar、Sumo Logic)或云提供商的安全中心(Azure Security Center)。

前面的部分介绍了日志收集和分析。虽然大多数 Kubernetes 环境中都提供了日志记录功能,但您需要确保选择的实施日志记录工具对您的 IDS 策略有效。

高级威胁防御技术

在本节中,我们将介绍一些您可以在集群中使用的高级威胁防御技术。这些技术旨在在 Kubernetes 环境中发挥作用,特别是用于检测攻击生命周期中的横向移动和数据外流阶段。

脚本化部署的 Pods/Resources

使用蜜罐是一种广为人知的技术,用于检测集群中的恶意行为者,并通过暴露在集群中的模拟或有意漏洞的应用程序来监视对这些应用程序的访问,从而了解他们的行动。这些应用程序充当金丝雀,通知蓝队入侵的发生,并阻止攻击者进一步访问实际敏感的应用程序和数据。一旦蓝队意识到情况,攻击可以追溯到初始向量,并且可以在集群中被隔离甚至删除。

在 Kubernetes 环境中应用此技术非常有效,这是因为应用清单的声明性质负责部署工作负载。无论集群是独立的还是作为复杂管道的一部分,工作负载通信都由应用程序的代码定义。任何未定义的通信最少可能被视为可疑,并且源资源可能已被 compromise。通过在生产工作负载周围引入虚假工作负载和服务,在工作负载被 compromise 时,攻击者无法区分其他真实和虚假工作负载。攻击者与集群操作员之间的不对称知识使得从被 compromise 的工作负载侧移动容易检测出来。

图 11-3 展示了在 Kubernetes 集群中实现这一目标的示例。

图 11-3. Kubernetes 集群中蜜罐的示例实现

Calico Enterprise 提供了蜜罐功能,当严格的网络策略或监控不可行时,提供了一种补充检测方法。 Calico Enterprise 蜜罐通过在敏感命名空间部署金丝雀工作负载和服务,并监视访问来运行。通过利用 Calico Enterprise 的监控和警报功能,对这些金丝雀工作负载进行的任何连接都将生成警报,并可以追溯到源头。应使用 DPI 引擎检查金丝雀流量,以提供基于签名的高保真警报,并显著减少误报。

基于 DNS 的攻击与防御

当您查看 Kubernetes 集群中的活动时,DNS 对于正在运行的应用程序至关重要。 Kubernetes 支持 DNS 作为基础设施,并支持使用 DNS 名称来命名 pod 和服务。 CoreDNS 是您 Kubernetes 集群的推荐 DNS 服务器。由于 DNS 对集群操作至关重要,因此需要允许集群内部和外部的 DNS 流量。这使得 DNS 成为攻击者针对的有吸引力的选项。在本节中,我们将讨论攻击者使用的域生成算法(DGA)攻击,用于建立到其命令与控制中心的连接以及数据外泄。

图 11-4 显示了域生成攻击的工作原理。对手首先在集群内下载一个利用已知种子和算法生成域名的漏洞。然后漏洞会查询算法生成的域名。相同的算法运行起来,启动一个 DNS 服务器来响应 DNS 查询,这个过程会重复,直到客户端和服务器的域名匹配。成功匹配后,集群已经与恶意软件的命令与控制服务器建立了成功的连接。

图 11-4. 基于域生成算法的攻击

由于域名是通过算法随机生成的,而查询是合法的 DNS 查询,因此使用 DNS 威胁源或在 DPI 边缘检测这些类型的攻击是不可能的。此外,集群中存在大量的 DNS 活动意味着恶意软件可以轻松地隐藏其活动。检测这类攻击的方法是使用机器学习技术,通过分析域名就能预测恶意域。另一个有效的机制是使用机器学习来基线化那些没有解析到有效服务器的 DNS 响应数量,并在这类失败的 DNS 查询增加时报告异常。

您可以通过安全研究团队与数据科学团队合作来构建威胁防御机制,实施域生成算法的检测机制。Calico Enterprise 提供了一个集成了其警报引擎的域生成算法实现。

结论

在本章中,我们介绍了如何在您的 Kubernetes 集群中实施威胁防御。以下是本章的主要要点:

  • 所介绍的技术基于我们目前的研究,这个领域在不断发展,对手使用更新的技术,安全团队正在研究解决这些威胁的方法。我们建议您的安全团队关注已发现的威胁,分析它们是否适用于 Kubernetes,并努力开发缓解技术。

  • Kubernetes 是一项新技术,我们开始看到它成为对手的一个关注领域,因此需要一种有效的威胁防御策略。

  • 理解网络安全杀伤链及其在 Kubernetes 中的应用对于构建有效的威胁防御策略至关重要。

  • 对于您来说,重要的是在集群内部的流量上应用威胁源和基于 DPI 的技术,以便检测内部发起的攻击。仅依靠边缘的这些技术是不够的,因为源自集群内部的流量可能不会通过边缘设备。

  • 蜜罐和基于域生成算法的攻击是您在 Kubernetes 集群中应实施的高级威胁防御技术的示例,以防范复杂的攻击。

第十二章:结论

我们希望这本书能帮助您了解 Kubernetes 部署的可观察性和安全性与传统部署的不同之处。我们希望这本书能够成为您设计和实施安全性和可观察性策略的指南,无论您是在旅程的早期阶段还是在采用 Kubernetes 的更进一步阶段。关键的观点是,在旅程的每个阶段都需要考虑安全性和可观察性;这不应该是您设计部署后才实施的事后想法。我们经常听到有人说:“我暂时不需要担心安全性或可观察性;让我先让我的工作负载在 Kubernetes 中运行起来。” 这种思维方式是不正确的,因为正确的安全实施可能会改变设计,并可能导致设计的不时迭代和实施的延迟。以下是 Kubernetes 不同的一些特征:

  • Kubernetes 是部署现代应用程序的最广泛采用的编排引擎,被用于公有云和本地部署。

  • Kubernetes 是声明性的,并使用户能够为他们的应用程序部署指定结果(例如,规模、规格、访问等)的性质。

  • Kubernetes 不断监视部署的状态,并采取纠正措施,以确保部署按照指定的方式运行。

  • Kubernetes 抽象了网络、IP 地址等细节,而允许用户使用高级构造(如标签)来定义身份。

  • 由于这些特性,为 Kubernetes 实施可观察性和安全性需要采用不同的方法。

Kubernetes 的这些特性也改变了开发过程和涉及的团队。以前,开发团队构建的应用程序将部署在预配基础设施上。在今天的世界中,当您在云中部署应用程序时,您首先会为应用程序需要的基础设施(如主机、虚拟机等)预配基础设施,然后将应用程序部署在预配的基础设施上。此外,基础设施是动态的,并根据应用程序的需求进行调整。以下列出了在 Kubernetes 集群中应用程序的生命周期以及各个团队的角色:

  • 应用程序的部署包括构建阶段(创建应用程序所需的资源)、部署阶段(使用 Kubernetes 部署应用程序)和运行时阶段(部署后的应用程序运行)。

  • 负责应用程序成功部署和运行的团队包括运维团队、平台团队、网络团队、安全团队和合规团队。

  • 要为您的应用设计一个有效的安全性和可观察性策略,您需要在所有阶段考虑安全性和可观察性。

  • 团队之间的合作对于成功至关重要,因为安全是所有涉及团队的共同责任。

下面的检查清单是确保你在 Kubernetes 部署中实现有效安全性和可观察性的良好指南:

  • 所有镜像在部署前都会进行已知漏洞的扫描,然后定期扫描已部署后发现的漏洞。

  • 所有部署的容器镜像都使用最小化的基础操作系统组件构建(例如 distroless 或 scratch 镜像)。

  • 主机操作系统采用不可变的 Linux 发行版,减少攻击面。

  • 主机操作系统和部署的 Pod 都配置了控制,只允许必要的访问(例如系统调用、文件系统访问)。

  • Kubernetes 集群部署采用密钥加密,确保 API 服务器和数据存储的安全访问。

  • Kubernetes 集群中工作负载的部署受到 RBAC 和准入控制器的最佳实践控制,以强制执行策略。

  • 所有服务的访问都遵循安全最佳实践,暴露给外部客户端。

  • Kubernetes 集群与周界安全设备(防火墙或网关)集成,使设备能够查看源自集群的流量,并添加有效的控制。

  • 你需要确保网络流量和应用程序流量的访问控制已经就位,使用 L3/L7 策略。

  • 确保你使用 Kubernetes 原生工具实现可观察性;例如,你的工具需要意识到支持服务或部署的 Pod 是相同的,并应作为一个单元来“观察”一个服务。

  • 确保你实施机器学习来基线集群中各种实体的行为,并在此基础上构建异常检测系统,用于检测安全事件。

  • 确保在集群中实施威胁防御功能,如 IDS、IPS 和高级威胁检测技术,以便检测集群内的恶意活动。

  • 确保你已经实现了数据传输加密,包括集群内部和与外部实体的通信。

祝愿你在 Kubernetes 之旅中实施工作负载的安全性和可观察性取得最好的结果!

posted @ 2025-11-15 13:07  绝不原创的飞龙  阅读(0)  评论(0)    收藏  举报