本文译自:
摘要:本文介绍了 Kubernetes 策略的七个步骤,包括基线、修复标签和注释、迁移到受限制的 Pod Security 标准、压制误报、加入常见加固指南、插入并播放、添加自定义规则以应对未预料的特殊情况。通过实施这些步骤,可以逐步减少配置错误和漏洞的数量,实现认证、合规和长期安全目标。
你刚刚开始了一份新工作,在这个工作中,你第一次有责任操作和管理 Kubernetes 基础设施。你对更深入地了解云原生充满了热情,但同时也非常担心。
是的,你关注的是编写符合命名和资源使用控制实践的安全应用程序的方法,但是关于已经部署到生产环境中的所有其他内容呢?你打开一个新的工具来查看正在发生的情况,发现有 100 个高或严重的 CVE 和 YAML 配置问题。你关闭标签页告诉自己,你以后会处理所有这些问题的。
你会吗?
也许最有雄心壮志和无所畏惧的人会,但问题在于云原生社区喜欢谈论安全、标准化和“左移”,但这些对话都无法减轻因安全、资源、语法和工具问题而产生的不安全感。没有一个开发范式或工具似乎发现了在不压垮人的情况下让错误配置可见的正确方式。
就像我们可能面对的所有待办事项列表一样,无论是工作还是家务,我们的大脑只能有效地处理有限数量的问题。太多问题了,我们就会迷失在上下文切换和优先处理不完整的临时解决方案之间。我们需要更好的方法来限制范围(即分类),设置里程碑,最终使安全工作可管理。
是时候忽略问题的数量,专注于交互地塑造,然后强制执行你的组织使用已建立策略的方式,以产生影响——无需产生不安全感。
云原生策略的历史
从 Kubernetes 的第一天开始,YAML 配置就是构建完整集群和运行应用程序的基石。作为开发人员应用程序代码和运维工程师维护集群之间的必要桥梁,它们不仅难以正确获取,而且还是 Kubernetes 中大多数部署/服务级别问题的根源。更有甚者,没有人——既不是开发人员,也不是运维工程师——想独自对此负责。
策略作为一种自动化的方式进入了云原生空间,用于编写和审批为生产环境编写的 YAML 配置。如果没有一个人或团队想要根据内部样式指南手动检查每个配置,那么策略可以慢慢塑造团队解决安全、资源使用和云原生实践中的常见配置错误的方式。更不用说任何唯一应用程序的规则或习语了。
Kubernetes 中策略的挑战在于它对如何、何时和为什么执行它们是不可知的。你可以用多种方式编写规则,在软件开发生命周期(SDLC)的不同点执行它们,并出于不同的原因使用它们。
在此混乱中,没有比 Pod 安全策略(PSP)更好的例子了,它在 2016 年 v1.3 中进入 Kubernetes 生态系统。PSP 的设计目的是控制 pod 的操作方式并拒绝任何不符合要求的配置。例如,它允许 K8s 管理员防止开发人员在任何地方运行特权 pod,从而实质上将低级别的 Linux 安全决策与开发生命周期分离开来。
PSP 从未离开 beta 阶段,有几个很好的理由。这些政策仅在人或进程请求创建 pod 时应用,这意味着没有办法对 PSP 进行改进或默认启用。Kubernetes 团队承认 PSP 使意外授予过于广泛的权限变得太容易了,除了其他困难[1]。
Kubernetes 安全领域的 PSP 时代充满了风险,这启发了一个新的发布周期管理规则:任何 Kubernetes 项目不能超过两个发布周期处于 beta 状态,必须成为稳定的或者标记为[弃用]( https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/)和删除。
另一方面,PSP 使 Kubernetes 安全领域朝着积极的方向发展:通过将 Kubernetes 安全策略的创建和实例化分离,PSP 开辟了一个新的外部接入控制器和策略执行工具生态系统,例如Kyverno[2]、Gatekeeper[3]和Monokle[4]。
我们用这些工具摆脱了 PSP 的束缚,并用 Pod Security Standard(PSS)替换了它。一会我们再来谈这个巨大的区别。
基于阶段的 Kubernetes 策略方法
在确定了策略创建和实例化之间的解耦后,您现在可以在不管您选择哪些工具的情况下,在您的集群、环境和团队之间应用一致的策略语言。您也可以随时更改您用于创建和实例化的工具,并在您的集群中获得可靠的结果。
创建通常发生在集成开发环境(IDE)中,这意味着您可以继续使用您当前最喜欢的语言来使用规则特定的语言,如Open Policy Agent (OPA)[5]、Kyverno 的声明性语法或 Go 或 TypeScript 等编程语言。
实例化和强制执行可以在软件开发生命周期的不同部分进行。正如我们在我们之前的101 级帖子[6]中看到的那样,您可以在配置生命周期的一个或多个点应用验证:
1. 通过开发人员的命令行界面(CLI)或 IDE 直接预提交
2. 通过您的CI/CD流水线进行预部署
3. 通过像 Kyverno 或 Gatekeeper 这样的接入控制器[7]进行后部署,或者
4. 在集群中检查部署状态是否仍符合您的策略标准。
策略的实例化、验证和强制执行越晚,危险的错误配置就越容易滑入生产环境,发现和修复任何发现的错误配置的原始来源所需的工作也越多。您可以在几个阶段实例化和强制执行策略,但越早越好——这正是 Monokle 擅长的,具有强大的预提交和预部署验证支持。
有了这个场景,以及对 Kubernetes 策略景观的理解,您可以开始消除您面前的误配置。
步骤 1:实施 Pod Security 标准
让我们从前面提到的 PSS 开始。Kubernetes 现在描述了三个包容性策略[8],您可以快速在整个集群中实施和执行。“特权”策略完全不受限制,应该仅保留给由管理员管理的系统和基础设施工作负载。
您应该从实例化“基线”策略开始,它允许最小规格的 Pod,这是大多数新接触 Kubernetes 的开发人员开始的地方:
从基线开始的好处是,您无需修改所有现有的 Dockerfile 和 Kubernetes 配置即可防止已知的权限升级。会有一些例外情况,稍后我会谈到。
在命名空间级别上创建和实例化这个策略级别是相对简单的:
您肯定会有一些特殊的服务需要比基线允许的访问权限更多,例如用于收集日志和可观察性的Promtail 代理[9]。在这些情况下,您需要在特权策略下运行那些命名空间。您需要跟进该供应商的安全改进,以限制您的风险。
通过强制执行 Pod Security 标准的基线水平来处理大多数配置,并允许一些特权配置,然后修复违反这些策略的任何误配置,您就完成了下一个策略里程碑。
步骤 2:修复标签和注释
标签用于标识资源进行分组或过滤,而注释则用于重要但不用于识别的上下文。如果您的头脑仍在旋转,来自 Ambassador Labs 的 Richard Li 的一个方便的定义[10]可能会帮助:“标签是为 Kubernetes 而设计的,而注释是为人类而设计的。”
标签应仅用于其预定目的,即使在这种情况下,您在何处以及如何应用它们时也要小心。过去,攻击者已使用标签[11]深入探索 Kubernetes 集群的架构,包括哪些节点运行单个 Pod,而不留下运行的查询的日志。
同样的想法也适用于注释:虽然它们是为人类而设计的,但它们经常被用于获取凭证[12],进而获得访问更多秘密的权限。如果您使用注释来描述应在出现问题的情况下联系的人员,请知道您正在为社交工程攻击创建额外的软目标。
步骤 3:迁移到受限制的 PSS
虽然基线是可允许但相对安全的,但“受限制”Pod Security 标准采用了目前加固 Pod 的实践。正如 Red Hat 的 Mo Khan曾经描述[13]的那样,受限制的标准确保“您能做的最糟糕的事情是毁掉自己”,而不是您的集群。
使用受限制的标准,开发人员必须编写在只读模式下运行的应用程序,仅启用 Pod 运行所需的 Linux 功能,不能在任何时候升级特权等。
我建议从基线开始并稍后迁移到受限制,作为单独的里程碑,因为后者几乎总是需要对现有的 Dockerfile 和 Kubernetes 配置进行主动更改。一旦您实例化并强制执行了受限制策略,您的配置将需要遵守这些策略,否则它们将被您的验证器或接入控制器拒绝。
步骤 3a:压制而不是忽略不可避免的误报
在完成基线和受限制的里程碑时,您正在接近策略管理的更成熟(和复杂)水平。为了确保每个人都在当前策略里程碑方面保持一致,您应该开始处理虚假阳性或必须显式允许的配置,尽管违反了受限制的 PSS。
在忽略规则或抑制规则之间进行选择时,始终选择抑制规则。这需要一个可审计的操作,具有日志或配置更改,以将例外情况编码为已建立的策略框架。您可以在源中添加抑制规则,直接添加到您的 K8s 配置中或在外部添加,其中开发人员请求其运维同行重新配置其验证器或接入控制器,以允许“误配置”通过。
在 Monokle 中,您可以将抑制直接添加到您的配置中作为注释,使用静态分析结果交换格式(SARIF)规范[14]所称的理由[15]:
第 4 步:加入常见加固指南
在这一步中,您已经超越了已有的 Kubernetes 安全框架,这意味着您需要更多地积极构建和努力实现自己的里程碑。
美国国家安全局(NSA)和网络安全和基础设施安全局(CISA)有一份受欢迎的Kubernetes 加固指南[16],其中详细介绍了不仅是 Pod 级别的改进措施,如有效地使用不可变容器文件系统,还包括网络分离、审计日志和威胁检测。
第 5 步:插入并播放
在实施了一些或所有已有的加固指南之后,每个新的策略都涉及选择、信任和权衡。花些时间在谷歌或 StackOverflow 上,你就会发现很多推荐的插入和播放策略。
你可以从众包策略中受益,其中许多来自于那些有着更独特经验的人,但请记住,虽然规则可能是出于良好意图的,但你并不了解推荐者的优先事项或操作上下文。他们知道如何实现某些“高挂水果”政策,因为他们不得不这样做,而不是因为这些政策普遍有价值。
目前正在进行的辩论是是否以及如何严格限制容器的资源需求。对于请求限制也是如此。不配置限制可能会引入安全风险,但如果严重限制 Pod,它们可能无法正常运行。
第 6 步:添加自定义规则以应对未预料的特殊情况
现在,你已经到了 Kubernetes 策略的远端,远离了导致生产负面影响的 20%的错误配置和漏洞。但即使现在,即使已经实施了所有的实践和集体云原生知识,你仍然无法免疫不会意地引发事故或停机的错误配置 - 安全和稳定的奇妙未知未知。
一个好的经验法则是,如果一个奇特的(错)配置在生产中引起了两次问题,那么就该将其编码为一条自定义规则,在开发过程中强制执行,或由准入控制器强制执行。它太重要了,不能仅在内部悄悄地记录下来,希望开发人员阅读它,在彼此的拉取请求审查中注意到它并捕获它。
一旦编码到您现有的策略中,自定义规则就成为了您尽可能接近开发人员执行的防护栏杆。如果你可以在开发人员提交工作之前就用验证到达开发人员,Monokle Cloud 就可以无缝地执行这一点,使用自定义插件和您本地运行的开发服务器,那么您可以节省整个组织大量的重复工作和调整他们的拇指等待 CI/CD 管道无可避免地失败时他们可以构建新功能或修复错误。
总结
如果您实施了以上所述的所有框架和里程碑,并对您的 Dockerfile 和 Kubernetes 配置进行了所有必要的更改以满足这些新策略,那么您可能会发现您的 90 个主要漏洞清单已经减少到了一个更易管理的数量。
您正在看到我们逐步塑造和执行 Kubernetes 策略的方法的价值。您与新策略和规则的影响互动得越多,就像 Monokle 在提交之前唯一做到的那样,就越容易在不压垮自己或其他人的情况下逐步迈出步伐。
您甚至可能会自豪地宣称,您的 Kubernetes 环境完全没有配置错误。这是一种胜利,毫无疑问,但这不是保证 - 总会有新的 Kubernetes 版本、新的应用程序和新的实践融入到您已经完成的工作中。利用框架和加固指南的优势在于,您有更好的共同基础来谈论您在认证、合规和长期安全目标方面的影响。
对于非专家来说,哪种听起来更有说服力:
• 您将 CVE 数量从 90 个降至 X 个,
• 还是您完全符合美国国家安全局的 Kubernetes 加固指南?
我们越早不再担心数字,而是更多地关注共同里程碑,在应用程序生命周期的早期(理想情况下是 pre-commit!)尽早执行,我们就能找到每个云原生策略的可持续甜蜜点。