Kubernetes 认证安全专家(CKS)学习指南(一)
原文:zh.annas-archive.org/md5/525e77013d4f84e5576c98a6b161f26a译者:飞龙
协议:CC BY-NC-SA 4.0
序言
Kubernetes 认证筹划自 2018 年以来已经存在五年。在这段时间里,安全性变得越来越重要,包括 Kubernetes 领域。最近,增长了认证 Kubernetes 安全专家 (CKS) 角色来满意需求。安全性可以有不同的方面,办理这些问题的方式可以非常多样化。这就是 Kubernetes 生态系统发挥作用的地方。除了 Kubernetes 内置的安全功能外,许多工具已经发展起来,资助识别和修复安全风险。作为 Kubernetes 管理员,你需要认识广泛的概念和工具,以加固你的集群和应用程序。
CKS 认证筹划旨在验证安全相关主题的能力,需要通过认证 Kubernetes 管理员 (CKA) 考试后才气注册。假如你对 Kubernetes 认证程序完全陌生,我发起首先了解 CKA 或认证 Kubernetes 应用开辟者 (CKAD) 筹划。
在本学习指南中,我将探究 CKS 考试涵盖的主题,以全面预备你通过认证考试。我们将研究何时以及如何应用 Kubernetes 的焦点概念和外部工具来掩护集群组件、集群配置以及在 Pod 中运行的应用程序。我还将提供一些提示,资助你更好地预备考试,并分享我预备考试的个人履历的所有方面。
CKS 不同于其他认证的典型多项选择格式。它完全基于表现,并要求你在巨大的时间压力下展示对手头任务的深入知识。你预备好一次通过考试了吗?
适合读者
本书适合已经通过 CKA 考试并希望在安全领域扩展知识的任何人。考虑到注册 CKS 前需要通过 CKA 考试,你应该已经认识考试问题的格式和环境。第一章 简要回首了考试课程的一般方面,但重点夸大了适用于 CKS 考试的信息。假如你还没有到场 CKA 考试,我发起你先阅读认证 Kubernetes 管理员 (CKA) 学习指南(O’Reilly)。这本书将为你提供开始学习 CKS 所需的底子。
学到什么
本书内容压缩了与 CKS 考试相关的最重要方面。不需要考虑云提供商特定的 Kubernetes 实现,如 AKS 或 GKE。鉴于 Kubernetes 中提供了大量的配置选项,几乎不大概涵盖所有用例和场景而不重复官方文档。发起考生参考Kubernetes 文档以获取更广泛的知识。与 CKS 考试相关的外部工具(如 Trivy 或 Falco)仅在高层次上举行了介绍。请参考它们的文档以探索更多功能、功能和配置选项。
本书结构
本书的大纲严格遵循 CKS 课程。虽然大概存在更天然的教学结构来学习 Kubernetes 的一般知识,但课程大纲将资助考生通过专注于特定主题来预备考试。因此,您大概会根据自己的知识水平交叉参考本书的其他章节。
请注意,本书仅涵盖与 CKS 考试相关的概念。不讨论底子的 Kubernetes 概念和原语。假如您希望深入了解,请参考 Kubernetes 文档或其他册本。
Kubernetes 的实际履历对于通过考试至关重要。每章都包罗名为“示例练习”的部门,此中包罗练习题。这些问题的办理方案可以在附录中找到。
本书使用的约定
本书使用以下排版约定:
斜体
表现新术语、URL 和电子邮件地址。
常量宽度
用于文件名、文件扩展名和程序清单,以及段落中用于引用程序元素(如变量或函数名称、数据库、数据类型、环境变量、语句和关键字)。
常量宽度粗体
显示用户应直接输入的下令或其他文本。
提示
此元素表现提示或发起。
注意
此元素表现一般说明。
警告
此元素表现警告或注意事项。
使用代码示例
本书中的一些代码片段使用反斜杠字符(\)来将单行代码分解为多行,以便适应页面。假如您直接从书中内容复制粘贴到终端或编辑器中,则需要手动整理代码。更好的选择是参考代码书的GitHub 存储库,那边已经有了正确的格式。
GitHub 存储库根据 Apache License 2.0 分发。代码可以在商业和开源项目中免费使用。假如在源代码中遇到问题或有疑问,请在GitHub 问题跟踪器中提出问题。我很乐意举行互换并办理大概出现的任何问题。
本书旨在资助您完成工作。一般而言,假如本书提供示例代码,您可以在您的程序和文档中使用它。除非您复制了代码的大部门,否则无需得到我们的许可。例如,编写一个使用本书多个代码块的程序不需要许可。贩卖或分发 O’Reilly 册本中的示例代码需要许可。引用本书并引用示例代码往返答问题不需要许可。将本书大量示例代码整合到您产物的文档中需要许可。我们感谢您的署名,但通常不要求署名。署名通常包括标题、作者、出书商和 ISBN。例如:“Certified Kubernetes Security Specialist (CKS) Study Guide by Benjamin Muschko (O’Reilly)。版权 2023 年 Automated Ascent, LLC, 978-1-098-13297-2。”
假如您认为您对代码示例的使用超出了公平使用或以上给出的权限,请随时通过permissions@oreilly.com与我们接洽。
O’Reilly 在线学习
注意
凌驾 40 年来,O’Reilly Media提供技能和商业培训、知识和见解,资助企业成功。
我们独特的专家和创新者网络通过册本、文章和我们的在线学习平台分享他们的知识和专业知识。O’Reilly 的在线学习平台为您提供按需访问的及时培训课程、深入的学习路径、交互式编码环境以及来自 O’Reilly 和 200 多家其他出书商的大量文本和视频。有关更多信息,请访问http://oreilly.com。
如何接洽我们
请将关于本书的评论和问题发送至出书商:
[*] O’Reilly Media, Inc.
[*] 1005 Gravenstein Highway North
[*] CA 95472 Sebastopol
[*] 800-889-8969(美国或加拿大)
[*] 707-829-7019(国际或本地)
[*] 707-829-0104(传真)
[*] support@oreilly.com
[*] https://www.oreilly.com/about/contact.xhtml
我们有本书的网页,列出勘误、示例和任何额外信息。您可以访问此页面:https://oreil.ly/cks-study-guide。
有关我们的册本和课程的消息和信息,请访问http://oreilly.com。
在 LinkedIn 上找到我们:https://linkedin.com/company/oreilly-media
在 Twitter 上关注我们:http://twitter.com/oreillymedia
在 YouTube 上观看我们:http://youtube.com/oreillymedia
在 Twitter 上关注作者:https://twitter.com/bmuschko
在 GitHub 上关注作者:https://github.com/bmuschko
关注作者的博客:https://bmuschko.com
致谢
每本册本项目都是一段漫长的路程,没有编辑部和技能审阅职员的资助是不大概完成的。特殊感谢 Robin Smorenburg,Werner Dijkerman,Michael Kehoe 和 Liz Rice 提供的具体技能指导和反馈。我还要感谢 O’Reilly Media 的编辑 John Devins 和 Michele Cronin,他们一直以来的支持和鼓励。
第一章:考试细节和资源
本章介绍了备考 Certified Kubernetes Security Specialist (CKS) 考试 时候候候候候候候候候候候候候候候候候候候候候候候候候候候候候候候。我们将讨论该认证的目标受众、课程设置和考试环境,以及本事和额外的学习资源。假如您对该认证筹划已经很认识,可以直接跳到覆盖技能概念的章节。
Kubernetes 认证学习路径
CNCF 提供四种不同的 Kubernetes 认证。Figure 1-1 根据目标受众对它们举行了分类。您会发现 CKS 是您可以得到的最高级别认证。它是唯一一个有通过其他认证作为先决条件的认证;其他所有认证都是独立的项目。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0101.png
图 1-1。Kubernetes 认证学习路径
让我们简要地检察每个认证的具体信息,看看 CKS 是否适合您。
Kubernetes and Cloud Native Associate (KCNA)
KCNA 是一个面向任何对云原生应用开辟、运行时环境和工具感爱好的入门级认证项目。虽然考试涵盖 Kubernetes,但并不盼望您实际办理实际问题。该考试适合对该主题有广泛了解的候选人。
Kubernetes and Cloud Native Security Associate (KCSA)
该认证偏重于安全概念的底子知识及其在 Kubernetes 集群中的应用。该程序的广度和深度与 KCNA 相当,因为它不要求实际动手办理问题。
Certified Kubernetes Application Developer (CKAD)
CKAD 考试偏重于验证您构建、配置和部署基于微服务的应用程序到 Kubernetes 的能力。您不需要实际实现应用程序;然而,该考试适合认识应用架构、运行时和编程语言等主题的开辟职员。
Certified Kubernetes Administrator (CKA)
CKA 考试的目标受众包括 DevOps 从业者、系统管理员和可靠性工程师。该考试测试您在 Kubernetes 管理员角色中执行任务的能力,包括集群、网络、存储和低级安全管理,重点放在故障排除场景上。
Certified Kubernetes Security Specialist (CKS)
CKS 考试扩展了 CKA 考试验证的主题。在您甚至能够报名到场 CKS 考试之前,通过 CKA 考试是必须的先决条件。对于这个认证,您需要对 Kubernetes 安全方面有更深入的了解。课程涵盖的主题包括应用构建容器化应用程序的最佳实践和确保安全的 Kubernetes 运行时环境。
考试目标
假如被利用,软件和 IT 底子办法中的漏洞大概对组织构成重大威胁。云原生存算基金会(CNCF)开辟了认证 Kubernetes 安全专家(CKS)证书,以验证 Kubernetes 管理员在掩护 Kubernetes 集群及此中运行的云原生软件方面的能力。作为 CKS 考试的一部门,您应该了解 Kubernetes 焦点安全功能,以及用于掩护应用程序和底子办法的第三方工具和已建立的实践。
考试期间使用的 Kubernetes 版本
在撰写本书时,考试基于 Kubernetes 1.26 版。本书的所有内容将遵循该特定版本的功能、API 和下令行支持。未来版本有大概会粉碎向后兼容性。在预备认证时,请查阅Kubernetes 发布说明,并使用考试中使用的 Kubernetes 版本举行练习,以避免不愉快的意外。
在本书中,我将通过提供特定用例来解释每种安全威胁。我们将从讨论答应攻击者访问集群、注入恶意代码或使用漏洞入侵系统的场景开始。然后,我们将涉及可以防止该情况发生的概念、实践和/或工具。通过这种方法,您将能够评估安全风险的严重性以及实验安全措施的须要性。
课程
以下概述列出了 CKS 考试的高级部门,也称为领域,以及它们的评分权重:
[*] 10%:集群设置
[*] 15%:集群硬化
[*] 15%:系统硬化
[*] 20%:最小化微服务漏洞
[*] 20%:供应链安全
[*] 20%:监控、日记和运行时安全性
册本工作原理
本书的大纲严格遵循 CKS 课程。虽然一般来说,学习 Kubernetes 大概会有更天然、更教学性的组织结构,但课程大纲将资助考试到场者通过专注于特定主题来预备考试。因此,根据你的现有知识水平,你会发现自己需要参考本书的其他章节。
让我们在接下来的章节具体分析每个领域。
集群设置
本节涵盖了 CKA 考试已经涵盖的 Kubernetes 概念;然而,它们假定您已经理解底子知识,并盼望您能够深入了解。在这里,您将被测试网络策略及其对在同一定名空间内和跨多个定名空间之间的 Pod 之间的网络通讯禁止和授予的影响。重要关注点将放在限制通讯以最小化攻击面上。此外,“集群设置”领域将验证您设置带有传输层安全性(TLS)停止的 Ingress 对象的知识。
夸大通过查抄集群设置来识别和修复安全漏洞。像 kube-bench 这样的外部工具可以资助主动化该过程。执行工具针对集群的效果将为您提供一个可执行的漏洞列表。根据发起更改集群的配置设置可以显着降低安全风险。
末了,锁定集群节点端点、端口和图形用户界面(GUI)可以资助防止攻击者控制集群。您需要了解默认集群设置,以便尽大概限制对它们的访问。需要查抄 Kubernetes 的二进制文件和可执行文件(如kubectl、kubeadm和 kubelet)的校验和,以确保它们没有被第三方窜改。您需要了解如何获取二进制文件的校验和文件以及如何使用它来验证可执行文件的有效性。
集群加固
大多数组织从答应开辟职员和管理员管理 Kubernetes 安装、配置和管理任何对象的集群开始。虽然这对于认识 Kubernetes 的团队来说是一种便利的方法,但并不安全,因为它大概会为攻击者打开闸门。一旦访问了集群,就可以执行任何恶意利用。
基于角色的访问控制(RBAC)将权限映射到用户或进程。考试要求深入了解涉及的 API 资源。领域“集群加固”还集中讨论保持集群版本最新以确保获取最新错误修复的主题。Kubernetes 通过端点公开 API 服务器。您应了解最小化其对外界暴露的策略。
系统加固
此领域的重点在于理解如何最小化对主机系统和外部网络的访问,以淘汰攻击面。这就是像 AppArmor 和 seccomp 这样的利用系统级工具发挥作用的地方。您需要展示它们的使用以满意要求。此领域还涉及在亚马逊云环境中运行的集群中使用 AWS IAM 角色的使用。
最小化微服务的漏洞
安全上下文定义了容器的特权和访问控制。平台和安全团队可以在组织级别上管理和执行所需的安全措施。考试要求您了解 Pod 安全策略和 OPA Gatekeeper 的目的。此外,您将被要求演示定义不同类型的 Secrets 并从 Pod 中使用它们以注入敏感的运行时信息。
偶然,您大概希望从未履历证的来源或埋伏不安全的来源实验容器镜像。像 gVisor 和 Kata Containers 这样的容器运行时沙箱可以在 Kubernetes 中配置,以使用非常受限的权限执行容器镜像。配置和使用这样的容器运行时沙箱是本事域的一部门。此外,您需要了解 mTLS Pod 对 Pod 加密的利益以及如何配置它。
供应链安全
容器安全始于底子镜像。您需要了解构建容器镜像的最佳实践,以最小化从一开始引入安全漏洞的风险。最优的做法是,您只答应从已经扫描过镜像漏洞的组织内部容器注册表中拉取受信托的容器镜像。仅答应这些注册表至关重要,并且是本事域重要主题之一。像 Trivy 这样的工具可以资助扫描镜像以查找漏洞,并被列为通过考试的要求之一。
监控、日记记录和运行时安全
此领域的重点之一是举动分析,即观察异常和恶意事件的过程。Falco 是本节中应认识的重要工具。容器启动后不应该可变,以避免为攻击者打开额外的后门。您需要了解最佳实践,并能够在容器的配置中应用它们。
审计日记记录对于及时检察集群事件或调试目的很有资助。配置 Kubernetes 集群的审计日记记录是考试的一部门。
涉及的 Kubernetes 原语
一些考试目标可以通过理解相关的焦点 Kubernetes 原语来实现。预计考试将在单个问题中结合多个概念。参考图 1-2 作为适用的 Kubernetes 资源及其关系的大略指南。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0102.png
图 1-2. 与考试相关的 Kubernetes 原语
除了 Kubernetes 焦点原语外,您还需要掌握开源项目提供的特定自定义资源定义(CRD)。例如,Open Policy Agent(OPA)Gatekeeper 提供了原语的 ConstraintTemplate。
涉及的外部工具
考试中有相当一部门内容需要展示你对外部安全工具的专业知识。课程中明确列出了一些工具,但还有其他属于同一功能类别的工具。至少,你需要认识以下工具:
[*] kube-bench
[*] AppArmor
[*] seccomp
[*] gVisor
[*] Kata Containers
[*] Trivy
[*] Falco
文档
考试期间,你可以打开明确定义的一系列网页作为参考。你可以自由浏览这些页面并在考试终端中复制粘贴代码。CKS 的 常见问题解答 (FAQ) 列出了一些答应的 URL。
Kubernetes 官方文档包括参考手册、GitHub 网站和博客:
[*] 参考手册: https://kubernetes.io/docs
[*] GitHub: https://github.com/kubernetes
[*] 博客: https://kubernetes.io/blog
对于外部工具,答应你打开并浏览以下网址:
[*] Trivy: https://github.com/aquasecurity/trivy
[*] Falco: https://falco.org/docs
[*] AppArmor: https://gitlab.com/apparmor/apparmor/-/wikis/Documentation
候选技能
CKS 认证假设你已经具备 Kubernetes 的管理员级理解。CNCF 要求你先得到 CKA 认证作为先决条件。没有这些证书,你将无法报名到场 CKS 考试。假如你还没有通过 CKA 考试,大概想复习相关主题,我发起你看一下我的册本 Certified Kubernetes Administrator (CKA) Study Guide。
在本书的别的部门,我将假设你已经掌握了通过 CKA 所需的知识。因此,我不会再重复涉及重叠主题的底子知识。为方便起见,我将在需要时指向 CKA 书中相关信息。请重新检察 CKA 书中有关考试环境和时间管理的部门。它们同样适用于 CKS 考试。
练习和实践考试
在通过考试时,动手练习非常重要。为此,你需要一个功能正常的 Kubernetes 集群环境。以下选项非常突出:
[*] 我发现使用 Vagrant 和 VirtualBox 运行一个或多个虚拟机非常有用。这些工具资助创建一个易于引导和按需处理的孤立 Kubernetes 环境。本书的一些实践练习使用此设置作为出发点。
[*] 在你的开辟者机器上安装一个简朴的 Kubernetes 集群相对来说是很轻易的。Kubernetes 文档根据你的利用系统提供各种安装选项。Minikube 在实验更高级功能(如 Ingress 或存储类)时非常有用,因为它提供了作为附加组件的须要功能,可以通过单个下令安装。
[*] 假如你是O’Reilly 学习平台的订阅者,你可以无限制地访问运行 Kubernetes 环境的实验室。此外,你可以通过以互动实验室形式的 CKS 实践测试来测试你的知识。
你大概还想尝试以下一些商业化的学习和练习资源:
[*] 册本Certified Kubernetes Administrator (CKA) Study Guide覆盖了 CKA 认证的课程内容。重新学习本书的材料,巩固底子知识。
[*] Killer Shell是一个模仿器,提供所有 Kubernetes 认证的示例练习。
[*] 来自 Study4exam 的CKS 实践考试提供了一个商业化的基于 Web 的测试环境,用于评估你的知识水平。
总结
CKS 考试验证你在 Kubernetes 中与安全相关的实际利用知识。你需要理解焦点 Kubernetes 基元和概念,以满意安全需求,如 RBAC、网络策略和 Ingress。考试还涉及有用的第三方安全工具。你需要展示如何有效地使用这些工具。通过 CKA 考试是 CKS 考试的须要条件。假如你还没有通过 CKA 考试,请确保先通过 CKA。
以下章节与考试大纲同等,这样你可以将内容映射到学习目标上。每章结束时,你会找到一些示例练习来巩固你的知识。每个领域的讨论以对最重要的学习要点的简短总结结束。
第二章:聚集设置
考试的第一个领域涉及与 Kubernetes 集群设置和配置相关的问题。在本章中,我们将仅深入探究与安全相关的方面,而不是 Kubernetes 管理员的标准责任。
在高层次上,本章涵盖以下概念:
[*] 使用网络策略来限制 Pod 到 Pod 的通讯
[*] 运行 CIS 基准工具以识别集群组件的安全风险
[*] 设置带有 TLS 支持的入口对象
[*] 掩护节点端口、API 端点和 GUI 访问
[*] 验证平台二进制文件与它们的校验和
使用网络策略来限制 Pod 到 Pod 的通讯
在 Kubernetes 中,为了使微服务架构正常运行,Pod 需要能够在同一节点上或不同节点上运行的另一个 Pod 举行通讯,而不需要网络地址转换(NAT)。Kubernetes 在每个 Pod 创建时从其节点的 Pod CIDR 范围内为其分配唯一的 IP 地址。该 IP 地址是临时的,因此不能恒久稳固。每次 Pod 重新启动时,都会租用一个新的 IP 地址。发起使用 Pod 到服务的通讯而不是 Pod 到 Pod 的通讯,以便依赖于同等的网络接口。
分配给 Pod 的 IP 地址在所有节点和定名空间中是唯一的。这是通过在注册节点时为每个节点分配专用子网来实现的。在节点上创建新的 Pod 时,IP 地址是从分配的子网中租赁的。这由容器网络接口(CNI)插件处理。因此,节点上的 Pod 可以与集群中任何其他节点上运行的所有其他 Pod 举行通讯。
网络策略雷同于防火墙规则,但用于 Pod 到 Pod 的通讯。规则可以包括网络流量的方向(入站和/或出站),一个或多个定名空间内或跨不同定名空间的多个 Pod 的目标端口。关于网络策略底子的深入覆盖,请参阅册本 Certified Kubernetes Application Developer (CKAD) Study Guide(O’Reilly)或 Kubernetes 文档。CKS 考试重要偏重于使用网络策略限制集群级访问。
正确定义网络策略的规则大概具有挑战性。网站 networkpolicy.io 提供了一个网络策略的可视化编辑器,在浏览器中呈现图形表现。
场景:攻击者得到对 Pod 的访问权限
假设你在为一家运营 Kubernetes 集群的公司工作,该集群有三个工作节点。工作节点 1 当前作为微服务架构的一部门运行两个 Pod。考虑到 Kubernetes 对 Pod 到 Pod 网络通讯的默认举动,Pod 1 可以无限制地与 Pod 2 举行通讯,反之亦然。
如您在图 2-1 中所见,攻击者已经访问了 Pod 1. 假如没有定义网络策略,攻击者可以简朴地与 Pod 2 举行通讯,并造成额外的损害。这种漏洞不限于单个定名空间。Pod 3 和 Pod 4 也可以被访问和受到威胁。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0201.png
图 2-1. 已经访问 Pod 1 的攻击者可以访问其他 Pod 的网络
观察默认举动
我们将设置三个 Pod 来演示实践中无限制的 Pod-to-Pod 网络通讯。如您在示例 2-1 中所见,YAML 清单定义了定名空间 g04 中名为 backend 和 frontend 的 Pods。other Pod 位于 default 定名空间中。观察定名空间和 Pods 的标签分配。稍后在本章节中定义网络策略时我们会引用它们。
示例 2-1. 位于不同定名空间中的三个 Pods 的 YAML 清单
apiVersion: v1
kind: Namespace
metadata:
labels:
app: orion
name: g04
---
apiVersion: v1
kind: Pod
metadata:
labels:
tier: backend
name: backend
namespace: g04
spec:
containers:
- image: bmuschko/nodejs-hello-world:1.0.0
name: hello
ports:
- containerPort: 3000
restartPolicy: Never
---
apiVersion: v1
kind: Pod
metadata:
labels:
tier: frontend
name: frontend
namespace: g04
spec:
containers:
- image: alpine
name: frontend
args:
- /bin/sh
- -c
- while true; do sleep 5; done;
restartPolicy: Never
---
apiVersion: v1
kind: Pod
metadata:
labels:
tier: outside
name: other
spec:
containers:
- image: alpine
name: other
args:
- /bin/sh
- -c
- while true; do sleep 5; done;
restartPolicy: Never
首先,使用声明性的 kubectl apply 下令从现有的 YAML 清单创建对象:
$ kubectl apply -f setup.yaml
namespace/g04 created
pod/backend created
pod/frontend created
pod/other created
让我们验证定名空间 g04 运行正确的 Pods。使用 -o wide CLI 选项来确定分配给 Pods 的虚拟 IP 地址。backend Pod 使用 IP 地址 10.0.0.43,而 frontend Pod 使用 IP 地址 10.0.0.193:
$ kubectl get pods -n g04 -o wide
NAME READY STATUS RESTARTS AGE IP NODE \
NOMINATED NODE READINESS GATES
backend 1/1 Running 0 15s 10.0.0.43 minikube \
<none> <none>
frontend 1/1 Running 0 15s 10.0.0.193 minikube \
<none> <none>
default 定名空间处理单个 Pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
other 1/1 Running 0 4h45m
frontend Pod 可以与 backend Pod 举行通讯,因为没有设置通讯限制:
$ kubectl exec frontend -it -n g04 -- /bin/sh
/ # wget --spider --timeout=1 10.0.0.43:3000
Connecting to 10.0.0.43:3000 (10.0.0.43:3000)
remote file exists
/ # exit
位于 default 定名空间中的 other Pod 可以与 backend Pod 举行通讯而没有问题:
$ kubectl exec other -it -- /bin/sh
/ # wget --spider --timeout=1 10.0.0.43:3000
Connecting to 10.0.0.43:3000 (10.0.0.43:3000)
remote file exists
/ # exit
在接下来的章节中,我们将讨论如何通过拒绝所有网络策略规则来限制 Pod-to-Pod 网络通讯的最大程度。然后,我们将仅针对微服务架构正常运行所需的网络通讯打开入口和/或出口通讯。
拒绝定向网络流量
限制 Pod-to-Pod 网络流量的最佳方法是使用最小权限原则。最小权限意味着 Pods 应该以最低特权举行网络通讯。通常情况下,您会从禁止任何方向的流量开始,然后开放应用架构所需的流量。
Kubernetes 文档提供了几个有用的 YAML 清单示例。示例 2-2 展示了一个拒绝定名空间 g04 中所有 Pods 入口流量的网络策略。
示例 2-2. 默认拒绝所有入口网络策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: g04
spec:
podSelector: {}
policyTypes:
- Ingress
选择所有 Pods 由赋值给 spec.podSelector 属性的值 {} 表现。spec.policyTypes 属性的值定义了流量的拒绝方向。对于入站流量,您可以将 Ingress 添加到数组中。出站流量可以通过 Egress 指定。在这个特定的例子中,我们禁止所有入站流量。出站流量仍然答应。
“拒绝所有”网络策略的内容已保存在文件deny-all-ingress-network-policy.yaml中。以下下令从文件创建对象:
$ kubectl apply -f deny-all-ingress-network-policy.yaml
networkpolicy.networking.k8s.io/default-deny-ingress created
让我们看看这如何改变 Pod 到 Pod 网络通讯的运行时举动。frontend Pod 无法再与backend Pod 通讯,通过运行与之前相同的wget下令观察到这一点。网络调用在一秒后超时,由 CLI 选项--timeout定义:
$ kubectl exec frontend -it -n g04 -- /bin/sh
/ # wget --spider --timeout=1 10.0.0.43:3000
Connecting to 10.0.0.43:3000 (10.0.0.43:3000)
wget: download timed out
/ # exit
此外,运行在不同定名空间中的 Pod 也无法再毗连到backend Pod。以下wget下令从运行在default定名空间中的other Pod 到backend Pod 的 IP 地址的调用:
$ kubectl exec other -it -- /bin/sh
/ # wget --spider --timeout=1 10.0.0.43:3000
Connecting to 10.0.0.43:3000 (10.0.0.43:3000)
wget: download timed out
此调用也超时。
答应细粒度入站流量
网络策略是可加的。要为网络通讯授予更多权限,只需创建另一个具有更精细规则的网络策略。例如,我们想要仅答应来自同一定名空间中的frontend Pod 的入口流量访问backend Pod。独立于它们运行的定名空间,应拒绝所有其他 Pod 的入口流量。
网络策略大量使用标签选择来定义规则。识别g04定名空间中的标签及其运行在同一定名空间中的 Pod 对象,以便在网络策略中使用它们:
$ kubectl get ns g04 --show-labels
NAME STATUS AGE LABELS
g04 Active 12m app=orion,kubernetes.io/metadata.name=g04
$ kubectl get pods -n g04 --show-labels
NAME READY STATUS RESTARTS AGE LABELS
backend 1/1 Running 0 9m46s tier=backend
frontend 1/1 Running 0 9m46s tier=frontend
g04定名空间的标签分配包括键值对app=orion。backend Pod 的标签集包括键值对tier=backend,而frontend Pod 则包括键值对tier=frontend。
创建一个新的网络策略,答应frontend Pod 只在端口 3000 上与backend Pod 通讯。不答应其他任何通讯。在示例 2-3 中,YAML 清单显示了完整的网络策略定义。
示例 2-3. 答应入口流量的网络策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-ingress
namespace: g04
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
app: orion
podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 3000
网络策略的定义已存储在文件backend-ingress-network-policy.yaml中。从文件创建对象:
$ kubectl apply -f backend-ingress-network-policy.yaml
networkpolicy.networking.k8s.io/backend-ingress created
frontend Pod 如今可以与backend Pod 通讯:
$ kubectl exec frontend -it -n g04 -- /bin/sh
/ # wget --spider --timeout=1 10.0.0.43:3000
Connecting to 10.0.0.43:3000 (10.0.0.43:3000)
remote file exists
/ # exit
仍在g04定名空间之外运行的 Pod 无法毗连到backend Pod。wget下令超时:
$ kubectl exec other -it -- /bin/sh
/ # wget --spider --timeout=1 10.0.0.43:3000
Connecting to 10.0.0.43:3000 (10.0.0.43:3000)
wget: download timed out
应用 Kubernetes 组件安全最佳实践
管理本地 Kubernetes 集群可以完全控制应用于集群组件(如 API 服务器、etcd、kubelet 等)的配置选项。当创建集群节点时,使用kubeadm的默认配置设置通常并不罕见。此中一些默认设置大概会暴露集群组件,使其面临不须要的攻击机会。
加固集群安全措施是任何寻求最小化攻击向量的 Kubernetes 管理员的关键运动。假如你了解最佳实践,可以手动执行此运动,大概使用主动化过程。
互联网安全中心(CIS)是一个非营利性组织,发布网络安全最佳实践。此中包括他们的最佳实践之一是 Kubernetes CIS Benchmark,这是针对 Kubernetes 环境的最佳实践目次。您将在他们的网页上找到针对集群组件推荐的具体安全设置列表。
云提供商 Kubernetes 环境的 CIS 基准测试
Kubernetes CIS Benchmark 针对自管理的 Kubernetes 安装。云提供商的 Kubernetes 环境(例如 Amazon Elastic Kubernetes Service (EKS) 和 Google Kubernetes Engine (GKE))提供了由其自己的下令行工具支持的托管控制平面。因此,Kubernetes CIS Benchmark 提出的安全发起大概不太适用。一些工具,如下讨论的 kube-bench,专门为云提供商提供验证查抄。
使用 kube-bench
使用工具kube-bench可以主动化地查抄 Kubernetes 集群组件是否符合 CIS 基准最佳实践。kube-bench 可以通过多种方式执行。例如,您可以将其安装为平台特定的二进制文件,如 RPM 或 Debian 文件。直接在 Kubernetes 集群上的 Pod 中运行 kube-bench 是最方便和直接的验证过程。为此,使用工具的 GitHub 仓库中提供的 YAML 文件创建一个 Job 对象。
首先,根据需求从文件 job-master.yaml 或 job-node.yaml 创建 Job,以查抄控制平面节点或工作节点。以下下令对控制平面节点执行验证查抄:
$ kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/\
main/job-master.yaml
job.batch/kube-bench-master created
在 Job 执行期间,可以通过其名称在 default 定名空间中标识运行验证过程的 Pod。Pod 的名称以 kube-bench 前缀开始,然后附加节点类型及末端的哈希。以下输出使用名为 kube-bench-master-8f6qh 的 Pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kube-bench-master-8f6qh 0/1 Completed 0 45s
期待 Pod 转换为“已完成”状态,以确保所有验证查抄已完成。您可以通过转储 Pod 的日记检察基准效果:
$ kubectl logs kube-bench-master-8f6qh
偶然,将验证效果写入文件大概更为方便。您可以将 kubectl logs 下令的输出重定向到文件,例如使用下令 kubectl logs kube-bench-master-8f6qh > control-plane-kube-bench-results.txt。
kube-bench 验证效果
天生的验证效果大概会很长,但包括以下关键元素:被查抄节点的类型,被查抄的组件,通过查抄的列表,未通过查抄的列表,警告列表和高级别摘要:
1 Control Plane Security Configuration <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/1.png>
1.1 Control Plane Node Configuration Files
1.1.1 Ensure that the API server pod specification file permissions are \
set to 644 or more restrictive (Automated) <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/2.png>
...
1.2 API Server
1.2.1 Ensure that the --anonymous-auth argument is set to false \
(Manual) <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/3.png>
...
1.2.6 Ensure that the --kubelet-certificate-authority argument is set \
as appropriate (Automated) <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/4.png>
== Remediations master ==
...
1.2.1 Edit the API server pod specification file /etc/kubernetes/manifests/ \
kube-apiserver.yaml on the control plane node and set the below parameter.
--anonymous-auth=false
...
1.2.6 Follow the Kubernetes documentation and setup the TLS connection between <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/5.png>
the apiserver and kubelets. Then, edit the API server pod specification file <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/5.png>
/etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and \ <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/5.png>
set the --kubelet-certificate-authority parameter to the path to the cert \ <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/5.png>
file for the certificate authority. <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/5.png>
--kubelet-certificate-authority=<ca-string> <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/5.png>
...
== Summary total == <https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/6.png>
42 checks PASS
9 checks FAIL
11 checks WARN
0 checks INFO
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_setup_CO1-1
在本例中,被查抄的节点是控制平面节点。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_setup_CO1-2
一个通过的查抄。这里是 API 服务器配置文件的文件权限。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_setup_CO1-3
提示消息,提示您手动查抄提供给 API 服务器可执行文件的参数值。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_setup_CO1-4
一个失败的查抄。例如,应为 API 服务器可执行文件设置标志--kubelet-certificate-authority。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_setup_CO1-5
修复问题所需的纠正利用。失败或警告的编号,例如 1.2.1,对应于纠正利用分配的编号。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_setup_CO1-11
所有通过和失败查抄的摘要,以及警告和信息消息。
修复检测到的安全问题
最初,陈诉的警告和失败查抄列表大概会让人有些不知所措。请记住,您不必一次性修复所有问题。一些查抄只是指南或提示,用于验证配置的分配值。以下步骤将引导您完成消除警告消息的过程。
控制平面组件的配置文件可以在控制平面节点的主机系统上的目次/etc/kubernetes/manifests中找到。假设您想修复由 kube-bench 陈诉的警告 1.2.12:
1.2 API Server
...
1.2.12 Ensure that the admission control plugin AlwaysPullImages is \
set (Manual)
== Remediations master ==
...
1.2.12 Edit the API server pod specification file /etc/kubernetes/manifests/ \
kube-apiserver.yaml
on the control plane node and set the --enable-admission-plugins parameter \
to include AlwaysPullImages.
--enable-admission-plugins=...,AlwaysPullImages,...
如纠正利用所发起的,您应编辑 API 服务器的配置文件并将值AlwaysPullImages添加到准入插件列表中。继续编辑文件kube-apiserver.yaml:
$ sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml
在参数--enable-admission-plugins中添加值AlwaysPullImages后,效果大概如下所示:
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: \
192.168.56.10:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=192.168.56.10
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction,AlwaysPullImages
...
将更改保存到文件。在kube-system定名空间中运行 API 服务器的 Pod 将主动重启。启动过程大概需要几秒钟。因此,执行以下下令大概需要一段时间才气成功:
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
...
kube-apiserver-control-plane 1/1 Running 0 71m
...
在验证更改效果之前,您需要删除现有的作业对象:
$ kubectl delete job kube-bench-master
job.batch "kube-bench-master" deleted
验证查抄 1.2.12 如今陈诉通过的效果:
$ kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/\
main/job-master.yaml
job.batch/kube-bench-master created
$ kubectl get podsNAME READY STATUS RESTARTS AGEkube-bench-master-5gjdn 0/1 Completed 0 10s$ kubectl logs kube-bench-master-5gjdn | grep 1.2.12 1.2.12 Ensure that the admission control plugin AlwaysPullImages is \set (Manual) 创建带有 TLS 停止的 Ingress
Ingress 会根据匹配的 URL 上下文路径,将来自集群外部的 HTTP 和/或 HTTPS 流量路由到一个或多个服务。您可以在图 2-2 中看到其功能。
https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0202.png
图 2-2. 通过 HTTP(S)管理对服务的外部访问
Ingress 已配置为接受来自集群外部的 HTTP 和 HTTPS 流量。假如调用方提供上下文路径/app,则流量路由到服务 1。假如调用方提供上下文路径/api,则流量路由到服务 2。重要的是指出,一旦流过 Ingress,通讯通常使用未加密的 HTTP 网络通讯。
考虑到 Ingress API 资源是 CKAD 和 CKA 考试的一部门,我们不再在这里讨论底子知识。有关具体讨论,请参阅Certified Kubernetes Administrator (CKA) Study Guide或Kubernetes 文档中的信息。
Ingress 控制器的角色
请记住,没有 Ingress 控制器,Ingress 无法工作。Ingress 控制器评估由 Ingress 定义的规则集,确定流量路由。一个生产级别的 Ingress 控制器示例是F5 NGINX Ingress Controller或AKS Application Gateway Ingress Controller。您可以在Kubernetes 文档中找到其他选项。假如您使用 minikube,请确保启用 Ingress 插件。
CKS 的重要焦点是设置带有 TLS 停止的 Ingress 对象。配置 Ingress 以举行 HTTPS 通讯可以免除在服务级别上处理网络通讯安全性的麻烦。在本书的本节中,您将学习如何创建 TLS 证书和密钥,如何将证书和密钥提供给 TLS 类型的 Secret 对象,以及如何配置 Ingress 对象以支持 HTTPS 通讯。
设置 Ingress 后端
在 Ingress 的上下文中,后端是服务名称和端口的组合。在创建 Ingress 之前,我们将处理服务、部署和运行 nginx 的 Pod,以便稍后演示将 HTTPS 流量路由到实际应用程序。所有这些对象都应存在于定名空间 t75 中。示例 2-4 将所有这些资源定义为单个 YAML 清单文件 setup.yaml,以便快速创建 Ingress 后端。
示例 2-4. 通过服务暴露 nginx 的 YAML 清单
apiVersion: v1
kind: Namespace
metadata:
name: t75
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: t75
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
---
apiVersion: v1
kind: Service
metadata:
name: accounting-service
namespace: t75
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
使用以下命令从 YAML 文件创建对象:
$ kubectl apply -f setup.yaml
namespace/t75 created
deployment.apps/nginx-deployment created
service/accounting-service created
让我们快速验证对象已正确创建,并且 Pod 已转换为“Running”状态。执行get all下令时,您应该看到一个名为 nginx-deployment 的控制三个副本的部署,以及一个类型为 ClusterIP 的名为 accounting-service 的服务:
$ kubectl get all -n t75
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-6595874d85-5rdrh 1/1 Running 0 108s
pod/nginx-deployment-6595874d85-jmhvh 1/1 Running 0 108s
pod/nginx-deployment-6595874d85-vtwxp 1/1 Running 0 108s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) \
AGE
service/accounting-service ClusterIP 10.97.101.228 <none> 80/TCP \
108s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 3/3 3 3 108s
当在同一节点上的另一个 Pod 中调用服务端点时,应该从 nginx Pod 得到成功响应。这里我们使用wget下令来验证这种举动:
$ kubectl run tmp --image=busybox --restart=Never -it --rm \
-- wget 10.97.101.228:80
Connecting to 10.97.101.228:80 (10.97.101.228:80)
saving to 'index.xhtml'
index.xhtml 100% |************| 6120:00:00 ETA
'index.xhtml' saved
pod "tmp" deleted**********
有了这些对象并按预期运行,我们如今可以集中精力创建带有 TLS 停止的 Ingress。 ****## 创建 TLS 证书和密钥
在创建 TLS Secret 之前,我们需要天生 TLS 证书和密钥。为此,我们将使用 OpenSSL 下令。天生的文件名为 accounting.crt 和 accounting.key:
$ openssl req -nodes -new -x509 -keyout accounting.key -out accounting.crt \
-subj "/CN=accounting.tls"
Generating a 2048 bit RSA private key
...........................+
..........................+
writing new private key to 'accounting.key'
-----
$ ls
accounting.crt accounting.key
用于生产环境,您需要天生一个密钥文件,并使用它从证书颁发机构(CA)获取 TLS 证书。有关创建 TLS 证书和密钥的更多信息,请参阅 OpenSSL 文档。
创建 TLS 类型的 Secret
创建 Secret 最简朴的方法是使用下令式下令的资助。此创建方法不需要您手动对证书和密钥值举行 base64 编码。编码在对象创建时会主动发生。以下下令使用 Secret 选项 tls 并使用选项 --cert 和 --key 分配证书和密钥文件名:
$ kubectl create secret tls accounting-secret --cert=accounting.crt \
--key=accounting.key -n t75
secret/accounting-secret created
示例 2-5 显示了假如要声明性地创建对象,则 TLS Secret 的 YAML 表现。
示例 2-5. 使用类型 kubernetes.io/tls 的 Secret
apiVersion: v1
kind: Secret
metadata:
name: accounting-secret
namespace: t75
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk...
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk...
确保将 tls.crt 和 tls.key 属性的值分配为单行的 base64 编码值。要天生 base64 编码的值,只需将 base64 下令指向要转换内容的文件名即可。以下示例将文件 accounting.crt 的内容举行了 base64 编码:
$ base64 accounting.crt
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNyakNDQ...
创建 Ingress
您可以使用一行下令的下令式方法创建 Ingress,如以下片段所示。订定 --rule 参数的值是件难事。您大概需要参考 create ingress 下令的 --help 选项,因为它需要特定的表达式。创建 Ingress 对象与 TLS Secret 之间毗连的相关信息是附加的参数 tls=accounting-secret:
$ kubectl create ingress accounting-ingress \
--rule="accounting.internal.acme.com/*=accounting-service:80, \
tls=accounting-secret" -n t75
ingress.networking.k8s.io/accounting-ingress created
示例 2-6 显示了 Ingress 的 YAML 表现。定义 TLS 信息的属性是 spec.tls[]。
示例 2-6. 定义 TLS 停止 Ingress 的 YAML 清单
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: accounting-ingress
namespace: t75
spec:
tls:
- hosts:
- accounting.internal.acme.com
secretName: accounting-secret
rules:
- host: accounting.internal.acme.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: accounting-service
port:
number: 80
使用下令式或声明式方法创建 Ingress 对象后,您应该能够在定名空间 t75 中找到它。正如您在以下输出中看到的那样,端口 443 列在 “PORT” 列中,表现已启用 TLS 停止:
$ kubectl get ingress -n t75
NAME CLASS HOSTS ADDRESS \
PORTS AGE
accounting-ingress nginx accounting.internal.acme.com 192.168.64.91 \
80, 443 55s
形貌 Ingress 对象显示,后端可以映射到路径 / 并通过名为 accounting-service 的 Service 将流量路由到 Pod:
$ kubectl describe ingress accounting-ingress -n t75
Name: accounting-ingress
Labels: <none>
Namespace: t75
Address: 192.168.64.91
Ingress Class: nginx
Default backend:<default>
TLS:
accounting-secret terminates accounting.internal.acme.com
Rules:
Host PathBackends
---- ------------
accounting.internal.acme.com
/ accounting-service:80 \
(172.17.0.5:80,172.17.0.6:80,172.17.0.7:80)
Annotations: <none>
Events:
Type ReasonAge From Message
---- ---------- ---- -------
NormalSync 1s (x2 over 31s)nginx-ingress-controllerScheduled for sync
调用 Ingress
要在您的本地 Kubernetes 集群上测试举动,您需要首先找出节点的 IP 地址。以下下令在 minikube 环境中显示 IP 地址:
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP \
EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
minikube Ready control-plane 3d19h v1.24.1 192.168.64.91 \
<none> Buildroot 2021.02.12 5.10.57 docker://20.10.16
接下来,您需要将 IP 地址添加到主机名映射到您的 /etc/hosts 文件中:
$ sudo vim /etc/hosts
...
192.168.64.91 accounting.internal.acme.com
您如今可以使用分配的域名发送 HTTPS 哀求到 Ingress,并收到 HTTP 响应码 200:
$ wget -O- https://accounting.internal.acme.com --no-check-certificate
--2022-07-28 15:32:43--https://accounting.internal.acme.com/
Resolving accounting.internal.acme.com (accounting.internal.acme.com)... \
192.168.64.91
Connecting to accounting.internal.acme.com (accounting.internal.acme.com) \
|192.168.64.91|:443... connected.
WARNING: cannot verify accounting.internal.acme.com's certificate, issued \
by ‘CN=Kubernetes Ingress Controller Fake Certificate,O=Acme Co’:
Self-signed certificate encountered.
WARNING: no certificate subject alternative name matches
requested host name ‘accounting.internal.acme.com’.
HTTP request sent, awaiting response... 200 OK
```********# 保护节点元数据和端点
Kubernetes 集群公开用于与集群组件通信的端口。例如,API 服务器默认使用端口 6443,以便客户端(如 `kubectl`)在执行命令时可以与其通信。
Kubernetes 文档列出了这些端口在 [“端口和协议”](https://oreil.ly/iN993) 中。以下两个表格显示了每个节点的默认端口分配。
表 2-1 显示了集群节点上的默认入站端口。
表 2-1\. 入站控制平面节点端口
| 端口范围 | 用途 |
| --- | --- |
| 6643 | Kubernetes API 服务器 |
| 2379–2380 | etcd 服务器客户端 API |
| 10250 | Kubelet API |
| 10259 | kube-scheduler |
| 10257 | kube-controller-manager |
许多这些端口是可配置的。例如,您可以通过在配置文件 `/etc/kubernetes/manifests/kube-apiserver.yaml` 中使用 `--secure-port` 标志提供不同的值来修改 API 服务器端口,如在集群组件的 [文档](https://oreil.ly/TTzAz) 中所述。有关所有其他集群组件,请参阅它们各自的文档。
表 2-2 列出了工作节点上的默认入站端口。
表 2-2\. 入站工作节点端口
| 端口范围 | 用途 |
| --- | --- |
| 10250 | Kubelet API |
| 30000–32767 | NodePort 服务 |
要保护集群组件使用的端口,设置防火墙规则以减少攻击面。例如,您可以决定不将 API 服务器暴露给企业内部以外的任何人。只有在 VPN 登录时,使用 `kubectl` 的客户端才能对 Kubernetes 集群运行命令,从而使集群更不容易受到攻击。
云提供商 Kubernetes 集群(例如 AWS、Azure 或 Google Cloud)公开了所谓的元数据服务。元数据服务是可以提供敏感数据(如认证令牌)的 API,供 VM 或 Pod 在不需要额外授权的情况下消费。在 CKS 考试中,您需要了解这些节点端点和云提供商元数据服务。此外,您应该对如何防止未经授权访问它们有高层次的理解。
## 场景:被攻击的 Pod 可以访问元数据服务器
图 2-3 显示了在云提供商 Kubernetes 集群中一个节点上运行的 Pod 的攻击者。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0203.png>
###### 图 2-3\. 攻击者已经获得对 Pod 的访问权限,因此可以访问元数据服务器。
对元数据服务器的访问未受到任何限制。攻击者可以检索敏感信息,这可能会打开其他入侵可能性。
## 使用网络策略保护元数据服务器访问
让我们选择一个暴露元数据端点的云提供商。在 AWS 中,可以使用 IP 地址 169.254.169.254 访问元数据服务器,详见 (https://oreil.ly/6DsIx)。所暴露的端点可以提供访问 EC2 实例元数据的权限。例如,您可以获取实例的本地 IP 地址以便通过脚本管理外部应用程序的连接或与实例联系。参见相应的 [文档页面](https://oreil.ly/Bwdej),了解使用 curl 命令行工具调用这些端点的详细信息。
要防止命名空间中的任何 Pod 访问元数据服务器的 IP 地址,需设置一个网络策略,允许所有 IP 地址的出站流量,除了 169.254.169.254\. 示例 2-7 展示了带有此规则集的 YAML 清单。
##### 示例 2-7\. 默认拒绝向 IP 地址 169.254.169.254 的出站网络策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress-metadata-server
namespace: a12
spec:
podSelector: {}
policyTypes:
[*]Egress
egress:
[*]to:
[*]ipBlock:
cidr: 0.0.0.0/0
except:
[*]169.254.169.254/32
创建网络策略后,命名空间 `a12` 中的 Pod 不应再能访问元数据端点。有关使用 curl 访问端点的详细示例,请参阅相关的 (https://oreil.ly/fQ07b)。
# 保护 GUI 元素
`kubectl` 工具并非管理集群的唯一用户界面(UI)。尽管 `kubectl` 允许进行精细操作,但大多数组织更喜欢使用更便捷的图形用户界面(GUI)来管理集群中的对象。您可以从多种选择中进行选择。(https://oreil.ly/ABDQo) 是一个免费的基于 Web 的应用程序。其他用于 Kubernetes 的 GUI 仪表板如 (https://oreil.ly/i_FJv) 则通过添加事件追踪或硬件资源消耗可视化等功能,扩展了基本功能。在本节中,我们将重点介绍 Kubernetes 仪表板,因为它易于安装和配置。
## 情景:攻击者获得了对仪表板功能的访问权限。
Kubernetes 仪表板作为集群内的一个 Pod 运行。安装仪表板还会创建一个 `ClusterIP` 类型的 Service,只允许从集群内部访问该端点。要使仪表板对最终用户可访问,必须将该 Service 暴露到集群外部。例如,可以切换到 `NodePort` Service 类型或者部署一个 Ingress。图 2-4 展示了部署和访问仪表板的高级架构。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0204.png>
###### 图 2-4\. 成功访问仪表板的攻击者
一旦将仪表板暴露给外部世界,攻击者可能会获取访问权限。如果没有正确的安全设置,对象可以被删除、修改或用于恶意目的。这种攻击的最著名受害者是特斯拉,在 2018 年成为黑客攻击的牺牲品,黑客们成功访问了其未受保护的仪表板以进行加密货币挖掘。从那时起,仪表板的新版本更改了默认设置,以使其从一开始就更安全。
## 安装 Kubernetes 仪表板
安装 Kubernetes 仪表板非常简单。您可以使用项目 GitHub 存储库中提供的 YAML 清单创建相关对象。以下命令安装所有必要的对象:
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/
v2.6.0/aio/deploy/recommended.yaml
# 渲染仪表板中的度量数据
如果您有兴趣检查仪表板功能中的资源消耗度量标准,您可能还希望安装[度量服务器](https://oreil.ly/3Rtkl)。
您可以在`kubernetes-dashboard`命名空间中找到清单创建的对象。其中包括 Deployments、Pods 和 Services。以下命令列出所有这些对象:
$ kubectl get deployments,pods,services -n kubernetes-dashboard
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/dashboard-metrics-scraper 1/1 1 1 11m
deployment.apps/kubernetes-dashboard 1/1 1 1 11m
NAME READY STATUS RESTARTS AGE
pod/dashboard-metrics-scraper-78dbd9dbf5-f8z4x 1/1 Running 0 11m
pod/kubernetes-dashboard-5fd5574d9f-ns7nl 1/1 Running 0 11m
NAME TYPE CLUSTER-IP EXTERNAL-IP
PORT(S) AGE
service/dashboard-metrics-scraper ClusterIP 10.98.6.37
8000/TCP 11m
service/kubernetes-dashboard ClusterIP 10.102.234.158
80/TCP 11m
## 访问 Kubernetes 仪表板
`kubectl proxy`命令可帮助临时创建代理,允许您在浏览器中打开仪表板。此功能仅用于故障排除目的,不适用于生产环境。您可以在[文档](https://oreil.ly/gGsqX)中找到有关`proxy`命令的信息:
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
使用[*http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy*](http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy)网址在浏览器中打开。仪表板将要求您提供身份验证方法和凭据。配置仪表板的推荐方式是通过承载令牌。
## 创建具有管理权限的用户
在登录屏幕中进行身份验证之前,您需要创建一个 ServiceAccount 和 ClusterRoleBinding 对象,授予管理员权限。首先创建名为`admin-user-serviceaccount.yaml`的文件,并填充其中显示的内容,该内容如示例 2-8 所示。
##### 示例 2-8\. 管理权限的服务账户
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
然后,将示例 2-9 的内容存储到名为`admin-user-clusterrolebinding.yaml`的文件中,以将 ClusterRole 命名为`cluster-admin`映射到 ServiceAccount。
##### 示例 2-9\. 用于管理员权限的 ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
[*]kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
使用以下声明性命令创建这两个对象:
$ kubectl create -f admin-user-serviceaccount.yaml
serviceaccount/admin-user created
$ kubectl create -f admin-user-clusterrolebinding.yaml
clusterrolebinding.rbac.authorization.k8s.io/admin-user created
您现在可以使用以下命令为管理员用户创建承载令牌。该命令将为提供的 ServiceAccount 对象生成一个令牌,并在控制台上呈现它:
$ kubectl create token admin-user -n kubernetes-dashboard
eyJhbGciOiJSUzI1NiIsImtpZCI6…
# 服务账户令牌的过期时间
默认情况下,此令牌将在 24 小时后过期。这意味着一旦“生存时间”(TTL)过去,令牌对象将自动删除。您可以通过提供命令行选项 `--ttl` 来更改令牌的 TTL。例如,`40h` 的值将在 40 小时后使令牌过期。值 `0` 表示令牌永不过期。
将命令的输出复制并粘贴到登录屏幕的“输入令牌”字段中,如 Figure 2-5 所示。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0205.png>
###### 图 2-5\. 仪表板登录界面中令牌的使用
单击“登录”按钮将带您进入 Figure 2-6 中显示的仪表板。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0206.png>
###### 图 2-6\. 特定命名空间中 Pods 的仪表板视图
您现在可以管理端用户和集群对象而无需任何限制。
## 创建具有受限权限的用户
在上一节中,您学习了如何创建具有全局集群管理权限的用户。但是,仪表板的大多数用户可能只需要一组受限权限。例如,实施和操作云原生应用程序的开发人员可能只需要在 Kubernetes 集群上执行其任务所需的部分管理权限。为仪表板创建具有受限权限的用户包括以下三个步骤:
1.创建一个 ServiceAccount 对象。
1.创建定义权限的 ClusterRole 对象。
1.创建将 ClusterRole 映射到 ServiceAccount 的 ClusterRoleBinding。
如您所见,该流程与我们为管理员用户所经历的流程非常相似。第 2 步是新的,因为我们需要明确我们要授予哪些权限。接下来的 YAML 清单将模拟作为开发人员工作的用户,该用户仅允许只读权限(例如获取、列出和观察资源)。
首先创建文件 `restricted-user-serviceaccount.yaml`,并填入示例 Example 2-10 中显示的内容。
##### 示例 2-10\. 用于受限权限的 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: developer-user
namespace: kubernetes-dashboard
Example 2-11 中的 ClusterRole 仅允许获取、列出和观察资源。不允许执行其他任何操作。将内容存储在文件 `restricted-user-clusterrole.yaml` 中。
##### 示例 2-11\. 用于受限权限的 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: “true”
name: cluster-developer
rules:
[*]apiGroups:
[*]‘*’
resources:
[*]‘*’
verbs:
[*]get
[*]list
[*]watch
[*]nonResourceURLs:
[*]‘*’
verbs:
[*]get
[*]list
[*]watch
最后,将 ServiceAccount 映射到文件 `restricted-user-clusterrolebinding.yaml` 中的 ClusterRole,如 Example 2-12 所示。
##### 示例 2-12\. 用于受限权限的 ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: developer-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-developer
subjects:
[*]kind: ServiceAccount
name: developer-user
namespace: kubernetes-dashboard
使用以下声明性命令创建所有对象:
$ kubectl create -f restricted-user-serviceaccount.yaml
serviceaccount/restricted-user created
$ kubectl create -f restricted-user-clusterrole.yaml
clusterrole.rbac.authorization.k8s.io/cluster-developer created
$ kubectl create -f restricted-user-clusterrolebinding.yaml
clusterrolebinding.rbac.authorization.k8s.io/developer-user created
使用以下命令生成具有受限用户的 Bearer 令牌:
$ kubectl create token developer-user -n kubernetes-dashboard
eyJhbGciOiJSUzI1NiIsImtpZCI6…
GUI 中,对于当前登录用户不允许的操作将不会显示为禁用选项。你仍然可以选择该选项,但会显示错误消息。图 2-7 展示了如果尝试通过没有权限执行操作的用户删除 Pod 时,Dashboard 的行为。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0207.png>
###### 图 2-7\. 尝试调用允许操作时显示的错误消息
## 避免不安全的配置参数
在生产环境中保护 Dashboard 包括使用[执行参数](https://oreil.ly/gS1hE),用于正确配置身份验证和授权。默认情况下,启用登录功能,并且 HTTPS 端点将在 8443 端口上公开。如果不希望自动生成 TLS 证书,可以使用`--tls-cert-file`和`--tls-cert-key`命令行选项提供 TLS 证书。
避免设置命令行参数`--insecure-port`来公开 HTTP 端点和`--enable-insecure-login`来启用以 HTTP 而非 HTTPS 提供登录页面。此外,请确保*不要*使用`--enable-skip-login`选项,因为这将允许通过在登录屏幕上单击跳过按钮来规避认证方法。
# 验证 Kubernetes 平台的二进制文件
Kubernetes 项目会在每个发布中发布客户端和服务器二进制文件。客户端二进制文件指的是可执行的`kubectl`。服务器二进制文件包括`kubeadm`,以及 API 服务器、调度器和 kubelet 的可执行文件。你可以在(https://oreil.ly/vHpAV)或发布页面[*https://dl.k8s.io*](https://dl.k8s.io)找到这些文件。
## 场景:攻击者向二进制文件注入恶意代码
可执行文件`kubectl`和`kubeadm`对于与 Kubernetes 交互至关重要。`kubectl`允许你针对 API 服务器运行命令,例如管理对象。`kubeadm`在将集群节点从一个版本升级到另一个版本时是必需的。假设你正在[升级集群版本的过程中](https://oreil.ly/hTJ57),从 1.23 版本升级到 1.24 版本。作为过程的一部分,你将需要升级`kubeadm`二进制文件。官方升级文档详细说明了升级二进制文件所需的命令。
假设攻击者成功修改了版本为 1.24 的`kubeadm`可执行文件,并诱使你以为需要从放置恶意二进制文件的位置下载该二进制文件。正如图 2-8 所示,每次调用修改后的`kubeadm`可执行文件都会暴露你于运行恶意代码的风险。例如,你可能会向集群外的服务器发送凭据,这会为入侵你的 Kubernetes 环境打开新的途径。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0208.png>
###### 图 2-8\. 一个将恶意代码注入二进制文件的攻击者
## 验证二进制文件与哈希值匹配
您可以借助 MD5 或 SHA 等哈希码验证二进制文件的有效性。Kubernetes 为每个二进制文件发布 SHA256 哈希码。在首次使用之前,应对各个二进制文件进行哈希验证。如果生成的哈希码与您下载的哈希码不匹配,则表示二进制文件存在问题。可能是第三方修改了二进制文件,或者您未正确使用特定类型或版本的哈希码。
您可以从[*https://dl.k8s.io*](https://dl.k8s.io)下载与二进制文件对应的哈希码。哈希码的完整 URL 反映了二进制文件的版本、操作系统和架构信息。以下列表展示了适用于 Linux AMD64 平台二进制文件的示例 URL:
+ `kubectl`: [*https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubectl.sha256*](https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubectl.sha256)
+ `kubeadm`: [*https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubeadm.sha256*](https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubeadm.sha256)
+ `kubelet`: [*https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubelet.sha256*](https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubelet.sha256)
+ `kube-apiserver`: [*https://dl.k8s.io/v1.26.1/bin/linux/amd64/kube-apiserver.sha256*](https://dl.k8s.io/v1.26.1/bin/linux/amd64/kube-apiserver.sha256)
您需要使用特定于操作系统的哈希码验证工具来检查二进制文件的有效性。如果您的机器上还没有该工具,可能需要先安装它。下面的命令展示了在不同操作系统上使用工具的示例,详细说明请参考(https://oreil.ly/2FmVm):
+ Linux: `echo "$(cat kubectl.sha256) kubectl" | sha256sum --check`
+ MacOSX: `echo "$(cat kubectl.sha256) kubectl" | shasum -a 256 --check`
+ Windows with Powershell: `$($(CertUtil -hashfile .\kubectl.exe SHA256) -replace " ", "") -eq $(type .\kubectl.exe.sha256)`
以下命令演示如何下载版本为 1.26.1 的`kubeadm`二进制文件及其对应的 SHA256 哈希文件:
$ curl -LO “https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubeadm”
$ curl -LO “https://dl.k8s.io/v1.26.1/bin/linux/amd64/kubeadm.sha256”
验证工具`shasum`可用于确认校验和是否匹配:
$ echo “$(cat kubeadm.sha256) kubeadm” | shasum -a 256 --check
kubeadm: OK
前一个命令返回“OK”消息。二进制文件未被篡改。任何其他消息都可能表示在执行二进制文件时存在潜在的安全风险。
# 总结
“集群设置” 领域关注于设置 Kubernetes 集群相关的安全方面。即使您可能正在使用 `kubeadm` 从头开始创建集群,这并不意味着您一定在遵循最佳实践。使用 kube-bench 检测潜在的安全风险是一个很好的起点。逐个修复工具报告的问题。您还可以检查客户端和服务器二进制文件的校验和,以确保它们未被攻击者修改。一些组织使用仪表板来管理集群及其对象。确保仪表板的身份验证和授权限制访问的小部分利益相关者。
网络通信是一个重要的安全方面。默认情况下,Pod 之间的通信是不受限制的。仔细查看运行在 Kubernetes 内的应用架构。只允许符合架构要求的 Pod 之间的单向网络流量。拒绝所有其他网络流量。在将应用程序暴露到集群外部时,请确保已配置带有 TLS 终止的 Ingress 对象。这将确保数据双向加密,使攻击者无法观察客户端和 Kubernetes 集群之间发送的密码等敏感信息。
# 考试要点
理解网络策略的目的和影响
默认情况下,Pod 之间的通信是无限制的。使用最小特权原则实例化默认拒绝规则以限制 Pod 之间的网络流量。网络策略的 `spec.podSelector` 属性根据标签选择选择目标 Pod 应用规则。入站和出站规则定义了允许进出流量的 Pod、命名空间、IP 地址和端口。网络策略可以进行聚合。默认拒绝规则可能禁止入站和/或出站流量。可以使用更精细的定义打开这些规则的其他网络策略。
练习使用 kube-bench 检测集群组件的漏洞
Kubernetes CIS 基准是在生产 Kubernetes 环境中推荐的安全设置的最佳实践集合。您可以利用工具 kube-bench 自动化检测安全风险的过程。运行 kube-bench 生成的报告描述了修复检测到问题所需的详细补救措施。学习如何解释结果以及如何缓解问题。
知道如何配置具有 TLS 终止的 Ingress
可以通过暴露 HTTPS 端点来配置 Ingress 发送和接收加密数据。为此,您需要创建一个 TLS Secret 对象,并为其分配一个 TLS 证书和密钥。然后,可以通过 Ingress 使用属性 `spec.tls[]` 消费这个 Secret。
知道如何为安全访问配置 GUI 元素
GUI 元素,例如 Kubernetes 仪表盘,提供了管理对象的便捷方式。如果应用程序没有受到未经授权访问的保护,攻击者可能会对您的集群造成危害。在考试中,您需要知道如何为特定利益相关者正确设置 RBAC。此外,您还应该大致了解与安全相关的命令行参数。练习安装仪表盘的过程,学习如何调整其命令行参数,并了解为不同用户设置权限的影响。
知道如何检测修改过的平台二进制文件
可以根据其相应的哈希码验证平台二进制文件,如 `kubectl` 和 `kubeadm`。了解如何查找哈希文件以及如何使用验证工具来识别二进制文件是否已被篡改。
# 示例练习
这些练习的解决方案可以在 附录 中找到。
1.创建一个网络策略,禁止出口流量到集群外的任何域。网络策略适用于具有标签 `app=backend` 的 Pod,并且还允许 UDP 和 TCP 的端口 53 的出口流量到任何其他命名空间中的 Pod。
1.创建一个名为 `allowed` 的 Pod,在端口 80 上运行 `busybox:1.36.0` 镜像,并为其分配标签 `app=frontend`。对 `http://google.com` 进行 `curl` 调用。网络调用应该被允许,因为网络策略不适用于该 Pod。
1.创建另一个名为 `denied` 的 Pod,在端口 80 上运行 `busybox:1.36.0` 镜像,并为其分配标签 `app=backend`。对 `http://google.com` 进行 `curl` 调用。网络调用应该被阻止。
1.安装 Kubernetes 仪表盘或确保已经安装。在命名空间 `kubernetes-dashboard` 中,创建一个名为 `observer-user` 的 ServiceAccount。此外,创建相应的 ClusterRole 和 ClusterRoleBinding。ServiceAccount 应只被允许查看部署(Deployments)。所有其他操作应被拒绝。例如,使用以下命令在 `default` 命名空间中创建名为 `deploy` 的部署:`kubectl create deployment deploy --image=nginx --replicas=3`。
1.为名为 `observer-user` 的 ServiceAccount 创建一个永不过期的令牌。使用该令牌登录仪表盘。确保仅可以查看部署(Deployments),而不能查看任何其他类型的资源。
1.在 Linux AMD64 上下载 API 服务器版本 1.26.1 的二进制文件。下载 API 服务器可执行文件版本 1.23.1 的 SH256 校验和文件。运行特定于操作系统的验证工具,并观察结果。
# 第三章:集群加固
“集群加固”领域涉及一些在集群初始化设置和配置后保持尽可能安全的重要主题。作为本章讨论的一部分,您可能会注意到,我将引用通常由 Kubernetes 管理员负责的概念和实践。在适当的情况下,我将提供链接到已涵盖 CKA 考试的主题。
在高层次上,本章涵盖以下概念:
+ 限制对 Kubernetes API 的访问
+ 配置基于角色的访问控制(RBAC)以最小化暴露
+ 在使用服务账户时要谨慎
+ 经常更新 Kubernetes
# 与 Kubernetes API 交互
API 服务器是访问 Kubernetes 集群的网关。任何人类用户、客户端(例如`kubectl`)、集群组件或服务账户都将通过 HTTPS 进行 RESTful API 调用访问 API 服务器。这是执行操作(如创建 Pod 或删除 Service)的*中心点*。
在本节中,我们将专注于与 API 服务器相关的安全特定方面。关于 API 服务器的内部工作方式以及 Kubernetes API 的使用详细讨论,请参考 Brendan Burns 和 Craig Tracey(O'Reilly)的书籍[*管理 Kubernetes*](https://learning.oreilly.com/library/view/managing-kubernetes/9781492033905)。
## 处理请求
图 3-1 说明了向 API 服务器发出调用时请求经历的阶段。有关更多信息,请参阅(https://oreil.ly/DuLdf)。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0301.png>
###### 图 3-1\. API 服务器请求处理
请求处理的第一个阶段是*身份验证*。通过检查客户端证书或令牌验证来验证调用者的身份。如果令牌与服务账户关联,则将在此处验证。
第二阶段确定了第一阶段提供的身份是否可以访问动词和 HTTP 路径请求。因此,第二阶段处理请求的*授权*,使用标准的 Kubernetes RBAC 模型实现。在这里,我们要确保服务账户被允许列出 Pods 或者根据请求创建新的 Service 对象。
请求处理的第三阶段涉及*准入控制*。准入控制验证请求是否格式正确,并在处理请求之前可能需要进行修改。例如,准入控制策略可以确保创建 Pod 的请求包含特定标签的定义。如果没有定义该标签,则请求将被拒绝。
最后一个阶段确保了请求中包含的资源是有效的。请求*验证*可以作为准入控制的一部分来实现,但不是必须的。例如,这个阶段确保了服务对象的名称遵循提供的 DNS 名称的标准 Kubernetes 命名规则。
## 连接到 API 服务器
运行以下命令很容易确定 API 服务器的端点:
$ kubectl cluster-info
Kubernetes control plane is running at https://172.28.40.5:6443
…
对于给定的 Kubernetes 集群,API 服务器已通过 URL [*https://172.28.40.5:6443*](https://172.28.40.5:6443) 暴露。此外,您还可以查看 API 服务器配置文件中的命令行选项 `--advertise-address` 和 `--secure-port` 来确定端点。您可以在 `/etc/kubernetes/manifests/kube-apiserver.yaml` 找到 API 服务器配置文件。
# 配置 API 服务器的不安全端口
可以配置 API 服务器使用不安全端口(例如,80)已在 Kubernetes 1.10 版本中弃用。在版本 1.24 中,不安全端口标志 `--port` 和 `--insecure-port` 已完全删除,因此不能再用于配置 API 服务器。有关更多信息,请参阅[发布说明](https://oreil.ly/OTsmV)。
### 使用 Kubernetes 服务
Kubernetes 使得特定用例更方便访问 API 服务器。例如,您可能希望从 Pod 发送请求到 Kubernetes API。而不是使用 API 服务器的 IP 地址和端口,您可以简单地引用名为 `kubernetes.default.svc` 的服务。这个特殊的服务位于 `default` 命名空间中,并由集群自动启动。删除服务将自动重新创建它。您可以通过以下命令轻松找到该服务:
$ kubectl get service kubernetes
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 32s
检查此服务的端点时,您将看到它指向 API 服务器的 IP 地址和端口,如通过执行以下命令所示:
$ kubectl get endpoints kubernetes
NAME ENDPOINTS AGE
kubernetes 172.28.40.5:6443 4m3s
服务的 IP 地址和端口也通过环境变量暴露给 Pod。您可以从容器内运行的程序中读取环境变量的值。服务的 IP 地址由环境变量 `KUBERNETES_SERVICE_HOST` 反映。端口可以使用环境变量 `KUBERNETES_SERVICE_PORT` 访问。要渲染环境,请简单地使用 `env` 命令在临时 Pod 中访问环境变量:
$ kubectl run kubernetes-envs --image=alpine:3.16.2 -it --rm --restart=Never
– env
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
我们将在 “最小化服务账户权限” 部分中使用 `kubernetes` 服务。
### 匿名访问
以下命令使用 `curl` 命令行工具匿名调用 API 列出所有命名空间。选项 `-k` 避免验证服务器的 TLS 证书:
$ curl https://172.28.40.5:6443/api/v1/namespaces -k
{
“kind”: “Status”,
“apiVersion”: “v1”,
“metadata”: {},
“status”: “Failure”,
“message”: “namespaces is forbidden: User “system:anonymous” cannot list
resource “namespaces” in API group “” at the cluster scope”,
“reason”: “Forbidden”,
“details”: {
“kind”: “namespaces”
},
“code”: 403
}
如你从 JSON 格式的 HTTP 响应体中看到的那样,API 服务器接受匿名调用,但没有适当的操作权限。在内部,Kubernetes 将调用映射到[用户名 `system:anonymous`](https://oreil.ly/_HrbF),这实际上没有授权执行该操作。
### 使用客户端证书访问
要作为授权用户发出请求,您需要创建一个新用户或使用具有管理员权限的现有默认用户 `kubernetes-admin`。我们现在不会详细介绍创建新用户的过程。有关创建用户的更多信息,请参阅“限制用户权限”。
以下命令列出了所有可用用户,包括其客户端证书和密钥:
$ kubectl config view --raw
apiVersion: v1
clusters:
[*]cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tL… https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/1.png
server: https://172.28.132.5:6443
name: kubernetes
contexts:
[*]context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
[*]name: kubernetes-admin https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/2.png
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tL… https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/3.png
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktL… https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/4.png
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_hardening_CO1-1>
证书颁发机构的 base64 编码值
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_hardening_CO1-2>
默认创建的具有管理员权限的用户条目
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_hardening_CO1-3>
用户客户端证书的 base64 编码值
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_cluster_hardening_CO1-4>
用户私钥的 base64 编码值
要使用用户 `kubernetes-admin` 进行调用,我们需要将 CA、客户端证书和私钥的 base64 编码值提取到文件中作为 base64 解码值。以下命令将复制 base64 编码值,并使用工具 `base64` 对其进行解码,然后写入文件。CA 值将存储在文件 `ca` 中,客户端证书值将存储在 `kubernetes-admin.crt` 中,私钥将存储在 `kubernetes-admin.key` 中:
$ echo LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tL… | base64 -d > ca
$ echo LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tL… | base64 -d > kubernetes-admin.crt
$ echo LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktL… | base64 - \
kubernetes-admin.key
现在,您可以使用相关命令行选项将 `curl` 命令指向这些文件。向 API 服务器的请求应正确进行身份验证,并返回所有现有的命名空间,因为 `kubernetes-admin` 具有适当的权限:
$ curl --cacert ca --cert kubernetes-admin.crt --key kubernetes-admin.key
https://172.28.132.5:6443/api/v1/namespaces
{
“kind”: “NamespaceList”,
“apiVersion”: “v1”,
“metadata”: {
“resourceVersion”: “2387”
},
“items”: [
…
]
}
# 限制访问 API 服务器
如果您正在将 API 服务器暴露到互联网,请问您是否有必要这样做。一些云提供商提供创建私有集群的选项,这将限制或完全禁用对 API 服务器的公共访问。有关更多信息,请参阅(https://oreil.ly/W4Oma)和(https://oreil.ly/c7G-g)的文档页面。
如果您正在运营本地 Kubernetes 集群,您将需要实例化防火墙规则以阻止对 API 服务器的访问。设置防火墙规则超出了考试范围,因此本书不会讨论此内容。
## 场景:攻击者可以通过互联网调用 API 服务器
云服务提供商有时会将 API 服务器暴露在互联网上,以简化管理访问。攻击者可以尝试通过拒绝提供客户端证书或令牌来向 API 服务器端点发起匿名请求。如果攻击者幸运地捕获到用户凭据,那么可以执行经过身份验证的调用。根据分配给用户的权限,可以执行恶意操作。图 3-2 说明了一个攻击者从互联网调用 API 服务器的情况。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0302.png>
###### 图 3-2\. 一个攻击者从互联网调用 API 服务器
在本章中,我们将看一下如何限制对 API 服务器的访问,并通过示例实现具有有限权限的 RBAC。“理解 Open Policy Agent(OPA)和 Gatekeeper”将通过 OPA Gateway 帮助审计控制。
## 限制用户权限
我们已经看到我们可以使用`kubernetes-admin`用户的凭据来调用 Kubernetes API。这个用户应该非常节俭地使用,也不应该与很多人共享凭据。如果凭据落入错误的手中,可能会造成很大的损害。将这个用户专门保留给负责集群管理的人员使用。
对于您的 Kubernetes 集群的其他利益相关者,您应该设置一个具有有限权限集的专用用户。您可能在组织中有特定的角色可以映射到。例如,您可能有一个开发者角色,应该被允许管理部署、Pod、ConfigMaps、Secrets 和 Services,但不允许其他操作。要创建一个新用户并分配相关的 RBAC 权限,请参考(https://oreil.ly/n8EMD)。简而言之,有四个步骤:
1.创建一个私钥。
1.创建并批准一个 CertificateSigningRequest。
1.创建一个 Role 和一个 RoleBinding。
1.将用户添加到(https://oreil.ly/OKs9g)中。
我们将详细介绍这个过程,但会在“最小化服务账户权限”中更详细地讨论 RBAC 概念。
### 创建一个私钥
使用`openssl`可执行文件创建一个私钥。提供一个有意义的文件名,比如`<username>.key`:
$ openssl genrsa -out johndoe.key 2048
Generating RSA private key, 2048 bit long modulus
…+
…+
e is 65537 (0x10001)
在带有扩展名`.csr`的文件中创建一个证书签名请求(CSR)。您需要提供上一步的私钥。当要求输入“通用名称”值时,以下命令使用用户名`johndoe`。所有其他输入请求都是可选的,可以根据需要填写:
$ openssl req -new -key johndoe.key -out johndoe.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.’, the field will be left blank.
Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:johndoe
Email Address []:
Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []:
使用以下命令检索 CSR 文件内容的 base64 编码值。在下一步创建 CertificateSigningRequest 对象时会需要它:
$ cat johndoe.csr | base64 | tr -d “\n”
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tL…
### 创建和批准一个 CertificateSigningRequest
以下脚本创建一个 CertificateSigningRequest 对象。(https://oreil.ly/ltFbE) 用于请求由指定签名者签名的证书:
$ cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: johndoe
spec:
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tL…
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 86400
usages:
[*]client auth
EOF
certificatesigningrequest.certificates.k8s.io/johndoe created
属性 `spec.signerName` 的值 `kubernetes.io/kube-apiserver-client` 签署证书,这些证书将被 API 服务器作为客户端证书接受。使用上一步的 Base64 编码值并将其分配给属性 `spec.request` 的值。最后,可选属性 `spec.expirationSeconds` 确定证书的生存期。分配的值 `86400` 使证书有效期为一天。根据你希望证书持续的时间长短增加或减少到期时间,或者简单地不添加这个属性。
创建 CertificateSigningRequest 对象后,条件将显示为“待处理”。你需要在 24 小时内批准签名请求,否则对象将被自动删除,作为清理集群中不必要对象的手段。
$ kubectl get csr johndoe
NAME AGE SIGNERNAME REQUESTOR
REQUESTEDDURATION CONDITION
johndoe 6s kubernetes.io/kube-apiserver-client minikube-user
24h Pending
使用 `certificate approve` 命令批准签名请求。结果,条件变更为“已批准,已发放”:
$ kubectl certificate approve johndoe
certificatesigningrequest.certificates.k8s.io/johndoe approved
$ kubectl get csr johndoe
NAME AGE SIGNERNAME REQUESTOR
REQUESTEDDURATION CONDITION
johndoe 17s kubernetes.io/kube-apiserver-client minikube-user
24h Approved,Issued
最后,导出从已批准的 CertificateSigningRequest 对象颁发的证书:
$ kubectl get csr johndoe -o jsonpath={.status.certificate}| base64
-d > johndoe.crt
### 创建一个角色和一个角色绑定
是时候分配 RBAC 权限了。在这一步,你将为用户创建一个角色和一个角色绑定。角色模拟了组织内的“应用开发者”角色。开发者只能允许获取、列出、更新和删除 Pod。以下的命令创建了角色对象:
$ kubectl create role developer --verb=create --verb=get --verb=list
–verb=update --verb=delete --resource=pods
role.rbac.authorization.k8s.io/developer created
接下来,我们将把角色绑定到名为 `johndoe` 的用户上。使用命令 `create rolebinding` 来完成这个操作:
$ kubectl create rolebinding developer-binding-johndoe --role=developer
–user=johndoe
rolebinding.rbac.authorization.k8s.io/developer-binding-johndoe created
### 将用户添加到 kubeconfig 文件中
在最后一步,你需要将用户添加到 kubeconfig 文件中,并为其创建一个用户上下文。请注意,在下面的命令中集群名称是 `minikube`,因为我们正在 minikube 安装中尝试这个操作:
$ kubectl config set-credentials johndoe --client-key=johndoe.key
–client-certificate=johndoe.crt --embed-certs=true
User “johndoe” set.
$ kubectl config set-context johndoe --cluster=minikube --user=johndoe
Context “johndoe” created.
### 验证权限
是时候切换到名为`johndoe`的用户上下文了:
$ kubectl config use-context johndoe
Switched to context “johndoe”.
使用 `kubectl` 作为客户端向 API 服务器发出调用,我们将验证这个操作是否被允许。用于在 `default` 命名空间中列出所有 Pod 的 API 调用已经被验证和授权:
$ kubectl get pods
No resources found in default namespace.
命令的输出指示当前`default`命名空间在这个时间点上不包含任何 Pod 对象,但是调用是成功的。我们还将测试负面案例。列出命名空间对于用户来说是一个不允许的操作。执行相关的 `kubectl` 命令将返回一个错误消息:
$ kubectl get namespaces
Error from server (Forbidden): namespaces is forbidden: User “johndoe” cannot
list resource “namespaces” in API group “” at the cluster scope
当你完成权限验证后,你可能想要切换回拥有管理员权限的上下文:
$ kubectl config use-context minikube
Switched to context “minikube”.
## 情景:攻击者可以通过服务账号调用 API 服务器
用户代表经常使用 `kubectl` 可执行文件或 UI 仪表板与 Kubernetes 集群进行交互的真实人员。在某些罕见条件下,运行在 Pod 容器内的应用程序需要与 Kubernetes API 进行交互。这种需求的典型示例是包管理器(https://helm.sh)。Helm 根据捆绑在 Helm 图表中的 YAML 清单管理 Kubernetes 资源。Kubernetes 使用服务账户通过身份验证令牌将 Helm 服务进程与 API 服务器进行身份验证。可以将此服务账户分配给 Pod,并映射到 RBAC 规则。
如果攻击者能够访问 Pod,很可能也能够滥用服务账户调用 Kubernetes API,如图 3-3 所示。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0303.png>
###### 图 3-3\. 攻击者使用服务账户调用 API 服务器。
## 最小化服务账户的权限。
重要的是只限制那些对应用程序真正必要的服务账户的权限。接下来的章节将解释如何实现这一点,以最小化潜在的攻击面。
要使这种场景正常工作,您需要创建一个 ServiceAccount 对象,并将其分配给 Pod。服务账户可以与 RBAC 绑定,并分配角色和角色绑定以定义它们应该允许执行的操作。
### 将服务账户绑定到 Pod。
作为起点,我们将设置一个 Pod,通过调用 Kubernetes API 列出命名空间 `k97` 中所有 Pods 和 Deployments。该调用作为每十秒无限循环的一部分进行。来自 API 调用的响应将写入标准输出,可通过 Pod 的日志访问。
为了对 API 服务器进行身份验证,我们将发送与 Pod 使用的服务账户相关联的令牌。服务账户的默认行为是自动挂载 API 凭据到路径 `/var/run/secrets/kubernetes.io/serviceaccount/token`。我们将使用 `cat` 命令行工具简单地获取文件内容,并将其作为 HTTP 请求的标头一并发送。示例 3-1 定义了命名空间、服务账户和 Pod,在一个名为 `setup.yaml` 的 YAML 文件中。
##### 示例 3-1\. 为将服务账户分配给 Pod 的 YAML 文件清单。
apiVersion: v1
kind: Namespace
metadata:
name: k97
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-api
namespace: k97
apiVersion: v1
kind: Pod
metadata:
name: list-objects
namespace: k97
spec:
serviceAccountName: sa-api
containers:
[*]name: pods
image: alpine/curl:3.14
command: [‘sh’, ‘-c’, ‘while true; do curl -s -k -m 5 -H
“Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/
serviceaccount/token)” https://kubernetes.default.svc.cluster.
local/api/v1/namespaces/k97/pods; sleep 10; done’]
[*]name: deployments
image: alpine/curl:3.14
command: [‘sh’, ‘-c’, ‘while true; do curl -s -k -m 5 -H
“Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/
serviceaccount/token)” https://kubernetes.default.svc.cluster.
local/apis/apps/v1/namespaces/k97/deployments; sleep 10; done’]
使用以下命令从 YAML 文件创建对象:
$ kubectl apply -f setup.yaml
namespace/k97 created
serviceaccount/sa-api created
pod/list-objects created
### 验证默认权限。
名为 `list-objects` 的 Pod 在专用容器中调用 API 服务器以检索 Pods 和 Deployments 的列表。容器 `pods` 执行调用以列出 Pods。容器 `deployments` 向 API 服务器发送请求以列出 Deployments。
如 (https://oreil.ly/gBp30) 所述,默认的 RBAC 策略不会授予 `kube-system` 命名空间之外的服务帐户任何权限。容器 `pods` 和 `deployments` 的日志返回错误消息,指示服务帐户 `sa-api` 未被授权列出资源:
$ kubectl logs list-objects -c pods -n k97
{
“kind”: “Status”,
“apiVersion”: “v1”,
“metadata”: {},
“status”: “Failure”,
“message”: "pods is forbidden: User “system:serviceaccount:k97:sa-api”
cannot list resource “pods” in API group “” in the
namespace “k97"”,
“reason”: “Forbidden”,
“details”: {
“kind”: “pods”
},
“code”: 403
}
$ kubectl logs list-objects -c deployments -n k97
{
“kind”: “Status”,
“apiVersion”: “v1”,
“metadata”: {},
“status”: “Failure”,
“message”: "deployments.apps is forbidden: User
“system:serviceaccount:k97:sa-api” cannot list resource
“deployments” in API group “apps” in the namespace
“k97"”,
“reason”: “Forbidden”,
“details”: {
“group”: “apps”,
“kind”: “deployments”
},
“code”: 403
}
接下来,我们将创建一个 ClusterRole 和 RoleBinding 对象,并赋予执行所需 API 调用的权限。
### 创建 ClusterRole
首先,定义名为 `list-pods-clusterrole` 的 ClusterRole,如 示例 3-2 中所示,在文件 `clusterrole.yaml` 中。规则集仅添加 Pod 资源和动词 `list`。
##### 示例 3-2\. 允许列出 Pods 的 ClusterRole 的 YAML 清单
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: list-pods-clusterrole
rules:
[*]apiGroups: [“”]
resources: [“pods”]
verbs: [“list”]
通过指向相应的 YAML 清单文件来创建对象:
$ kubectl apply -f clusterrole.yaml
clusterrole.rbac.authorization.k8s.io/list-pods-clusterrole created
### 创建 RoleBinding
示例 3-3 在 `rolebinding.yaml` 文件中定义了 RoleBinding 的 YAML 清单。RoleBinding 将 ClusterRole `list-pods-clusterrole` 映射到名为 `sa-pod-api` 的服务帐户,并且仅适用于命名空间 `k97`。
##### 示例 3-3\. 附加到服务帐户的 RoleBinding 的 YAML 清单
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: serviceaccount-pod-rolebinding
namespace: k97
subjects:
[*]kind: ServiceAccount
name: sa-api
roleRef:
kind: ClusterRole
name: list-pods-clusterrole
apiGroup: rbac.authorization.k8s.io
使用 `apply` 命令创建 RoleBinding 对象:
$ kubectl apply -f rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/serviceaccount-pod-rolebinding created
### 验证授予的权限
通过授予 `list` 权限,服务帐户现在可以正确检索 `k97` 命名空间中的所有 Pods。`pods` 容器中的 `curl` 命令成功执行,如下所示:
$ kubectl logs list-objects -c pods -n k97
{
“kind”: “PodList”,
“apiVersion”: “v1”,
“metadata”: {
“resourceVersion”: “628”
},
“items”: [
{
“metadata”: {
“name”: “list-objects”,
“namespace”: “k97”,
…
}
]
}
我们没有授予服务帐户其他资源的任何权限。在 `k97` 命名空间中列出 Deployments 仍然失败。以下输出显示了在 `deployments` 命名空间中使用 `curl` 命令的响应:
$ kubectl logs list-objects -c deployments -n k97
{
“kind”: “Status”,
“apiVersion”: “v1”,
“metadata”: {},
“status”: “Failure”,
“message”: "deployments.apps is forbidden: User
“system:serviceaccount:k97:sa-api” cannot list resource
“deployments” in API group “apps” in the namespace
“k97"”,
“reason”: “Forbidden”,
“details”: {
“group”: “apps”,
“kind”: “deployments”
},
“code”: 403
}
随意修改 ClusterRole 对象,以允许列出 Deployment 对象。
### 禁用服务帐户令牌的自动装载
前一节中描述的 Pod 使用服务帐户的令牌作为针对 API 服务器进行身份验证的手段。将令牌文件挂载到 `/var/run/secrets/kubernetes.io/serviceaccount/token` 是每个服务帐户的标准行为。只有当 Pod 实际与 Kubernetes API 交互时,您才真正需要文件的内容。在所有其他情况下,此行为可能构成潜在的安全风险,因为访问 Pod 将直接将攻击者引导至令牌。
您可以通过将属性 `automountServiceAccountToken` 的值设置为 `false` 来禁用服务帐户对象的自动装载行为,如 示例 3-4 所示。
##### 示例 3-4\. 选择退出服务帐户的令牌自动装载行为
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-api
namespace: k97
automountServiceAccountToken: false
如果要为单个 Pod 禁用自动装载行为,请在 Pod 定义中使用 `spec.automountServiceAccountToken` 属性。示例 3-5 展示了一个 Pod 的 YAML 清单。
##### 示例 3-5\. 在 Pod 中禁用服务帐户的令牌自动装载
apiVersion: v1
kind: Pod
metadata:
name: list-objects
namespace: k97
spec:
serviceAccountName: sa-api
automountServiceAccountToken: false
…
### 生成服务账户令牌
有多种用例表明希望创建一个禁用令牌自动挂载的服务账户。例如,您可能需要从外部工具或连续交付流水线访问 Kubernetes API,以查询现有对象的信息。在这些场景中,仍需要使用令牌对 API 服务器进行身份验证。所列场景不一定运行分配了服务账户的 Pod,而只是从诸如`curl`之类的工具执行 RESTful API 调用。
要手动创建令牌,请执行`create token`命令,并将服务账户的名称作为参数提供。该命令的输出呈现令牌:
$ kubectl create token sa-api
eyJhbGciOiJSUzI1NiIsImtpZCI6IjBtQkJzVWlsQjl…
您需要将令牌存储在安全的位置,例如密码管理器中。如果丢失令牌,将无法重新获取。您只能使用相同的命令重新创建令牌,这将自动使先前的令牌失效。所有使用该令牌的引用都必须更改。
对于自动化流程,使用有限生命周期生成令牌可能很有帮助。`--duration`将在“生命周期”结束后自动使令牌失效:
$ kubectl create token sa-api --duration 10m
eyJhbGciOiJSUzI1NiIsImtpZCI6IjBtQkJzVWlsQjl…
### 创建服务账户的密钥
在 Kubernetes 1.24 中,ServiceAccount 对象不再自动创建包含令牌的对应 Secret 对象。有关更多信息,请参阅 [发行说明](https://oreil.ly/MSPuX)。列出 ServiceAccount 对象会呈现 0 个密钥的数量。该对象在 YAML 表示中也不再包含`secrets`属性:
$ kubectl get serviceaccount sa-api -n k97
NAME SECRETS AGE
sa-api 0 42m
您可以使用`create token`命令生成令牌,如 “生成服务账户令牌”中所述,或手动创建相应的密钥。示例 3-6 显示了这种密钥的 YAML 清单。
##### 示例 3-6\. 手动创建服务账户的密钥
apiVersion: v1
kind: Secret
metadata:
name: sa-api-secret
namespace: k97
annotations:
kubernetes.io/service-account.name: sa-api
type: kubernetes.io/service-account-token
要为服务账户分配密钥,需添加带有键`kubernetes.io/service-account.name`的注释。以下命令创建密钥对象:
$ kubectl create -f secret.yaml
secret/sa-api-secret created
描述 Secret 对象时,您可以在“数据”部分找到令牌:
$ kubectl describe secret sa-api-secret -n k97
…
Data
ca.crt: 1111 bytes
namespace: 3 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjBtQkJzVWlsQjl…
# 频繁更新 Kubernetes
安装具有特定版本的 Kubernetes 集群并非一劳永逸的操作。即使在安装时使用了最新的长期支持 (LTS) 版本,也不能保证您的集群没有安全漏洞。
随着时间的推移,将发现与安全相关的漏洞和弱点。此声明包括底层操作系统以及集群节点运行的依赖项。攻击者可以轻易在公开披露的 [通用漏洞和暴露 (CVE) 数据库](https://oreil.ly/FHhXD) 中查找安全漏洞并利用它们。
## 版本控制方案
将 Kubernetes 版本定期更新到所有节点上,这由集群管理员负责。Kubernetes 遵循[语义化版本控制方案](https://semver.org)。语义化版本包括主版本号、次版本号和补丁版本号。例如,对于 Kubernetes 版本 1.24.3,主版本号是 1,次版本号是 24,补丁版本号是 3。
每个版本号的部分都有特定的含义。主版本号的变更表示有不兼容的改动。递增次版本号表示以向后兼容的方式新增了功能。补丁版本号仅修复了一个 bug。
# Kubernetes 中通过次版本更新引入的不兼容变更
需要指出的是,Kubernetes 并不总是严格遵循语义化版本控制的解释。例如,PodSecurityPolicy(PSP)准入控制器已经在[版本 1.25.0](https://oreil.ly/JE-i8)中被 Pod Security Admission 概念取代。按照传统,这些更改只应该发生在主版本更新时。参考(https://oreil.ly/on9lu)以更好地理解 API、标志或功能如何逐步淘汰。
## 发布节奏
您可以预期每三个月发布[一个新的次版本](https://oreil.ly/LGIn5)的 Kubernetes。新发布版本可能包含新功能和额外的 bug 修复。必要时,安全修复也会被实施到最新版本的 Kubernetes,并将回溯到之前的两个次版本。始终保持更新到您的集群的最新版本需要相当多的人力投入。因此,您需要相应地预留时间来执行这些活动。
## 执行升级过程
建议从一个次版本升级到更高的次版本(例如,从 1.23 到 1.24),或者从一个补丁版本升级到更近期的一个(例如,从 1.24.1 到 1.24.3)。避免跨越多个次版本进行升级,以避免意外的副作用。
您可以在官方 Kubernetes 文档中找到关于[升级步骤](https://oreil.ly/RxC9j)的完整描述。图 3-4 概述了高层次的升级过程。
集群版本升级过程已经是 CKA 考试的一部分。考虑到您必须通过 CKA 作为先决条件,我假设您已经知道如何执行这个过程。有关详细描述,请参考[*Certified Kubernetes Administrator (CKA) Study Guide*](https://oreil.ly/cka-study-guide)。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0304.png>
###### 图 3-4\. 集群版本升级流程
# 概述
用户、客户端应用程序(例如`kubectl`或`curl`)、使用服务账户的 Pod,以及集群组件都通过 API 服务器与其通信,以管理对象。确保安全地保护 API 服务器,以防止恶意访问,这至关重要。
为了最小化攻击面积,避免使用防火墙规则将 API 服务器暴露给互联网。对于每个用户或服务账户,使用 RBAC 规则将权限限制到最低限度,以执行对 Kubernetes API 的操作。权限最小化可以在攻击者获得凭据访问时造成更少的损害。
确保升级您的 Kubernetes 集群版本。集成错误和安全修复将减少暴露给攻击者利用的不必要漏洞的风险。
# 考试要点
练习与 Kubernetes API 交互。
本章演示了与 Kubernetes API 交互的不同方式。我们通过切换到用户上下文执行 API 请求,并借助使用`curl`进行 RESTful API 调用。您需要了解如何确定 API 服务器的端点以及如何使用不同的身份验证方法,例如客户端凭据和 Bearer 令牌。探索 Kubernetes API 及其端点,以便获得更广泛的暴露。
理解为用户和服务账户定义 RBAC 规则的影响。
匿名用户对 Kubernetes API 的请求不会允许任何实质性操作。对于来自用户或服务账户的请求,您需要仔细分析授予主体的权限。通过创建相关对象来控制权限,了解定义 RBAC 规则的方方面面。服务账户在 Pod 中使用时会自动挂载令牌。如果打算从 Pod 中进行 API 调用,则仅暴露令牌作为卷。
注意 Kubernetes 的发布节奏和升级集群的必要性。
出于安全原因,Kubernetes 集群需要随着时间的推移进行维护。攻击者可能会利用已知的过时 Kubernetes 版本中的漏洞。版本升级过程是每个管理员的职责的一部分,不应忽视。
# 示例练习
这些练习的解答在附录中提供。
1.为名为`jill`的用户在`observer`组中创建客户端证书和密钥。使用管理员上下文为用户`jill`创建上下文。
1.对于组(而不是用户!),在`default`名称空间中定义一个允许资源 Pods、ConfigMaps 和 Secrets 的动词`get`、`list`和`watch`的角色和角色绑定。创建这些对象。
1.切换到用户上下文,并执行允许已授权操作的`kubectl`命令,以及一个不应允许的`kubectl`命令。切换回`admin`上下文。
1.在名称空间`t23`中创建一个名为`service-list`的 Pod。该容器使用`alpine/curl:3.14`镜像,并在无限循环中向 Kubernetes API 发出一个列出`default`名称空间中 Service 对象的`curl`调用。创建并附加服务账户`api-call`。在 Pod 启动后检查容器日志。您期望从`curl`命令中看到什么响应?
1.为服务账号分配 ClusterRole 和 RoleBinding,仅允许 Pod 所需的操作。查看 `curl` 命令的响应。
1.配置 Pod 以禁用自动挂载服务账号令牌。检索令牌值并直接在 `curl` 命令中使用。确保 `curl` 命令仍然能够授权操作。
1.导航到 GitHub 仓库 [*bmuschko/cks-study-guide*](https://oreil.ly/sImXZ) 的 *app-a/ch03/upgrade-version* 目录。使用命令 `vagrant up` 启动运行集群的虚拟机。将集群中所有节点从 Kubernetes 1.25.6 升级到 1.26.1\. 集群包括名为 `kube-control-plane` 的单一控制平面节点和名为 `kube-worker-1` 的工作节点。完成后,使用 `vagrant destroy -f` 关闭集群。
*先决条件:* 这个练习需要安装工具 (https://oreil.ly/FiyeH) 和 (https://oreil.ly/WW8IK)。
# 第四章:系统加固
“系统加固”领域涉及到运行 Kubernetes 集群节点的底层主机系统的安全相关方面。本章讨论的主题涉及基本的 Linux 核心功能,包括禁用服务和删除软件包、管理用户和组、禁用端口以及设置防火墙规则。最后,本章讨论了可以限制容器中运行的进程在主机级别上执行操作的 Linux 内核加固工具。
从高层次上看,本章涵盖以下概念:
+ 减少主机操作系统的占用
+ 减少 IAM 角色
+ 减少对网络的外部访问
+ 使用像 AppArmor 和 seccomp 这样的内核加固工具
# 减少主机操作系统的占用
集群节点运行在物理机或虚拟机上。在大多数情况下,这些机器上的操作系统是 Linux 发行版。显然,操作系统可能会暴露安全漏洞。
随着时间的推移,您需要将操作系统的版本保持最新,以包含最新的安全修复。例如,这个过程可能涉及将节点的操作系统从 Ubuntu 18 升级到 22。本书不涵盖操作系统的升级;有关更多信息,请参阅相关的 Linux 文档。
许多 Linux 发行版,如 Ubuntu,自带额外的工具、应用程序和服务,并非操作 Kubernetes 集群所必需。作为管理员,您的任务是识别安全风险,禁用或移除可能暴露漏洞的任何操作系统特定功能,并保持操作系统补丁,以包含最新的安全修复。操作系统的功能越少,风险就越小。
# Ubuntu Linux 的 CIS 基准
作为参考指南,您可以将操作系统的配置与 (https://oreil.ly/AeAAE) 进行比较。
## 场景:攻击者利用软件包漏洞
图 4-1 描述了攻击者如何利用系统上安装的软件包的漏洞。例如,该应用程序可能是软件包管理器 (https://oreil.ly/ZOFTj)。假设攻击者利用已知的漏洞 (https://oreil.ly/lw_MV),可能会向攻击者公开敏感信息。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0401.png>
###### 图 4-1\. 攻击者利用操作系统级别的漏洞
下一节将解释如何通过简单禁用或移除不真正需要的服务和软件包,来减少操作 Kubernetes 所需的服务和软件包的安全风险。
## 禁用服务
在 Linux 上,许多应用程序作为后台服务运行。可以使用命令行工具 `systemctl` 管理服务。以下 `systemctl` 命令列出所有正在运行的服务:
$ systemctl | grep running
…
snapd.service loaded active running Snap Daemon
在操作群集节点时我们不需要的一个服务是软件包管理器 snapd。 有关该服务的更多详细信息,请使用`status`子命令检索其状态:
$ systemctl status snapd
● snapd.service - Snap Daemon
Loaded: loaded (/lib/systemd/system/snapd.service; enabled; vendor
preset: enabled)
Active: active (running) since Mon 2022-09-19 22:49:56 UTC; 30min ago
TriggeredBy: ● snapd.socket
Main PID: 704 (snapd)
Tasks: 12 (limit: 2339)
Memory: 45.9M
CGroup: /system.slice/snapd.service
└─704 /usr/lib/snapd/snapd
您可以使用`systemctl`子命令`stop`停止服务:
$ sudo systemctl stop snapd
Warning: Stopping snapd.service, but it can still be activated by:
snapd.socket
执行`disable`子命令以防止系统重新启动时再次启动服务:
$ sudo systemctl disable snapd
Removed /etc/systemd/system/multi-user.target.wants/snapd.service.
服务现已停止并已禁用:
$ systemctl status snapd
● snapd.service - Snap Daemon
Loaded: loaded (/lib/systemd/system/snapd.service; disabled; vendor
preset: enabled)
Active: inactive (dead) since Mon 2022-09-19 23:22:22 UTC; 4min 4s ago
TriggeredBy: ● snapd.socket
Main PID: 704 (code=exited, status=0/SUCCESS)
## 删除不需要的软件包
既然服务已停用,保留软件包也就没有意义了。 您可以删除软件包以释放额外的磁盘空间和内存。 您可以使用`apt purge`命令删除软件包及其传递的软件包,如下所示:
$ sudo apt purge --auto-remove snapd
Reading package lists… Done
Building dependency tree
Reading state information… Done
The following packages will be REMOVED:
snapd* squashfs-tools*
0 upgraded, 0 newly installed, 2 to remove and 116 not upgraded.
After this operation, 147 MB disk space will be freed.
Do you want to continue? y
…
即使软件包不受服务控制,您也可以使用相同的命令。 确定您不需要的软件包,然后简单地删除它们。 您应该能够获得系统更加精简的足迹。
潜在攻击者无法再使用 snapd 服务来利用系统。 您应该对任何不需要的服务重复此过程。 结果,snapd 服务将不再存在于系统中:
$ systemctl status snapd
Unit snapd.service could not be found.
# 最小化 IAM 角色
系统级别的身份验证和访问管理(IAM)涉及管理 Linux 用户、他们所属的组以及授予他们的权限。 任何目录和文件都将为用户分配文件权限。
适当的用户和访问管理是每个系统管理员的经典责任。 尽管您作为 Kubernetes 管理员的角色可能不直接涉及系统级别的 IAM,但理解安全性的影响至关重要。 您可能需要与同行合作以加固运行 Kubernetes 集群的系统。
本节将简要介绍如何管理用户和组。 我们还将讨论如何设置文件权限和所有权以尽可能减少访问权限。 在本书中,我们只是涉及这个主题的皮毛。 如需更多信息,请参考您选择的 Linux 文档。
## 场景:攻击者使用凭据获取文件访问权限
安全漏洞可能导致用户凭据被窃取。 获得有效用户凭据使攻击者可以打开其他攻击向量的大门。 图 4-2 显示一个攻击者使用窃取的用户凭据登录到群集节点,现在可以与分配给用户权限的所有文件和目录进行交互。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/ckss_0402.png>
###### 图 4-2。 攻击者使用窃取的凭据访问文件
建议遵循最小特权原则。 只向有限的用户组授予管理权限。 所有其他用户应只被允许执行其工作所需的操作。
## 理解用户管理
每个用户必须对系统进行身份验证才能使用它。 认证用户根据分配的权限访问资源。 本节将为您介绍管理用户所需的主要操作。
### 用户列表
要列出系统上的所有用户,请渲染文件`/etc/passwd`的内容。每个条目遵循一般模式`username:password:UID:GID:comment:home:shell`。模式内的某些字段可能为空:
$ cat /etc/passwd
root❌0:0:root:/root:/bin/bash
nobody❌65534:65534:nobody:/nonexistent:/usr/sbin/nologin
…
命令输出将用户`root`呈现在输出的第一个位置。字符串的最后一部分`/bin/bash`表示用户被允许使用`bash` shell 登录系统。其他用户可能根本无法登录。对于这些用户,您将在`shell`字段中找到分配给`/usr/sbin/nologin`的字符串。
在任何给定时间点,您可以看到由用户启动的哪些进程。以下命令显示所有`bash`进程,包括启动它的相应用户:
$ ps aux | grep bash
root 956 0.0 0.4 22512 19200 pts/0 Ss 17:57 0:00 -bash
root 7064 0.0 0.0 6608 2296 pts/0 S+ 18:08 0:00 grep
–color=auto bash
### 添加用户
在某些时候,您可能希望让团队成员访问运行集群节点的机器,并限制权限。您可以使用`adduser`命令将新用户添加到系统。添加标志`--shell /sbin/nologin`以禁用用户的 Shell 访问权限。以下命令创建用户`ben`:
$ sudo adduser ben
Adding user ‘ben’ …
Adding new group ‘ben’ (1001) …
Adding new user ‘ben’ (1001) with group ‘ben’ …
Creating home directory ‘/home/ben’ …
Copying files from ‘/etc/skel’ …
New password:
Retype new password:
…
用户条目已添加到文件`/etc/passwd`中:
$ cat /etc/passwd
…
ben❌1001:1001:,:/home/ben:/bin/bash
### 切换到用户
您可以使用`su`命令在 Shell 中更改用户。以下命令切换到之前创建的用户`ben`。系统将要求您输入用户的密码:
$ su ben
Password:
ben@controlplane:/root$ pwd
/root
Shell 将通过其提示指示当前用户。您将从运行`su`命令时使用的帐户继承环境变量。要创建新环境,请在`su`命令中添加连字符:
$ su - ben
ben@controlplane:~$ pwd
/home/ben
另一种临时切换用户的方法是使用`sudo`命令。您需要有提升的权限来执行该命令。因此,`sudo`命令等效于“以管理员身份运行此命令”:
$ sudo -u ben pwd
/root
### 删除用户
由系统中的用户代表的团队成员转移到其他团队,或者可能只是离开公司。您将希望撤销用户的访问权限,以防止未经授权使用凭据。以下命令删除用户,包括用户的主目录:
$ sudo userdel -r ben
## 理解组管理
系统管理员更方便地将具有类似访问要求的用户分组,以控制个别用户级别的权限。Linux 系统提供了组的概念,用于基于团队或特定组织角色组织用户。我们将简要介绍组管理的最重要方面。
### 列出组
可以通过检查文件`/etc/group`的内容来列出组。每个条目遵循一般模式`groupname:password:GID:group members`:
$ cat /etc/group
root❌0:
plugdev❌46:packer
nogroup❌65534:
…
正如您在输出中看到的那样,某些字段可能为空。唯一具有分配成员的组是`plugdev`,其名称为`packer`。
### 添加组
使用命令`groupadd`添加新组。以下示例添加组`kube-developers`:
$ sudo groupadd kube-developers
该组现在将在文件`/etc/group`中列出。请注意,组标识符为 1004:
$ cat /etc/group
…
kube-developers❌1004:
### 将用户分配到一个组
要将组分配给用户,请使用`usermod`命令。以下命令将用户`ben`添加到组`kube-developers`:
$ sudo usermod -g kube-developers ben
组标识符 1004 充当`kube-developers`组的替代品:
$ cat /etc/passwd | grep ben
ben❌1001:1004:,:/home/ben:/bin/bash
### 删除组
有时您希望完全摆脱一个组。也许是指向一个不再存在的 Linux 组的组织角色。使用`groupdel`命令删除一个组。如果成员仍然是该组的一部分,您将收到错误消息:
$ sudo groupdel kube-developers
groupdel: cannot remove the primary group of user ben
在删除组之前,您应该使用`usermod`命令将组成员重新分配到另一个组。以下命令将组从`kube-developers`更改为`kube-admins`。假设组`kube-admins`已经在之前创建好:
$ sudo usermod -g kube-admins ben
$ sudo groupdel kube-developers
## 理解文件权限和所有权
将文件权限分配为尽可能小的访问权限对于最大化安全性至关重要。这就是 Linux 文件权限和所有权发挥作用的地方。我只会高层次地讨论相关操作。更多详细信息,请参考(https://oreil.ly/3IpRT)。
### 查看文件权限和所有权
每个用户都可以创建新的目录和文件。例如,您可以使用`touch`命令创建一个空文件。以下命令在当前目录中创建一个名为`my-file`的文件:
$ touch my-file
要查看目录内容的“长”格式,请使用`ls`命令。由于请求的`-l`命令行参数,输出的长格式呈现文件权限和文件所有权:
$ ls -l
total 0
-rw-r–r-- 1 root root 0 Sep 26 17:53 my-file
输出的重要部分是`-rw-r--r--`。第一个字符是特殊的权限字符,可以根据系统而变化,后跟三个以`rwx`表示的组合。前三个字符代表所有者权限,第二组三个字符是组权限,最后三个字符表示所有用户的权限。符号`r`表示读取权限,`w`表示写入权限,`x`表示执行权限。在前面的示例中,用户`root`可以读取和写入文件,而组和其他用户只能读取文件。
### 更改文件所有权
使用`chown`命令更改文件或目录的用户和组分配。命令的语法遵循`chown owner:group filename`的模式。以下命令将文件的所有权更改为用户`ben`,但不重新分配组。执行`chown`命令的用户需要具有写权限:
$ chown ben my-file
$ ls -l
total 0
-rw-r–r-- 1 ben root 0 Sep 26 17:53 my-file
### 更改文件权限
您可以使用`chmod`命令以多种表示法添加或删除权限。例如,使用以下命令为文件所有者删除写权限:
$ chmod -w file1
$ ls -l
total 0
-r–r–r-- 1 ben root 0 Sep 26 17:53 my-file
# 减少网络对外部访问
只有允许对 Kubernetes 操作必需的端口进行集群节点的外部访问。我们已经在“保护节点元数据和端点”中讨论了标准的 Kubernetes 端口。所有其他端口的访问应被阻止。
## 辨识和停用开放端口
像 FTP 服务器、Web 服务器以及诸如 Samba 之类的文件和打印服务这样的应用程序通过开放端口来向客户端公开通信终点。运行打开网络通信的应用程序可能会带来安全风险。您可以通过简单地禁用服务并卸载应用程序来消除风险。
假设我们在控制平面节点上[安装了 Apache 2 HTTP Web 服务器](https://oreil.ly/t-np3),使用以下命令:
$ sudo apt update
$ sudo apt install apache2
# 关于 netstat 命令的更新
`netstat`命令已经弃用,推荐使用更快速、更易读的`ss`命令。有关更多信息,请参阅您使用的操作系统的文档。
我们可以使用命令行工具`ss`检查所有开放端口,该工具类似于`netstat`。以下命令显示了所有开放端口及其进程,其中包括 Apache 2 暴露的端口 80:
$ sudo ss -ltpn
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
…
LISTEN 0 511 *:80 : users:
((“apache2”,pid=18435,fd=4),(“apache2”,pid=18434,fd=4),(“apache2”, ]
pid=18432,fd=4))
您可能仅临时需要 Web 服务器,并且可能只是忘记了安装它。该过程当前由服务器管理。您可以使用`systemctl status`命令查看服务的状态:
$ sudo systemctl status apache2
● apache2.service - The Apache HTTP Server
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor
preset: enabled)
Active: active (running) since Tue 2022-09-20 22:25:25 UTC; 39s ago
Docs: https://httpd.apache.org/docs/2.4/
Main PID: 18432 (apache2)
Tasks: 55 (limit: 2339)
Memory: 5.6M
CGroup: /system.slice/apache2.service
├─18432 /usr/sbin/apache2 -k start
├─18434 /usr/sbin/apache2 -k start
└─18435 /usr/sbin/apache2 -k start
Kubernetes 不需要 Apache 2。我们决定关闭该服务并卸载该软件包:
$ sudo systemctl stop apache2
$ sudo systemctl disable apache2
Synchronizing state of apache2.service with SysV service script with
/lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable apache2
Removed /etc/systemd/system/multi-user.target.wants/apache2.service.
$ sudo apt purge --auto-remove apache2
验证端口是否不再被使用。`ss`命令再也找不到暴露端口 80 的应用程序:
$ sudo ss -ltpn | grep :80
## 设置防火墙规则
控制端口的另一种方式是使用操作系统级防火墙的帮助。在 Linux 上,您可以使用(https://oreil.ly/iqiwv)。本节将简要介绍如何启用 UFW 及配置防火墙规则。
遵循最小权限原则,建议首先启用防火墙,并为*任何*传入和传出的网络流量设置拒绝规则。以下命令演示了实现这一目标的步骤:
$ sudo ufw allow ssh
Rules updated
Rules updated (v6)
$ sudo ufw default deny outgoing
Default outgoing policy changed to deny
(be sure to update your rules accordingly)
$ sudo ufw default deny incoming
Default incoming policy changed to deny
(be sure to update your rules accordingly)
$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
你需要允许像`kubectl`这样的外部工具连接到运行在 6443 端口的 API 服务器。在控制平面节点上,执行以下命令以允许访问 API 服务器端口:
$ sudo ufw allow 6443
Rule added
Rule added (v6)
您需要重复相同的过程以打开控制平面和工作节点上的其他端口。确保所有不需要用于操作 Kubernetes 的其他端口都被阻止。
# 使用内核硬化工具
容器内运行的应用程序或进程可以进行系统调用。典型示例可能是 `curl` 命令执行 HTTP 请求。系统调用是用户空间中用于请求内核服务的程序抽象。我们可以使用内核硬化工具限制允许进行的系统调用。CKS 考试明确提到了两种工具,AppArmor 和 seccomp。我们将讨论这两种工具及其与 Kubernetes 集成的机制。
## 使用 AppArmor
(https://apparmor.net) 提供对在 Linux 系统上运行的程序的访问控制。该工具在用户空间调用的应用程序和底层系统功能之间实现了一个额外的安全层。例如,我们可以限制网络调用或文件系统交互。许多 Linux 发行版(如 Debian、Ubuntu、openSUSE)已经预装了 AppArmor。因此,不需要手动安装 AppArmor。不支持 AppArmor 的 Linux 发行版使用 (https://oreil.ly/CKBr7) 代替,其与 AppArmor 采取了类似的方法。了解 SELinux 超出了 CKS 考试的范围。
### 理解配置文件
定义程序能够执行或无法执行的规则在 AppArmor 配置文件中定义。每个配置文件在生效之前都需要加载到 AppArmor 中。AppArmor 提供了一个命令行工具,用于检查已加载的配置文件。执行命令 `aa-status` 可以查看所有加载配置文件的摘要。您会看到 AppArmor 已经带有一组默认的应用程序配置文件,用于保护 Linux 服务:
$ sudo aa-status
apparmor module is loaded.
31 profiles are loaded.
31 profiles are in enforce mode.
/snap/snapd/15177/usr/lib/snapd/snap-confine
…
0 profiles are in complain mode.
14 processes have profiles defined.
14 processes are in enforce mode.
/pause (11934) docker-default
…
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
配置文件模式决定运行时对规则的处理,如果匹配事件发生。AppArmor 区分两种配置文件模式:
强制执行
系统强制执行规则,并将违规情况报告并记录到系统日志中。您应该使用此模式来防止程序进行特定的调用。
抱怨
系统不强制执行规则,但会将违规情况记录到日志中。如果您希望了解程序调用的调用,请使用此模式。
示例 4-1 定义了一个自定义配置文件 `k8s-deny-write`,用于限制文件写入访问权限。该文件应放置在每个执行工作负载的工作节点的目录 `/etc/apparmor.d` 中。本书不详细解释所有规则。要了解更多信息,请参阅 (https://oreil.ly/mNuWB)。
##### 示例 4-1\. 限制文件写入访问的 AppArmor 配置文件
#include <tunables/global>
profile k8s-deny-write flags=(attach_disconnected) { https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/1.png
#include <abstractions/base>
file, https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/2.png
deny /** w, https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/3.png
}
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO1-1>
`profile` 关键字后的标识符是配置文件的名称。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO1-2>
应用到文件操作。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO1-3>
拒绝所有文件写入。
### 设置自定义配置文件
要将配置文件加载到 AppArmor 中,请在工作节点上运行以下命令:
$ sudo apparmor_parser /etc/apparmor.d/k8s-deny-write
该命令默认使用强制执行模式。要加载投诉模式的配置文件,请使用 `-C` 选项。现在 `aa-status` 命令将列出该配置文件以及默认配置文件。从输出中可以看到,配置文件以强制执行模式列出:
$ sudo aa-status
apparmor module is loaded.
32 profiles are loaded.
32 profiles are in enforce mode.
k8s-deny-write
…
AppArmor 还支持作为一个实用程序包的附加方便命令。如果你想使用它们,可以手动安装该包,并执行以下命令:
$ sudo apt-get update
$ sudo apt-get install apparmor-utils
安装完成后,您可以使用 `aa-enforce` 命令以强制执行模式加载配置文件,使用 `aa-complain` 命令以投诉模式加载配置文件。对于考试来说,直接使用标准的 `apparmor_parser` 命令可能更容易。
### 将配置文件应用到容器上
在使用 Pod 定义中的 AppArmor 规则之前,您需要确保一些前提条件。首先,容器运行时需要支持 AppArmor,以便规则生效。此外,运行 Pod 的工作节点上需要安装 AppArmor。最后,请确保加载了配置文件,如前一节所述。
示例 4-2 展示了一个在文件 `pod.yaml` 中定义的 Pod 的 YAML 清单。要将配置文件应用到容器上,需要设置一个特定的注解。注解键需要使用格式 `container.apparmor.security.beta.kubernetes.io/<container-name>` 中的键。在我们的案例中,容器名称为 `hello`。完整的键是 `container.apparmor.security.beta.kubernetes.io/hello`。注解的值遵循模式 `localhost/<profile-name>`。这里我们想要使用的自定义配置文件是 `k8s-deny-write`。有关配置选项的更多信息,请参阅 (https://oreil.ly/1o3zO)。
##### 示例 4-2\. 应用一个 AppArmor 配置文件到一个容器的 Pod
apiVersion: v1
kind: Pod
metadata:
name: hello-apparmor
annotations:
container.apparmor.security.beta.kubernetes.io/hello: \ https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/1.png
localhost/k8s-deny-write https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/2.png
spec:
containers:
[*]name: hello https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/3.png
image: busybox:1.28
command: [“sh”, “-c”, “echo ‘Hello AppArmor!’ && sleep 1h”]
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO2-1>
由硬编码前缀和由斜杠字符分隔的容器名称组成的注解键。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO2-2>
在当前节点上指示的配置文件名称,由 `localhost` 表示。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO2-3>
容器名称。
我们已经准备好创建 Pod 了。运行 `apply` 命令,并指向 YAML 清单。等待 Pod 转换为“Running”状态:
$ kubectl apply -f pod.yaml
pod/hello-apparmor created
$ kubectl get pod hello-apparmor
NAME READY STATUS RESTARTS AGE
hello-apparmor 1/1 Running 0 4s
现在,您可以进入容器并执行文件写入操作:
$ kubectl exec -it hello-apparmor – /bin/sh
/ # touch test.txt
touch: test.txt: Permission denied
如果尝试向容器文件系统写入文件,AppArmor 将会阻止此操作,并显示“Permission denied”的消息。
## 使用 seccomp
Seccomp,即“安全计算模式”,是另一个 Linux 内核特性,可以限制从用户空间到内核的调用。Seccomp 配置文件是定义限制系统调用及其参数的规则的机制。使用 Seccomp 可以降低利用 Linux 内核漏洞的风险。有关 Kubernetes 上 Seccomp 的更多信息,请参阅 [文档](https://oreil.ly/B8I5L)。
### 将默认的容器运行时配置文件应用到容器上
容器运行时(如 Docker Engine 或 containerd)附带默认的 seccomp 配置文件。默认的 seccomp 配置文件允许应用程序使用的最常用系统调用,同时禁止被认为危险的系统调用。
Kubernetes 在创建 Pod 时不会将默认容器运行时配置文件应用于容器,但可以通过设置安全上下文属性`seccompProfile`将 seccomp 配置文件类型设置为`RuntimeDefault`来启用它。您可以使用`SeccompDefault` [功能门](https://oreil.ly/m9g0G)在 Pod 级别启用该功能。示例 4-3 演示了其使用方法。
##### 示例 4-3. 一个应用了容器运行时配置文件提供的默认 seccomp 配置文件的 Pod
apiVersion: v1
kind: Pod
metadata:
name: hello-seccomp
spec:
securityContext:
seccompProfile:
type: RuntimeDefault https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/1.png
containers:
[*]name: hello
image: busybox:1.28
command: [“sh”, “-c”, “echo ‘Hello seccomp!’ && sleep 1h”]
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO3-1>
应用默认的容器运行时配置文件。
使用`apply`命令并指向 YAML 清单文件可以启动 Pod。Pod 应该会转换为“运行”状态:
$ kubectl apply -f pod.yaml
pod/hello-seccomp created
$ kubectl get pod hello-seccomp
NAME READY STATUS RESTARTS AGE
hello-seccomp 1/1 Running 0 4s
在容器中执行的`echo`命令在安全性方面被默认的 seccomp 配置文件视为无问题。以下命令检查容器的日志:
$ kubectl logs hello-seccomp
Hello seccomp!
允许调用并导致将消息“Hello seccomp!”写入标准输出。
### 设置自定义配置文件
除了默认容器运行时配置文件之外,您还可以创建和设置自定义配置文件。这些文件的标准目录是`/var/lib/kubelet/seccomp`。我们将在子目录`profiles`中组织我们的自定义配置文件。如果目录不存在,则创建该目录:
$ sudo mkdir -p /var/lib/kubelet/seccomp/profiles
我们决定在配置文件目录中的文件`mkdir-violation.json`中创建我们的自定义配置文件。示例 4-4 显示了配置文件定义的详细信息。简而言之,规则集禁止使用`mkdir`系统调用。
##### 示例 4-4. 阻止执行`mkdir`系统调用的 seccomp 配置文件
{
“defaultAction”: “SCMP_ACT_ALLOW”, https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/1.png
“architectures”: ![2
“SCMP_ARCH_X86_64”,
“SCMP_ARCH_X86”,
“SCMP_ARCH_X32”
],
“syscalls”: [
{
“names”: [
“mkdir”
],
“action”: “SCMP_ACT_ERRNO” https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/3.png
}
]
}
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO4-1>
默认操作适用于所有系统调用。在这里,我们将使用`SCMP_ACT_ALLOW`允许所有系统调用。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO4-2>
您可以过滤默认操作应用于的特定体系结构。该字段的定义是可选的。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO4-3>
默认操作可以通过声明更细粒度的规则来覆盖。`SCMP_ACT_ERRNO`操作将阻止执行`mkdir`系统调用。
将自定义配置文件放入目录`/var/lib/kubelet/seccomp`中不会自动将规则应用于 Pod。您仍然需要配置 Pod 以使用它。
### 将自定义配置文件应用于容器
应用自定义配置文件与应用默认的容器运行时配置文件遵循类似的模式,但存在一些小差异。正如您在示例 4-5 中所看到的,我们将安全配置文件的`seccompProfile`属性指向文件`mkdir-violation.json`,并将类型设置为`Localhost`。
##### 示例 4-5\. 应用自定义 seccomp 配置文件的 Pod 阻止了`mkdir`系统调用。
apiVersion: v1
kind: Pod
metadata:
name: hello-seccomp
spec:
securityContext:
seccompProfile:
type: Localhost https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/1.png
localhostProfile: profiles/mkdir-violation.json https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/2.png
containers:
[*]name: hello
image: busybox:1.28
command: [“sh”, “-c”, “echo ‘Hello seccomp!’ && sleep 1h”]
securityContext:
allowPrivilegeEscalation: false
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO5-1>
指的是当前节点上的一个配置文件。
<https://github.com/OpenDocCN/ibooker-devops-zh/raw/master/docs/cks-stdgd/img/#co_system_hardening_CO5-2>
应用名称为`mkdir-violation.json`的配置文件位于子目录`profiles`中。
使用声明性的`apply`命令创建 Pod。等待 Pod 转换为“运行”状态。
$ kubectl apply -f pod.yaml
pod/hello-seccomp created
$ kubectl get pod hello-seccomp
NAME READY STATUS RESTARTS AGE
hello-seccomp 1/1 Running 0 4s
进入容器的 Shell 中,验证 seccomp 是否正确执行了应用的规则:
$ kubectl exec -it hello-seccomp – /bin/sh
/ # mkdir test
mkdir: can’t create directory test: Operation not permitted
正如您在输出中所看到的,在尝试执行`mkdir`命令时,操作会生成一个错误消息。违反了自定义配置文件中的规则。
# 概要
处理安全方面不仅限于 Kubernetes 集群组件或工作负载。您可以在主机系统层面上做很多事情。我们讨论了不同的操作系统功能以及如何利用它们来减少潜在的安全漏洞。
许多操作系统提供了丰富的软件包和服务,以提供更丰富的用户体验。识别不需要操作 Kubernetes 集群的功能非常重要。彻底清理不必要的软件包和服务,并关闭不需要的端口。您还需要严格控制哪些用户被允许访问特定目录、文件和应用程序。利用 Linux 的用户管理来限制权限。
容器中运行的应用程序和进程通常会进行系统调用,这是非常普遍的。您可以使用 Linux 内核加固工具如 AppArmor 和 seccomp 来限制这些调用。只允许那些对您应用运行所需至关重要的系统调用。
# 考试要点
具备基本的 Linux 操作系统工具使用理解。
CKS 考试主要关注 Kubernetes 中的安全功能。该领域跨越边界到 Linux 操作系统安全特性。熟悉 Linux 特定的工具和安全方面的高级内容不会有害。独立于本章涵盖的内容,建议熟悉 Linux 上的服务、包管理、用户管理和网络管理。
知道如何将 Linux 内核加固工具与 Kubernetes 集成。
AppArmor 和 seccomp 只是一些可以与 Kubernetes 集成的内核加固工具,用于限制容器中发出的系统调用。实践加载配置文件并将其应用于容器的过程。为了拓宽视野,您可能还希望探索与 Kubernetes 并行工作的其他内核功能,如(https://oreil.ly/DrGbB)或(https://oreil.ly/GyUoc)。
# 样例练习
这些练习的解决方案可以在附录中找到。
1.进入已检出的 GitHub 代码库 [*bmuschko/cks-study-guide*](https://oreil.ly/sImXZ) 的目录 *app-a/ch04/close-ports*。使用命令 `vagrant up` 启动运行集群的虚拟机(VMs)。该集群包含一个名为 `kube-control-plane` 的控制平面节点和一个名为 `kube-worker-1` 的工作节点。完成后,使用 `vagrant destroy -f` 关闭集群。
确定在 VM `kube-worker-1` 上监听端口 21 的进程。为降低攻击者利用该端口的风险,决定关闭该端口对应的进程。
*先决条件:* 此练习需要安装工具 (https://oreil.ly/FiyeH) 和 (https://oreil.ly/WW8IK)。
1.进入已检出的 GitHub 代码库 [*bmuschko/cks-study-guide*](https://oreil.ly/sImXZ) 的目录 *app-a/ch04/apparmor*。使用命令 `vagrant up` 启动运行集群的虚拟机(VMs)。该集群包含一个名为 `kube-control-plane` 的控制平面节点和一个名为 `kube-worker-1` 的工作节点。完成后,使用 `vagrant destroy -f` 关闭集群。
创建一个名为 `network-deny` 的 AppArmor 配置文件。该配置文件应禁止任何入站和出站网络流量。将该配置添加到处于强制执行模式的 AppArmor 规则集中。将该配置应用于运行在 `default` 命名空间中的名为 `network-call` 的 Pod。检查 Pod 的日志以确保无法进行网络调用。
*先决条件:* 此练习需要安装工具 (https://oreil.ly/FiyeH) 和 (https://oreil.ly/WW8IK)。
1.进入已检出的 GitHub 代码库 [*bmuschko/cks-study-guide*](https://oreil.ly/sImXZ) 的目录 *app-a/ch04/seccomp*。使用命令 `vagrant up` 启动运行集群的虚拟机(VMs)。该集群包含一个名为 `kube-control-plane` 的控制平面节点和一个名为 `kube-worker-1` 的工作节点。完成后,使用 `vagrant destroy -f` 关闭集群。
创建一个名为 `audit.json` 的 seccomp 配置文件,记录标准 seccomp 目录中的所有系统调用。将该配置应用于运行在 `default` 命名空间中的名为 `network-call` 的 Pod。检查日志文件 `/var/log/syslog` 查看日志条目。
*先决条件:* 此练习需要安装工具 (https://oreil.ly/FiyeH) 和 (https://oreil.ly/WW8IK)。
1.创建一个名为 `sysctl-pod` 的新 Pod,使用镜像 `nginx:1.23.1`。设置 sysctl 参数 `net.core.somaxconn` 为 1024 和 `debug.iotrace` 为 `1`。检查 Pod 的状态。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]