Introduction

我们将通过引导您了解 Kubernetes 基础知识、一些安全的 K8s 实践以及一些实践经验,将这个似乎每个人都非常喜欢的神秘词变成一个熟悉的术语。开始消除这个谜团,让你不再思考“‘Keights’到底是什么?”。 Kubernetes 有时被称为 K8s,如 k(ubern)8s - 在这个行业中任何可以节省时间的东西!

学习先决条件

这个房间将延续 DevSecOps 路径,因此所有之前的 DevSecOps 模块都应该完成,尤其是 Container Security https://tryhackme.com/module/container-security)模块([容器化简介](https://tryhackme.com/jr/introtocontainerization) 和 [Docker 简介](https://tryhackme.com/jr /introtodockerk8pdqk)) 应该完成。

学习目标

  • 了解为什么需要像 Kubernetes 这样的系统
  • 了解基本的 Kubernetes 架构
  • 了解 Kubernetes 环境的关键组成部分
  • 了解如何使用 kubectl 遍历 Kubernetes 集群
  • 了解 DevSecOps 工程师如何使用 Kubernetes 以及最佳 K8s 安全实践

Kubernetes 101

新的曙光

为了更好地理解 Kubernetes,让我们首先考虑一下它出现的背景。换句话说,为什么首先需要它?过去,公司采用整体架构更为常见。也就是说,应用程序被构建为单个单元、单个代码库,并且通常作为单个组件部署的单个可执行文件。这对很多公司来说都有效,并且仍然有效。然而,一些公司开始转向微服务架构,因此不再有一个单一的应用程序,而是将其分解为不同的组件,每个组件通常具有不同的业务功能,这意味着可以扩展高需求的业务功能,而无需扩展整个应用程序。一个引人注目的例子发生在 2009 年(在微服务还没有名字之前),当时 Netflix 的单体架构无法满足其日益增长的需求,因此开始采用微服务架构。

微服务架构现在是一种越来越流行的方法,这对于今天的课程如此重要的原因是容器技术为这些微服务提供了完美的宿主。由于微服务架构通常包含数百甚至数千个这样的容器,因此需要一种技术来帮助管理和组织它们——无需猜测是什么技术可以解决这个问题。

进入 Kubernetes

让我们从一个大问题开始吧?什么是 Kubernetes?在本模块的前面的房间中,我们讨论了容器和 Docker,所以让我们以此为出发点。想象一下,您有一个 Docker 容器,正在运行一个可以从外部访问的应用程序。 突然,该应用程序开始接收大量流量,因此需要启动一个包含该应用程序的新容器,以便可以在两者之间分配流量。这就是 Kubernetes 作为容器编排系统的用武之地。 “管弦乐队”这个词让人想起真正的管弦乐队的形象。这个比喻确实有一定的吸引力,容器就像仪器,而 Kubernetes 是控制流程的指挥者。这将是一个非常奇怪的管弦乐队,当歌曲不再需要演奏者时,指挥告诉演奏者离开,并在需要时带来新的演奏者。

Kubernetes 的诸多好处

现在我们已经了解了 Kubernetes 是什么以及为什么需要它,让我们看看这项技术如何使 DevSecOps 领域受益。

  • Kubernetes 使应用程序高度可用和可扩展。例如,它通过复制应用程序(及其数据库组件)并将对该应用程序的外部请求负载平衡到可用资源来实现这一点,从而从架构中消除单点故障并消除可能减慢应用程序响应的瓶颈次。如前所述,可扩展性是许多公司非常关心的问题。 Kubernetes 允许扩大或缩小工作负载以满足需求。
  • Kubernetes 还具有高度可移植性,因为它可以在几乎任何类型的基础设施的任何地方运行,并且可以与单个或多云集一起使用,使其成为一个非常灵活的工具。
  • Kubernetes 的另一个好处是它的受欢迎程度;正因为如此,许多技术都与其兼容,并且可以用来使每个工具变得更加强大。

这些只是 Kubernetes 让 DevSecOps 领域受益的几种方式,它解释了为什么这个词被如此广泛地使用!

Kubernetes Architecture

集群架构

好的,我们已经了解了 Kubernetes 是什么、为什么需要它以及它如何使我们在 DevSecOps 中受益。现在,是时候通过深入了解并分析它如何做到这一点来加深我们的理解了。这是正确的!现在是建筑时间!我们将一一介绍构成 Kubernetes 架构的每个关键组件,然后最后将它们组合在一起并展示所有这些组件如何连接。让我们开始吧!

Kubernetes Pod

Pod 是您可以在 Kubernetes 中创建和管理的最小可部署计算单元。当您使用 Kubernetes 进行 DevSecOps 工作时,您会听到很多这个词。您可以将 Pod 视为一组一个或多个容器。这些容器共享存储和网络资源。 因此,同一 Pod 上的容器可以轻松通信,就像它们在同一台机器上一样,同时保持一定程度的隔离。 Pod 在 Kubernetes 中被视为一个复制单元;如果需要扩大工作负载,您将增加运行的 Pod 数量。

pod diagram

Kubernetes 节点

Kubernetes 工作负载(应用程序)在容器内运行,容器放置在 Pod 中。这些 Pod 在节点上运行。在谈论节点架构时,有两种类型需要考虑。 控制平面(也称为“主节点”)和工作节点。这两个都有自己的架构/组件,所以让我们来看看它们。节点可以是虚拟机或物理机。可以这样想:如果应用程序在放置在 Pod 中的容器中运行,则节点包含运行 Pod 所需的所有服务。

Kubernetes 集群

在最高层,我们有 Kubernetes 集群;简而言之,集群只是一组节点。

Kubernetes 控制平面

控制平面管理集群中的工作节点和 Pod。它通过使用各种组件来实现这一点。查看每个组件以及它们的职责。然后将它们全部放在控制平面架构图中。

Kube-apiserver

API服务器是控制平面的前端,负责暴露Kubernetes API。 kube-apiserver 组件是可扩展的,这意味着可以创建多个实例,以便对流量进行负载平衡。

Etcd

Etcd 是一个键/值存储,包含集群数据/集群的当前状态。它具有高可用性和一致性。如果集群中发生更改,例如启动另一个 pod,这将反映在键/值存储、etcd 中。其他控制平面组件依赖 etcd 作为信息存储,并查询它以获取可用资源等信息。

Kube 调度程序

kube-scheduler 组件主动监控集群。它的工作是捕获任何尚未分配给节点的新创建的 Pod,并确保它被分配给一个节点。它根据特定标准做出此决定,例如正在运行的应用程序使用的资源或所有工作节点上的可用资源。

Kube-控制器-管理器

该组件负责运行控制器进程。有许多不同类型的控制器进程,但控制器进程的一个示例是 节点控制器 进程,它负责通知节点何时出现故障。然后,控制器管理器将与调度程序组件通信以调度新节点的出现。

云控制器管理器

该组件支持 Kubernetes 集群和云提供商 API 之间的通信。该组件的目的是允许将集群内部通信的组件与通过与云提供商交互进行外部通信的组件分开。这也允许云提供商按照自己的节奏发布功能。

Kubernetes uses a Hub and Spoke API pattern

Kubernetes工作节点

Worker 节点负责维护正在运行的 Pod。让我们看一下每个工作节点上都存在的组件以及它们的职责:

Kubelet

Kubelet 是一个运行在集群中每个节点上的代理,负责确保容器在 pod 中运行。 Kubelet 提供了 pod 规范,并确保此 pod 规范中详细说明的容器运行且健康!它执行控制器管理器给它的操作,例如,启动内部有容器的 Pod。

Kube-proxy

Kube-proxy 负责集群内的网络通信。它制定网络规则,以便流量可以流动并定向到 Pod(从集群内部或外部)。流量不会直接到达 Pod,而是到达称为服务的东西(它将与一组 Pod 关联),然后被定向到其中一个关联的 Pod。 下一个任务中将详细介绍服务!

Container runtime

Pod 内有运行的容器。为此,必须在每个节点上安装容器运行时。到目前为止,我们已经在本模块中介绍了一个示例,它可能是最受欢迎的选择,Docker。但是,可以使用一些替代方案,例如 rkt 或 runC。

The control plane can communicate with nodes using two paths

组件之间的通信

好的,我们已经介绍了很多内容。让我们看看我们刚刚介绍的所有这些单独组件如何协同工作来构成 Kubernetes 架构。 Kubernetes 集群包含节点,Kubernetes 通过将容器放入在这些节点上运行的 Pod 来运行工作负载。请看下图,了解所有这些组件是如何组合在一起的。

Cluster diagram

Kubernetes Landscape

The Lay of the Land

我们刚刚了解了 Kubernetes 架构以及事物的工作原理和幕后设置!现在是时候向您展示这片土地的情况了。换句话说,作为一名 DevSecOps 工程师,您每天会与什么互动?我们将介绍 Kubernetes 中一些最常见的概念并详细解释它们的含义。

Namespaces

在 Kubernetes 中,命名空间用于隔离单个集群中的资源组。例如,假设您想要对与特定组件关联的资源进行分组,或者如果您使用集群来托管多个租户,则按租户对资源进行分组。资源在命名空间内必须唯一命名,但相同的资源名称可以在不同的命名空间中使用。

ReplicaSet

顾名思义,Kubernetes 中的 ReplicaSet 维护一组副本 pod,并且可以保证 x 个相同 pod 的可用性(当需要在多个 pod 之间分配工作负载时,相同 pod 很有帮助)。 ReplicaSet 通常不是直接定义的(就此而言,pod 也不是),而是由部署进行管理,这给我们带来了下一个概念。

Deployments

Kubernetes 中的部署用于定义所需的状态。一旦定义了所需状态,部署控制器(控制器进程之一)就会将实际状态更改为所需状态。部署为 Pod 和副本集提供声明性更新。换句话说,作为用户,您可以定义部署,例如“test-nginx-deployment”。在定义中,您可以注意到您希望此部署有一个包含三个 nginx pod 的 ReplicaSet。一旦定义了此部署,ReplicaSet 将在后台创建 Pod。

StatefulSets

要了解 Kubernetes Statefulset 是什么,您必须首先了解有状态应用程序和无状态应用程序之间的区别。有状态应用程序存储和记录用户数据,允许它们返回到特定状态。例如,假设您有一个使用电子邮件应用程序的开放会话并阅读了 3 封电子邮件,但您的会话被中断。在这种情况下,您可以重新加载此应用程序,并且状态将被保存,确保这 3 封电子邮件仍然被阅读。然而,无状态应用程序不知道任何先前的用户交互,因为它不存储用户会话数据。例如,考虑使用搜索引擎提出问题。如果该会话被中断,您可以通过搜索问题重新开始该过程,而不依赖于任何先前的会话数据。

对于这些无状态应用程序(搜索引擎示例),部署可用于定义和管理 Pod 副本。由于应用程序的无状态性质,可以使用随机 Pod 名称创建副本,并且在删除时可以随机删除 Pod。

但是,对于有状态应用程序(电子邮件示例)而言,情况并非如此,因为有状态应用程序需要访问和更新用户会话的当前状态。想象一下当前状态存储在跨 3 个 Pod 运行的数据库中(意味着数据库被复制 3 次)。现在,当其中一个数据库更新时会发生什么?这会使两个数据库不同步。这就是 StatefulSet 的用武之地,也是您使用 StatefulSet 而不是部署来管理 Kubernetes 中的有状态应用程序的原因。

Statefulsets 使有状态应用程序能够在 Kubernetes 上运行,但与部署中的 Pod 不同,它们不能以任何顺序创建,并且将具有唯一的 ID(这是持久性的,这意味着如果 Pod 发生故障,它将被恢复并保留此 ID) )与每个 Pod 关联。 换句话说,这些 Pod 是根据相同规格创建的,但不可互换。 StatefulSets 将有一个可以读取/写入数据库的 pod(因为如果其他 pod 可以的话,将会出现绝对的大屠杀和各种数据不一致),称为 master pod。其他 Pod(称为从属 Pod)只能读取并拥有自己的存储复制,该复制不断同步以确保反映主节点所做的任何更改。

statefulset diagram

服务

为了更好地理解 Kubernetes 中的服务,了解它们正在解决的问题非常重要。 Kubernetes Pod 是短暂的,这意味着它们的寿命很短,并且会定期启动和销毁。 想象一下现在需要与这些 Pod 建立连接。这可能来自集群内部;也许后端应用程序正在这些 Pod 中运行,并且前端应用程序需要访问它们,或者请求可能来自浏览器,并且这些 Pod 正在运行 Web 应用程序。要进行此连接,需要 IP 地址。 如果IP地址与Pod绑定,那么这些IP地址会频繁变化,导致各种问题;使用服务以便单个静态 IP 地址可以与 Pod 及其副本关联。 换句话说,服务被放置在这些 pod 前面并公开它们,充当访问点。拥有这个单一访问点可以让请求在 Pod 副本之间实现负载平衡。您可以定义不同类型的服务:ClusterIP、LoadBalancer、NodePort 和ExternalName。要了解有关这些类型和服务的更多信息,请查看此处

The source of the traffic will depend on the type of service

入口

在上面有关 Kubernetes 服务的部分中,我们提到了一个应用程序示例,可以通过使用服务公开运行该应用程序的 pod(我们将此称为服务 A)来访问该应用程序。现在想象一下这个 Web 应用程序有一个新功能。这个新功能需要自己的应用程序,因此有自己的一组 pod,这些 pod 由单独的服务(我们将此服务称为 B)公开。现在,假设用户请求访问 Web 应用程序的这一新功能;需要某种流量路由来确保该请求定向到服务 B。这就是入口的用武之地。入口充当集群的单个访问点,意味着所有路由规则都位于单个资源中。

Ingress diagram

K8s 中的 DevOps 与 DevSecOps

此任务向您展示了一些协同工作以构建集群的 Kubernetes 资源。接下来,我们将讨论如何与该集群交互以创建这些组件,但在此之前,这是讨论 K8s 世界中的分工的好时机。理解 DevOps 和 DevSecOps 职责之间的区别非常重要,尤其是在学习特定职业道路时。在非常高的层面上,您可以将 DevOps 任务视为与构建集群相关的任务,将 DevSecOps 任务视为与保护集群相关的任务。当然,根据公司架构或公司设置,会有一些重叠,但一般来说,这些任务可以定义为其中之一。由于此房间作为介绍,因此首先向您展示 Kubernetes 集群的构建块以及如何与这些资源(DevOps 任务)进行交互。由于这些知识是基础知识,因此当我们讨论保护集群(DevSecOps 任务)时,理解这些概念将有助于您完成后续任务。话虽这么说,正如前面提到的,这种区别很重要,特别是对于那些追求特定职业道路的人来说,所以请记住这一点!

Kubernetes Configuration

配置时间

现在我们已经介绍了 Kubernetes 中使用的一些关键组件,让我们将其中一些组件组合起来,并向您介绍如何配置它!我们将使用的示例是控制 ReplicaSet 的部署,该 ReplicaSet 管理服务公开的 pod。

Interfacing with deployment diagram

要配置此设置,我们需要两个配置文件,一个用于部署,一个用于服务。在向您介绍每个文件的工作方式之前,让我们先了解一下 Kubernetes 中的一些基本配置,这些配置将在这两个文件(以及所有配置文件)中保持一致。

首先,让我们讨论一下文件格式。Kubernetes 配置文件通常以 YAML 编写。它们也可以使用 JSON 格式互换,但根据 Kubernetes 文档,通常认为使用 YAML 是最佳做法,因为它简单易读(只需注意缩进!)。

必填字段

接下来,让我们讨论每个 YAML 文件中必须存在的四个字段,并细分每个文件中需要包含的内容:

apiVersion:您将用于创建此对象的 Kubernetes API 版本。您使用的 API 版本将取决于所定义的对象。可以在 此处 找到有关哪个对象使用哪个 API 版本的备忘单。

kind:您要创建哪种类型的对象(例如 Deployment、Service、StatefulSet)。

metadata:这将包含可用于唯一标识对象的数据(包括名称和可选的命名空间)。

spec:对象的期望状态(对于部署,这可能是 3 个 nginx pod)。

配置资源

这些是 Kubernetes YAML 配置文件的基础。让我们考虑一下这些,现在看看上面提到的两个文件。我们首先来看一下服务配置文件,因为在定义部署和服务时,通常最佳做法是先定义服务,然后再定义它指向的后端部署/副本集(这是因为当 Kubernetes 启动容器时,它会为容器启动时运行的每个服务创建一个环境变量)。我们的 example-service.yaml 文件如下所示:

apiVersion: v1
kind: Service
metadata:
name: example-nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 8080
targetPort: 80
type: ClusterIP

让我们分解一下:apiVersion 设置为 v1(最适合用于此简单服务示例的 Kubernetes API 版本),kind 设置为 service。对于 metadata,我们只是将此服务称为“example-nginx-service”。spec 变得更加有趣,在“selector”下,我们有“app: nginx”。当我们定义部署配置时,这一点很重要,ports 信息也是如此,因为我们在这里基本上是在说:“此服务将查找带有 nginx 标签的应用程序并将目标端口 80。这里需要区分‘port’和‘targetPort’字段。‘targetPort’是服务将向其发送请求的端口,即 pod 将监听的端口。‘port’是服务公开的端口。最后,‘type’定义为 ClusterIP(之前,我们讨论过有多种类型的服务,这是其中一种),这是默认的服务类型。现在让我们看一下部署 YAML 并定义此服务将指向的后端:

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

您可能注意到的第一件事是,在“spec”字段内,有一个名为“template”的嵌套字段,它本身包含“metadata”和“spec”字段。要理解这一点,请记住此任务开始时的图像。我们正在定义一个控制 ReplicaSet 的部署;在这里,在外部“spec”字段中,我们告诉 Kubernetes,我们希望此 ReplicaSet 中有 3 个副本(相同的 pod)。此 template 字段是 Kubernetes 将用于创建这些 pod 的模板,因此需要其自己的元数据字段(以便可以识别 pod)和 spec 字段(以便 Kubernetes 知道要运行哪个映像以及要监听哪个端口)。请注意,此处定义的端口与服务 YAML 中的端口相同。这是因为服务的目标端口是 80,需要匹配。除此之外,在外部“spec”字段中,您可以看到我们还将“selector”字段设置为具有“matchLabels”,这与我们在服务 YAML 的“selector”字段中定义的内容相匹配。这样服务就可以映射到正确的 pod。通过这两个配置 YAML 文件,我们定义了一个部署,该部署控制一个 ReplicaSet,该 ReplicaSet 管理 3 个 pod,所有 pod 都暴露给一个服务。一切都整合在一起了!

这些配置文件用于定义 Kubernetes 组件的期望状态;Kubernetes 将不断根据集群的当前状态检查此期望状态。使用 etcd(前面任务中提到的控制平面进程之一),Kubernetes 用当前状态填充这些配置文件并进行比较。例如,如果我们告诉 Kubernetes 我们希望运行 3 个 nginx pod,并且它在状态中检测到只有 2 个正在运行的 pod,它将开始采取措施来纠正此问题。

Q1:在配置文件中,您刚刚声明需要 4 个 nginx pod。在哪一个“必填字段”中声明了这一点?

A1:spec

Q2:配置文件用于’deployment’。在哪一个“必填字段”中声明了此内容?
A2:kind

Q3:此部署中的 pod 将由服务公开。在服务配置文件中,目标端口设置为 80。您应该将“containerPort”填入什么?

A3:80

Kubectl

Kubectl 来救场

我们刚刚介绍了如何使用 YAML 配置文件定义集群的所需状态,但目前,这些文件仅以文件形式存在。要将这些文件从配置转换为正在运行的进程,我们需要与集群进行交互。我们可以通过使用不同的方法与控制平面的 apiserver 组件进行交互来实现这一点:如果使用 Kubernetes 仪表板,则使用 UI;如果使用某种脚本,则使用 API;或者使用名为 kubectl 的工具,则使用命令行。此任务重点介绍其中的最后一项。简而言之,kubectl 是 Kubernetes 提供的命令行工具,它允许我们与 Kubernetes 集群的控制平面进行通信,是 DevSecOps 工具库的一个很好的补充。此任务将向您展示如何获取上一个任务中概述的配置文件并应用它们,并为您提供一些此工具的新兵训练营培训,教您一些基本命令,以便您可以像在后花园一样浏览 Kubernetes 集群!

Kubectl apply

在 YAML 文件中定义部署和服务配置后,下一步就是应用它们,以便 Kubernetes 可以采用所需的配置并将其转变为正在运行的进程。这是使用恰当命名的 apply 命令完成的。例如,如果应用上一个任务中提到的服务 YAML,它将如下所示:

kubectl apply -f example-deployment.yaml

Kubectl get

一旦应用了两种配置,您将需要检查两者的状态以确保一切按预期运行。这可以使用 Kubectl get 命令完成。这是一个非常通用的命令,您在使用 Kubernetes 时会经常使用它。get 命令可用于检查资源的状态。资源类型将跟在“get”后面,然后是“-n”或“–namespace”,后面是命名空间(除非您正在检查集群级资源,如节点)。例如,要检查部署的状态,您可以使用:

kubectl get pods -n example-namespace

此命令的输出将类似于以下内容:

​ Terminal

           user@tryhackme$ kubectl get pods -n example-namespace
NAME READY STATUS RESTARTS AGE
example-pod 1/1 Running 0 2m18s

如上所述,此命令可用于检查各种资源,例如部署、服务、pod 和 ReplicaSet。

Kubectl describe

此命令可用于显示资源(或一组资源)的详细信息。这些详细信息有助于故障排除或分析情况。例如,假设集群中的一个 Pod 开始出错,并且您想要获取有关该 Pod 的更多信息以尝试确定其崩溃的原因。您可以运行以下命令:

kubectl describe pod example-pod -n example-namespace

这将返回有关出错 pod 的一些详细信息。例如:

​ Terminal

           user@tryhackme$ kubectl describe pod example-pod -n example-namespace

Name: example-pod
Namespace: example-namespace
Priority: 0
Service Account: default
Node: minikube/192.168.49.2
Start Time: Mon, 22 Jan 2024 14:01:14 +0000
Labels: <none>
Annotations: <none>
Status: Running
IP: 10.244.0.21
...
...

您可以在此处看到 describe 命令详细信息包含一些“事件”。这些事件可以帮助阐明问题所在。有关此事件的更多详细信息,Kubernetes 会从正在运行的 pod 中的每个容器中捕获日志。可以使用我们的下一个命令查看这些日志。

Kubectl logs

假设您想要查看出错 pod 的应用程序日志。也许您想要查看围绕事件错误的日志。为此,我们将使用 kubectl logs 命令。以下是如何使用这个命令的示例:

kubectl logs example-pod -n example-namespace

这些日志不仅可以为故障排除提供有价值的见解,还可以为安全分析提供有价值的见解。

Kubectl exec

现在,假设日志信息很有帮助,但仍有一些未解答的问题,并且您想要深入挖掘并访问容器的 shell。kubectl exec 命令将允许您进入容器并执行此操作!如果 pod 有多个容器,您可以使用 -c--container 标志指定容器。此命令为(-it 标志以交互模式运行命令,-- 之后的所有内容都将在容器内运行):

kubectl exec -it example-pod -n example-namespace -- sh

从这里,您可以在容器内部运行任何您想要的命令。也许您想窥探一下您的安全分析,或者运行命令来测试容器的连接性。

Kubectl port-forward

另一个方便的命令是 kubectl port-forward。此命令允许您在本地计算机和集群中正在运行的 pod 之间创建安全隧道。这可能有用的一个例子是测试应用程序时。假设我们有一个 nginx Web 应用程序在 3 个由 Web 应用程序服务公开的 pod 中运行。如果您还记得,服务使 pod 可从外部访问。我们获取用于公开这些 pod 的端口并将其映射到我们的本地端口之一。例如,将目标端口(服务公开的端口,在我们的配置示例中为 8080)与本地端口 8090 匹配将使此 Web 应用程序可通过“http://localhost:8090”在我们的本地计算机上访问。指定的资源是“resource-type/resource-name”。这将使用 kubectl port-forward 命令完成,语法如下:

kubectl port-forward service/example-service 8090:8080

Kubernetes & DevSecOps

好吧,现在你已经对 Kubernetes 非常熟悉了;它不再是熟人,而更像是朋友。你已经了解了该工具的来龙去脉,很快就会有机会亲手试用它,但在此之前,让我们先看看作为一名 DevSecOps 工程师,你将如何使用这个工具。毕竟,这很可能就是你来这里的原因!

Kubernetes 和安全

在我们深入探讨 DevSecOps 工程师在 Kubernetes 集群中负责什么之前,让我们从安全的角度来考虑一下 Kubernetes,以提供一些背景信息。相对而言,Kubernetes 是一个新兴技术。也就是说,它是一种新兴技术。它虽然新兴,但非常受欢迎,许多公司都在采用 Kubernetes,尤其是年轻的科技和初创公司。

将任何工具引入系统都被视为增加安全风险,因为新工具意味着进入该系统的新潜在途径。当使用 Kubernetes 这样的工具时,这些风险会被放大,因为您拥有一个可以相互通信的 Pod 网络。默认设置允许任何 Pod 与另一个 Pod 通信。这意味着需要考虑各种安全问题。作为 DevSecOps 工程师,您有责任确保这些渠道是安全的。

Kubernetes 强化

容器强化是 DevSecOps 工程师保护这些渠道的一种方式,我们将在本模块后面的 容器强化 房间中深入探讨这一点。它是使用容器扫描工具检测集群中存在的 CVE 并对其进行修复以确保将安全漏洞风险降至最低的过程。

Kubernetes 强化正是通过按照您作为 DevSecOps 工程师将执行的最佳容器安全实践来强化集群,从而确保这些渠道是安全的。各种公司和政府机构都定义了这些最佳实践;让我们来看看我们可以加强容器安全性的每个领域以及如何做到这一点。

保护您的 Pod!

让我们从几种保护 Pod 本身的方法开始。一些 Pod 安全性的最佳实践包括:

  • 运行应用程序的容器不应具有 root 权限
  • 容器应具有不可变的文件系统,这意味着它们不能被更改或添加(根据容器的用途,这可能是不可能的)
  • 应经常扫描容器镜像以查找漏洞或错误配置
  • 应防止特权容器
  • Pod 安全标准Pod 安全准入

强化和分离您的网络!

在这项任务的介绍中,有一件事特别被标记为一个巨大的安全风险:通信。该通信通过网络进行,作为 DevSecOps 工程师,您的工作就是确保此通信的安全。可以使用以下最佳实践来实现这一点:

  • 应使用防火墙和隔离网络中的基于角色的访问控制来限制对控制平面节点的访问
  • 控制平面组件应使用传输层安全性 (TLS) 证书进行通信
  • 应创建显式拒绝策略
  • 凭据和敏感信息不应以纯文本形式存储在配置文件中。相反,它们应该被加密并存储在 Kubernetes 机密中

最佳地使用身份验证和授权

如果我们不谈论身份验证和授权,那就不是安全课程了!Kubernetes 也不例外。以下是一些最佳实践,可帮助您确保高效使用 Kubernetes 身份验证和授权功能:

  • 应禁用匿名访问
  • 应使用强用户身份验证
  • 应为使用集群的各个团队和所使用的服务帐户创建 RBAC 策略

保持警惕

如果您不知道 Kubernetes 集群中发生了什么,您就无法安心地知道 Kubernetes 集群是安全的。以下是一些日志记录最佳实践,可确保您确切了解集群中发生了什么,并能在威胁出现时检测到它们:

  • 应启用审计日志记录
  • 应实施日志监控和警报系统

安全永不停歇

这绝不是对不眠生活方式的认可;事实上,DevSecOps 工程师确实需要睡觉!安全是一回事,保持安全是另一回事。以下是一些确保您的集群保持安全状态的最佳实践:

  • 应快速应用安全补丁和更新
  • 应定期进行漏洞扫描和渗透测试
  • 应删除集群中任何过时的组件

Kubernetes 安全最佳实践在行动

以上信息告诉我们,有很多方法可以强化 Kubernetes基础设施。事实上,有太多实践,分解每一项实践都会把这个房间变成一本自出版的电子书。有机会在完成一项任务后开始使用 Kubernetes,让我们通过分解其中三个来完成这项工作。

RBAC

Kubernetes 中的 RBAC(基于角色的访问控制)根据定义的角色和权限来规范对 Kubernetes 集群及其资源的访问。这些权限(创建/删除 x 资源等的权限)分配给用户、组或服务帐户。RBAC 是一种确保集群中的资源只能由需要访问它的人访问的好方法。可以使用 YAML 文件(与定义资源相同)配置 RBAC,其中可以通过声明资源类型和动词来定义特定规则。动词是受限制的操作,例如“创建”和“获取”。

机密管理

Kubernetes 机密是用于存储敏感信息(如凭证、OAuth 令牌或 SSH 密钥)的对象。机密是确保敏感数据不泄露并允许更好地控制这些信息使用方式的好方法。机密以 base64 编码字符串的形式存储,默认情况下未加密。为了安全起见,最好配置静态加密。在 Kubernetes 集群中促进安全机密管理的另一种方法是使用 RBAC 配置对机密的最小特权访问。

PSA(Pod 安全准入)和 PSS(Pod 安全标准)

Pod 安全标准用于在命名空间或集群范围级别上定义 3 个级别(特权、基线和受限)的安全策略。这些级别的含义:

  • 特权:这是一项几乎不受限制的策略(允许已知权限升级)
  • 基线:这是一项最低限度限制的策略,将阻止已知权限升级(允许使用默认配置部署 Pod)
  • 受限:此严格限制的策略遵循当前 Pod 强化最佳实践

Pod 安全准入(使用 Pod 安全准入控制器)通过拦截 API 服务器请求并应用这些策略来执行这些 Pod 安全标准。

注意:以前,PSA 和 PSS 的角色是使用 PSP(Pod 安全策略)来实现的;但是,从 Kubernetes v1.25 开始,这些已被删除。如果您偶然发现 PSP 正在进行一些课外 Kubernetes 安全研究,请避免混淆!

如您所见,强化 Kubernetes 集群需要做很多工作,足以让 DevSecOps 工程师忙个不停。这些是 Kubernetes 强化中涉及的一些最佳实践。 DevSecOps 工程师在遵循这些实践并利用自动安全检查等技术的同时,可以确保他们的 Kubernetes 环境免受网络威胁。

Hands-on with Kubernetes

第一阶段:探索

好的,我们开始吧?我们要做的第一件事是启动我们的 Minikube 集群。您可以通过在终端中运行以下命令来执行此操作(集群将需要几分钟才能启动):

minikube start

一旦集群启动,您就可以开始了!本指南将带您了解该集群,巩固课堂上教授的知识。话虽如此,您有一个 Kubernetes 集群可供使用,因此请随意探索集群并尝试您迄今为止学到的不同命令!

让我们看看哪些已经在运行,好吗?我们可以使用以下命令执行此操作(所有命名空间均使用 -A

kubectl get pods -A

运行此命令后,您应该会看到一些正在运行的 Pod。这些是首次启动 Kubernetes 集群时默认存在的 Pod(您可能认识其中一些名称,因为它们代表一些控制平面和工作节点进程)。令人兴奋的东西!但是,我们如何通过添加部署和服务使其更令人兴奋?在虚拟机上,您将能够在此处找到一些配置 YAML 文件:

~/Desktop/configuration

这里有两个我们感兴趣的配置 YAML:nginx-deployment.yamlnginx-service.yaml。使用 cat 命令检查这些配置文件:

cat filename 

让我们分析一下每个文件中的内容:

nginx-deployment.yaml

这是一个相对简单的部署;我们可以看到所需状态已定义为单个副本 pod,其中将是一个运行 nginx 映像的容器。从其他行中,我们可以确定此 pod 将用于运行某种 Web 应用程序。

nginx-service.yaml

这又是一个在此处定义的简单 nginx NodePort 服务。眼尖的您可能已经注意到,“selector: -> app: ”字段与部署中定义的 app 标签匹配,并且“targetPort”与部署中概述的“containerPort”匹配。此服务公开部署控制的 pod 中运行的 Web 应用程序。要应用这些 YAML 文件中概述的配置,我们使用 kubectl apply 命令。请记住先应用服务文件!如下所示:

kubectl apply -f nginx-service.yaml
kubectl apply -f nginx-deployment.yaml

使用以下命令验证副本 pod 是否正在运行(不提供任何命名空间标志将获取默认命名空间中的所有 pod,这也是我们的 pod 应该所在的位置):

kubectl get pods -A

现在您应该会看到一个名称中带有“nginx-deployment”的 pod!我们的部署已启动!

第二阶段:交互

好的,现在我们有一个在 pod 中运行的 Web 应用程序,该应用程序由服务公开。如果您回想一下 nginx-service.yaml,该服务使用目标端口 80(容器公开的端口)连接到 Web 应用程序。但是,服务的端口本身设置为 8080。我们想要访问此服务。我们将使用 kubectl port-forward 命令来执行此操作,该命令允许我们将本地主机上的端口转发到 Kubernetes 服务端口(8080)。以下是执行此操作的完整命令:

kubectl port-forward service/nginx-service 8090:8080

运行此命令后,打开 Web 浏览器(Firefox)并通过以下地址访问 Web 应用程序:

http://localhost:8090/

看起来像一个简单的登录终端,但它需要凭证。为什么我们没有看到集群上是否有任何可以帮助我们登录终端的 Kubernetes 机密?打开另一个终端窗口(以便前一个窗口继续端口转发)并运行以下命令以查看是否有任何机密(在默认命名空间中):

kubectl get secrets

啊,有!“terminal-creds”听起来像是我们找到了赢家!使用 kubectl describe 命令,我们可以获取有关此机密的更多详细信息,以查看此处存储的内容:

kubectl describe secret terminal-creds

在描述中,我们可以看到存储了两部分“数据”:用户名和密码。虽然 Kubernetes 机密纯文本形式存储且默认情况下未加密,但它们是 base64 编码的,因此我们通过管道传输此命令并对输出进行 base64 解码以获取纯文本。要访问此数据,我们可以使用以下命令:

要获取用户名,请运行:

kubectl get secret terminal-creds -o jsonpath='{.data.username}'| base64 --decode

要获取密码,请运行:

kubectl get secret terminal-creds -o jsonpath='{.data.password}'| base64 --decode

使用这些凭据访问登录终端。成功了!我们成功了,您已经检索到了标志!

奖励任务:对于那些好奇心强的人,您可以使用另一种方法来获取此标志。这将需要您进行一些 Kubernetes 调查,但第一个线索位于 nginx-deployment.yaml 中!

第三阶段:安全

我们可以使用存储在 Kubernetes 机密中的凭据访问终端,这很棒,但作为 DevSecOps 工程师,我们应该在这里敲响警钟。是时候开始使用一些 Kubernetes 机密管理了。由于这些凭据是敏感信息,我们希望限制对存储它们的 Kubernetes 机密的访问。我们可以通过配置 RBAC(基于角色的访问控制)来实现这一点。

首先,让我们决定谁可以访问这个机密。您的 DevSecOps 经理建议您限制对服务帐户的访问,服务帐户本质上是 pod 可以假设与 Kubernetes API/集群交互的身份。通过这样做,我们甚至可以设置它,以便将来我们的日常终端任务可以由 pod 中的应用程序运行。让我们使用 kubectl create serviceaccount(可以缩写为“sa”)命令创建两个服务帐户,“terminal-user”用于非管理员终端活动(不应有权访问机密),“terminal-admin”用于管理员终端活动(应该有权访问机密)。运行这两个命令来创建这些服务帐户:

kubectl create sa terminal-user
kubectl create sa terminal-admin

创建这些服务帐户后,就该限制对“terminal-creds”机密的访问,以便只有“terminal-admin”服务帐户可以访问它。我们将通过定义和应用两个配置来实现这一点。首先,定义角色及其功能的角色 YAML(获取“terminal-creds”机密)。然后,将我们定义的角色绑定到“terminal admin”服务帐户的角色绑定 YAML。导航到以下目录并 cat 这两个 YAML 以检查它们是如何定义的:

~/Desktop/configuration/rbac

role.yaml

在这里,您可以看到我们定义了一个名为“secret-admin”的角色。在规则部分,我们定义了这个角色可以做什么。我们定义了资源(secrets)、受限制的动词(我们限制了“get”动词,但您可以限制其他动词)以及最后,我们的秘密的名称(terminal-creds)。

role-binding.yaml

在此 YAML 中,我们将“terminal-admin”服务帐户(在“subjects”部分中)与上面定义的“secret-admin”角色(在 roleRef 部分中)绑定。

现在让我们以与应用部署和服务相同的方式应用这些配置(使用 kubectl apply):

kubectl apply -f role.yaml
kubectl apply -f role-binding.yaml

现在您已为该 Kubernetes 机密配置了 RBAC!剩下要做的就是测试我们的 RBAC 是否正常工作。我们可以使用 kubectl auth 命令执行此操作,该命令告诉我们服务帐户是否具有足够的权限来执行特定操作。让我们首先验证常规“终端用户”服务帐户是否无法访问机密:

kubectl auth can-i get secret/terminal-creds --as=system:serviceaccount:default:terminal-user

看起来就像我们预期的那样。这个“否”响应确认此服务帐户不再能够访问终端凭据机密。现在,最后,让我们验证我们的“终端管理员”服务帐户是否可以访问它:

kubectl auth can-i get secret/terminal-creds --as=system:serviceaccount:default:terminal-admin

通过此“是”输出,您已确认 RBAC 已到位并履行了您作为 DevSecOps 工程师的职责,强化了集群并迈出了强化此集群的良好第一步。希望您喜欢参观这个 Kubernetes 集群并了解基础知识。下次见!