在本章中,您将了解 Kubernetes 安全性的一些关键部分。我们将讨论一些最近的 Kubernetes 安全问题,以及最近对 Kubernetes 进行审计的结果。然后,我们将研究在集群的每个级别实现安全性,从 Kubernetes 资源及其配置的安全性开始,然后是容器安全性,最后是带有入侵检测的运行时安全性。首先,我们将讨论一些与 Kubernetes 相关的关键安全概念。
在本章中,我们将涵盖以下主题:
- 了解 Kubernetes 的安全性
- 审查 Kubernetes 的简历和安全审计
- 实现集群配置和容器安全的工具
- 在 Kubernetes 上处理入侵检测、运行时安全性和合规性
为了运行本章中详细介绍的命令,您将需要一台支持kubectl
命令行工具的计算机,以及一个工作正常的 Kubernetes 集群。参见 第一章与 Kubernetes通讯,了解几种快速与 Kubernetes 一起起床跑步的方法,以及如何安装kubectl
工具的说明。
此外,您将需要一台支持 Helm CLI 工具的机器,该工具通常具有与kubectl
相同的先决条件–有关详细信息,请查看位于https://helm.sh/docs/intro/install/的 Helm 文档。
本章使用的代码可以在本书的 GitHub 资源库中找到https://GitHub . com/PacktPublishing/Cloud-Native-with-Kubernetes/tree/master/chapter 12。
在 Kubernetes 上讨论安全时,注意安全边界和责任分担是非常重要的。共享责任模型是一个常用术语,用于描述公共云服务中如何处理安全性。它规定,客户负责其应用的安全性,以及其公共云组件和服务配置的安全性。另一方面,公共云提供商负责服务本身及其运行的基础设施的安全,一直到数据中心和物理层。
同样,Kubernetes 上的安全性也是共享的。尽管上游的 Kubernetes 不是商业产品,但来自大型科技公司的数以千计的 Kubernetes 贡献者和重要的组织影响力确保了 Kubernetes 组件的安全性得到维护。此外,由个人贡献者和使用该技术的公司组成的大型生态系统确保了随着简历的报告和处理,它会变得更好。不幸的是,正如我们将在下一节中讨论的,Kubernetes 的复杂性意味着有许多可能的攻击媒介。
应用共享责任模型然后,作为开发人员,您负责如何配置 Kubernetes 组件的安全性、在 Kubernetes 上运行的应用的安全性以及集群配置中的访问级别安全性。虽然您的应用和容器本身的安全性不在本书的讨论范围之内,但是它们对于 Kubernetes 的安全性来说绝对是重要的。我们将花大部分时间讨论配置级安全性、访问安全性和运行时安全性。
无论是 Kubernetes 本身还是 Kubernetes 生态系统都提供了工具、库和成熟的产品来处理这些级别的安全性,我们将在本章中回顾其中的一些选项。
现在,在我们讨论这些解决方案之前,最好首先对为什么可能需要它们有一个基本的了解。让我们进入下一部分,我们将详细介绍 Kubernetes 在安全领域遇到的一些问题。
Kubernetes 在其传奇历史中遇到了多个常见漏洞和暴露 ( CVEs )。在撰写本文时,MITRE CVE 数据库在搜索kubernetes
时列出了 2015 年至 2020 年的 73 份 CVE 公告。其中的每一个都直接与 Kubernetes 相关,或者与运行在 Kubernetes 上的公共开源解决方案相关(例如,像 NGINX 入口控制器)。
其中有几个非常关键,需要对 Kubernetes 源代码进行修复,因此它们在 CVE 描述中列出了受影响的版本。所有与 Kubernetes 相关的简历的完整列表可以在https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=kubernetes找到。为了让您了解已经发现的一些问题,让我们按时间顺序回顾一下这些简历。
这个 CVE 是 Kubernetes 生产的第一批主要安全问题之一。国家脆弱性数据库(NIST 的一个网站)给这个问题的基本评分为 7.7,将其归入高影响类别。
有了这个问题,Kubernetes 准入控制器将无法确保kubectl patch
命令遵循准入规则,从而允许用户完全绕过准入控制器——这在多租户场景中是一个噩梦。
这个 CVE 可能是 Kubernetes 项目迄今为止最关键的一个。事实上,NVD 给了它一个 9.8 的临界分数!在这个 CVE,人们发现在 Kubernetes 的某些版本中,可以利用来自 Kubernetes API 服务器的错误响应,然后升级连接。一旦连接升级,就可以向集群中的任何后端服务器发送经过身份验证的请求。这使得恶意用户可以在没有适当凭据的情况下模拟完全经过身份验证的 TLS 请求。
除了这些简历(可能部分由这些简历驱动),CNCF 还在 2019 年赞助了对 Kubernetes 的第三方安全审计。审核的结果是开源的是公开的,值得审核。
上一节我们提到,2019 年 Kubernetes 安全审计是由第三方进行的,审计结果完全开源。包含所有部分的完整审计报告可在https://www . cncf . io/blog/2019/08/06/open-sourcing-the-kubernetes-security-audit/上找到。
总的来说,此次审计主要关注以下 Kubernetes 功能:
kube-apiserver
etcd
kube-scheduler
kube-controller-manager
cloud-controller-manager
kubelet
kube-proxy
- 容器运行时
这样做的目的是为了在安全性方面关注 Kubernetes 最重要和最相关的部分。审计结果不仅包括完整的安全报告,还包括威胁模型和渗透测试,以及白皮书。
深入研究审计结果不在本书的讨论范围之内,但是有一些主要的收获,这些收获是了解许多最大的 Kubernetes 安全问题的关键。
简而言之,审计发现,由于 Kubernetes 是一个复杂的、高度网络化的系统,具有许多不同的设置,没有经验的工程师可能会执行许多可能的配置,并且在这样做时,向外部攻击者开放他们的集群。
Kubernetes 非常复杂,不安全的配置很容易发生,这一点值得注意和牢记。
整个审计值得一读——对于那些对网络安全和容器有丰富知识的人来说,这是一个很好的视角,可以看到一些作为 Kubernetes 平台开发的一部分而做出的安全决策。
既然我们已经讨论了在哪里发现了 Kubernetes 安全问题,我们可以开始研究方法来提高集群的安全态势。让我们从一些默认的 Kubernetes 安全功能开始。
Kubernetes 为集群配置和容器权限的安全性提供了许多内置选项。由于我们已经讨论了 RBAC、TLS 入口和加密的 Kubernetes Secrets,让我们讨论几个我们还没有时间回顾的概念:准入控制器、Pod 安全策略和网络策略。
入场控制是一个经常被忽视但极其重要的 Kubernetes 特性。Kubernetes 的许多高级功能都在引擎盖下使用入场控制器。此外,您可以创建新的准入控制器规则,以便为集群添加自定义功能。
入场控制员一般有两种类型:
- 变异准入控制者
- 验证准入控制器
变异的接纳控制器接受 Kubernetes 资源规范并返回更新的资源规范。他们还执行副作用计算或进行外部调用(在定制准入控制器的情况下)。
另一方面,验证准入控制器只是接受或拒绝 Kubernetes 资源 API 请求。重要的是要知道,这两种类型的控制器只对创建、更新、删除或代理请求起作用。这些控制器不能改变或改变列出资源的请求。
当这些类型之一的请求进入 Kubernetes API 服务器时,它将首先通过所有相关的变异准入控制器运行该请求。然后,在最终被应用编程接口服务器执行之前(或者,如果调用被准入控制器拒绝,则不执行),可能会发生变化的输出将通过验证准入控制器。
从结构上来说,Kubernetes 提供的准入控制器是功能或“插件”,作为 Kubernetes API 服务器的一部分运行。它们依赖于两个网络钩子控制器(它们本身就是准入控制器,只是特殊的控制器):突变管理网络钩子和验证注册管理网络钩子。所有其他入场控制人员根据他们的类型,在引擎盖下使用其中一个挂钩。此外,您编写的任何自定义准入控制器都可以连接到这些网络挂钩中的任何一个。
在我们看创建自定义接纳控制器的过程之前,让我们回顾一下 Kubernetes 提供的一些默认接纳控制器。完整列表请查看上的 Kubernetes 官方文档 https://Kubernetes . io/docs/reference/access-authn-authz/入场控制员/#每个入场控制员做什么。
在典型的 Kubernetes 设置中有相当多的默认准入控制器——其中许多是一些相当重要的基本功能所需要的。这里有一些默认准入控制器的例子。
名称空间存在许可 T2 控制器检查任何传入的 Kubernetes 资源(名称空间本身除外)。这是为了检查附加到资源的命名空间是否存在。如果不是,它在准入控制器级别拒绝资源请求。
PodSecurityPolicy 准入控制器支持 Kubernetes Pod 安全策略,我们将马上了解这些策略。此控制器防止创建不遵循 Pod 安全策略的资源。
除了默认的接纳控制器,我们还可以创建自定义的接纳控制器。
创建自定义准入控制器可以使用两个 webhook 控制器之一动态完成。其工作方式如下:
- 您必须编写自己的服务器或脚本,单独运行到 Kubernetes API 服务器。
- 然后,配置前面提到的两个 webhook 触发器中的一个,用资源数据向您的自定义服务器控制器发出请求。
- 基于这个结果,webhook 控制器将告诉 API 服务器是否继续。
让我们从第一步开始:编写一个快速入场服务器。
为了创建我们的定制准入控制器服务器(它将接受来自 Kubernetes 控制平面的网络钩子),我们可以使用任何编程语言。与 Kubernetes 的大多数扩展一样,Go 拥有最好的支持和库,使得编写自定义准入控制器的任务变得更加容易。现在,我们将使用一些伪代码。
我们服务器的控制流程如下所示:
准入控制服务器
// This function is called when a request hits the
// "/mutate" endpoint
function acceptAdmissionWebhookRequest(req)
{
// First, we need to validate the incoming req
// This function will check if the request is formatted properly
// and will add a "valid" attribute If so
// The webhook will be a POST request from Kubernetes in the
// "AdmissionReviewRequest" schema
req = validateRequest(req);
// If the request isn't valid, return an Error
if(!req.valid) return Error;
// Next, we need to decide whether to accept or deny the Admission
// Request. This function will add the "accepted" attribute
req = decideAcceptOrDeny(req);
if(!req.accepted) return Error;
// Now that we know we want to allow this resource, we need to
// decide if any "patches" or changes are necessary
patch = patchResourceFromWebhook(req);
// Finally, we create an AdmissionReviewResponse and pass it back
// to Kubernetes in the response
// This AdmissionReviewResponse includes the patches and
// whether the resource is accepted.
admitReviewResp = createAdmitReviewResp(req, patch);
return admitReviewResp;
}
现在我们有了一个简单的服务器作为我们定制的准入控制器,我们可以配置一个 Kubernetes 准入网络钩子来调用它。
为了告诉 Kubernetes 呼叫我们的定制入场服务器,它需要一个可以呼叫的地方。我们可以在任何地方运行我们的定制入场控制器——它不需要在 Kubernetes 上。
也就是说,本章的目的是在 Kubernetes 上运行它。我们不会查看完整的清单,但是让我们假设我们有一个服务和一个它所指向的部署,运行一个作为我们的服务器的容器。该服务看起来像这样:
服务-web book . YAML
apiVersion: v1
kind: Service
metadata:
name: my-custom-webhook-server
spec:
selector:
app: my-custom-webhook-server
ports:
- port: 443
targetPort: 8443
需要注意的是,我们的服务器需要使用 HTTPS,这样 Kubernetes 才能接受 webhook 响应。配置这一点有很多方法,我们不会在本书中详细介绍。证书可以自签名,但是证书和 CA 的通用名称需要与设置 Kubernetes 集群时使用的名称相匹配。
现在我们已经运行了服务器并接受了 HTTPS 请求,让我们告诉 Kubernetes 在哪里可以找到它。为此,我们使用MutatingWebhookConfiguration
。
下面的代码块显示了MutatingWebhookConfiguration
的一个例子:
突变-webhook-config-service.yaml
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: my-service-webhook
webhooks:
- name: my-custom-webhook-server.default.svc
rules:
- operations: [ "CREATE" ]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods", "deployments", "configmaps"]
clientConfig:
service:
name: my-custom-webhook-server
namespace: default
path: "/mutate"
caBundle: ${CA_PEM_B64}
让我们为我们的MutatingWebhookConfiguration
挑选出 YAML。正如您所看到的,我们可以在这个配置中配置多个 webhook 尽管在这个例子中我们只做了一个。
对于每个网钩,我们设置name
、rules
和一个configuration
。name
只是网络钩子的标识符。rules
允许我们精确地配置在哪些情况下 Kubernetes 应该向我们的准入控制发出请求。在这种情况下,我们已经将我们的网络钩子配置为每当类型为pods
、deployments
和configmaps
的资源发生CREATE
事件时触发。
最后,我们有clientConfig
,在这里我们确切地指定了 Kubernetes 应该在哪里以及如何发出 webhook 请求。由于我们在 Kubernetes 上运行我们的定制服务器,除了在我们的服务器上命中的路径(这里"/mutate"
是最佳实践)以及要与 HTTPS 终止证书进行比较的集群的证书颁发机构之外,我们还像前面的 YAML 那样指定了服务名称。如果您的自定义准入服务器在其他地方运行,还有其他可能的配置字段–如果需要,请查看文档。
一旦我们在 Kubernetes 中创建了MutatingWebhookConfiguration
,就很容易测试验证了。我们所需要做的就是像平常一样创建一个 Pod、Deployment 或 ConfigMap,并根据我们服务器中的逻辑检查我们的请求是否被拒绝或打补丁。
现在让我们假设我们的服务器设置为拒绝任何名称包含字符串deny-me
的 Pod。还设置为向AdmissionReviewResponse
添加错误响应。
让我们按如下方式使用 Pod 规范:
拒绝 pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-pod-to-deny
spec:
containers:
- name: nginx
image: nginx
现在,我们可以创建我们的 Pod 来检查准入控制器。我们可以使用以下命令:
kubectl create -f to-deny-pod.yaml
这将产生以下输出:
Error from server (InternalError): error when creating "to-deny-pod.yaml": Internal error occurred: admission webhook "my-custom-webhook-server.default.svc" denied the request: Pod name contains "to-deny"!
就这样!我们的自定义准入控制器已成功拒绝一个与我们在服务器中指定的条件不匹配的 Pod。对于打补丁的资源(不是拒绝,而是更改),kubectl
不会显示任何特殊响应。您需要获取有问题的资源来查看正在运行的补丁。
既然我们已经探索了自定义准入控制器,那么让我们看看强制实施集群安全实践的另一种方式——Pod 安全策略。
Pods 安全策略的基础是,它们允许集群管理员创建 Pods 必须遵循的规则,以便安排到节点上。从技术上讲,Pod 安全策略只是另一种类型的准入控制器。但是,这个特性得到了 Kubernetes 的官方支持,值得深入讨论,因为有很多选项可供选择。
Pod 安全策略可用于防止 Pods 以 root 用户身份运行、限制使用的端口和卷、限制权限升级等等。我们现在将回顾 Pod 安全策略功能的一个子集,但是要查看 Pod 安全策略配置类型的完整列表,请查看官方 PSP 文档,网址为https://kubernetes . io/docs/concepts/policy/Pod-security-policy/。
最后,Kubernetes 还支持控制容器权限的低级原语,即apparemor、 SELinux 和 Seccomp 。这些配置不在本书的讨论范围内,但是它们对于高度安全的环境非常有用。
实施 Pod 安全策略有几个步骤:
- 首先,必须启用 Pod 安全策略准入控制器。
- 这将阻止在您的群集中创建所有 Pod,因为它需要匹配的 Pod 安全策略和角色才能创建 Pod。因此,您可能希望在启用准入控制器之前创建您的 Pod 安全策略和角色。
- 启用准入控制器后,必须创建策略本身。
- 然后,必须创建具有 Pod 安全策略访问权限的
Role
或ClusterRole
对象。 - 最后,该角色可以与集群角色绑定或角色绑定绑定到用户或服务
accountService
帐户,允许使用该服务帐户创建的 Pod 使用 Pod 安全策略可用的权限。
在某些情况下,您的集群上可能没有默认启用 Pod 安全策略准入控制器。让我们看看如何启用它。
为了启用 PSP 准入控制器,kube-apiserver
必须以指定准入控制器开始的标志开始。在托管 Kubernetes (EKS、AKS 和其他)上,PSP 准入控制器可能会默认启用,同时还会创建一个权限 Pod 安全策略供初始管理员用户使用。这可以防止 PSP 在新集群中创建 Pods 时出现任何问题。
如果您正在自我管理 Kubernetes,并且尚未启用 PSP 准入控制器,您可以通过使用以下标志重新启动kube-apiserver
组件来实现:
kube-apiserver --enable-admission-plugins=PodSecurityPolicy,ServiceAccount…<all other desired admission controllers>
如果你的库本尼斯应用编程接口服务器是使用一个systemd
文件运行的(就像跟随库本尼斯:艰难之路一样),你应该更新那里的标志。通常,systemd
文件放在/etc/systemd/system/
文件夹中。
为了找出哪些许可插件已经启用,您可以运行以下命令:
kube-apiserver -h | grep enable-admission-plugins
此命令将显示一长串已启用的准入插件。例如,您将在输出中看到以下准入插件:
NamespaceLifecycle, LimitRanger, ServiceAccount…
现在我们确定 PSP 准入控制器已启用,我们实际上可以创建一个 PSP。
Pod 安全策略本身可以使用典型的 Kubernetes 资源 YAML 创建。这是一份 YAML 的权限 Pod 安全策略文件:
权限-psp.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: privileged-psp
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
privileged: true
allowedCapabilities:
- '*'
volumes:
- '*'
hostNetwork: true
hostPorts:
- min: 2000
max: 65535
hostIPC: true
hostPID: true
allowPrivilegeEscalation: true
runAsUser:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
该 Pod 安全策略允许用户或服务帐户(通过角色绑定或集群角色绑定)创建具有权限功能的 Pod。例如,使用此PodSecurityPolicy
的 Pod 将能够绑定到端口2000
- 65535
上的主机网络,以任何用户身份运行,并绑定到任何卷类型。此外,我们还有一个关于allowedProfileNames
的seccomp
限制的注释—让您了解Seccomp
和AppArmor
注释如何与PodSecurityPolicies
一起工作。
正如我们之前提到的,仅仅创建 PSP 没有任何作用。对于将要创建权限 Pod 的任何服务帐户或用户,我们需要通过角色和角色绑定(或ClusterRole
和ClusterRoleBinding
)授予他们访问 Pod 安全策略的权限。
为了创建一个可以访问这个 PSP 的ClusterRole
,我们可以使用下面的 YAML:
Privileged-clusterrole.yaml
apiVersion: rbac.authorization.k8s.io
kind: ClusterRole
metadata:
name: privileged-role
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames:
- privileged-psp
现在,我们可以将新创建的ClusterRole
绑定到我们打算用来创建权限 Pods 的用户或服务帐户。让我们用一个ClusterRoleBinding
来完成:
privileged-cluster role binding . YAML
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: privileged-crb
roleRef:
kind: ClusterRole
name: privileged-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:authenticated
在我们的例子中,我们希望让集群中每个经过身份验证的用户创建权限 Pods,因此我们绑定到system:authenticated
组。
现在,我们可能不希望我们的所有用户或 Pods 都享有权限。更现实的 Pod 安全政策对 Pod 的功能进行了限制。
让我们看一下有这些限制的 PSP 的一些例子 YAML:
非权限-psp.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: unprivileged-psp
spec:
privileged: false
allowPrivilegeEscalation: false
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAsNonRoot'
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
readOnlyRootFilesystem: false
可以看出,这种 Pod 安全策略在对创建的 Pod 施加的限制方面有很大的不同。此策略下的任何 Pods 都不允许作为 root 用户运行或升级到 root 用户。它们对可以绑定到的卷类型也有限制(这一部分在前面的代码片段中已经突出显示了),并且它们不能使用主机网络或直接绑定到主机端口。
在这个 YAML 中,runAsUser
和supplementalGroups
部分都控制可以运行或由容器添加的 Linux 用户标识和组标识,而fsGroup
键控制可以由容器使用的文件系统组。
除了使用像MustRunAsNonRoot
这样的规则之外,还可以直接指定容器可以使用哪个用户标识运行,并且任何未在其规范中专门使用该标识运行的 Pods 都将无法调度到节点上。
有关将用户限制在特定标识的 PSP 示例,请查看以下 YAML:
Specific-user-id-psp.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: specific-user-psp
spec:
privileged: false
allowPrivilegeEscalation: false
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAs'
ranges:
- min: 1
max: 3000
readOnlyRootFilesystem: false
此 Pod 安全策略应用后,将阻止任何 Pod 以用户 ID 0
或3001
或更高的身份运行。为了创建满足这个条件的 Pod ,我们在 Pod 规格中使用securityContext
中的runAs
选项。
以下是一个满足此约束的 Pod 示例,即使有此 Pod 安全策略,也能成功调度:
Specific-user-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: specific-user-pod
spec:
securityContext:
runAsUser: 1000
containers:
- name: test
image: busybox
securityContext:
allowPrivilegeEscalation: false
正如你所看到的,在这个 YAML,我们给我们的 Pod 一个特定的用户来运行,ID 1000
。我们也不允许我们的 Pod 升级到根。即使specific-user-psp
到位,该 Pod 规格也能成功调度。
既然我们已经讨论了 Pod 安全策略如何通过限制 Pod 的运行来保护 Kubernetes,我们可以进入网络策略,在这里我们可以限制 Pod 的网络方式。
Kubernetes 中的网络策略的工作方式类似于防火墙规则或路由表。它们允许用户通过选择器指定一组 Pods,然后确定这些 Pods 可以如何以及在哪里通信。
要使网络策略起作用,您选择的 Kubernetes 网络插件(例如, Weave 、Flannel或 Calico )必须支持网络策略规范。可以像所有其他 Kubernetes 资源一样,通过 YAML 文件创建网络策略。让我们从一个非常简单的网络策略开始。
这里有一个网络策略规范,限制对带有标签app=server
的 Pods 的访问:
标签-限制-策略. yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-network-policy
spec:
podSelector:
matchLabels:
app: server
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 80
现在,让我们把这个 YAML 网络策略分开,因为它将帮助我们解释一些更复杂的网络策略。
首先,在我们的规范中,我们有一个podSelector
,它在功能上类似于节点选择器。在这里,我们使用matchLabels
来指定该网络策略将只影响标签为app=server
的 Pods。
接下来,我们为网络策略指定一个策略类型。有两种策略类型:ingress
和egress
。网络策略可以指定一种或两种类型。ingress
指的是制定对匹配 Pods 的连接生效的网络规则,egress
指的是对离开匹配 Pods 的连接生效的网络规则。
在这个特定的网络策略中,我们只是规定了一个单一的ingress
规则:带有标签app=server
的 Pods 将接受的唯一流量是来自带有标签app:frontend
的 Pods 的流量。此外,唯一能接受带有标签app=server
的 Pods 流量的端口是80
。
一个ingress
策略集中可以有多个from
块对应多个流量规则。同样,有了egress
,可以有多个to
区块。
需要注意的是,网络策略是按名称空间工作的。默认情况下,如果一个命名空间中没有单一的网络策略,则该命名空间中的 Pod 到 Pod 通信不受限制。但是,一旦单个网络策略选择了特定的 Pod,进出该 Pod 的所有流量都必须明确匹配网络策略规则。如果不符合规则,将被阻止。
考虑到这一点,我们可以轻松地创建策略,对 Pod 网络实施广泛的限制。让我们看看下面的网络策略:
完全限制策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: full-restriction-policy
namespace: development
spec:
policyTypes:
- Ingress
- Egress
podSelector: {}
在这个NetworkPolicy
中,我们指定我们将同时包括一个Ingress
和Egress
策略,但是我们不为它们中的任何一个写块。这具有自动拒绝Egress
和Ingress
的任何流量的效果,因为没有流量匹配的规则。
此外,我们的{}
Pod 选择器值对应于选择名称空间中的每个 Pod。该规则的最终结果是development
命名空间中的每个 Pod 都将无法接受入口流量或发送出口流量。
重要说明
还需要注意的是,网络策略是通过组合所有影响 Pod 的独立网络策略来解释的,然后将所有这些规则的组合应用于 Pod 流量。
这意味着,即使我们在前面的示例中已经限制了development
命名空间中的所有入口和出口流量,我们仍然可以通过添加另一个网络策略来为特定的 Pods 启用它。
让我们假设现在我们的development
名称空间对 Pods 有完全的流量限制,我们希望允许 Pods 的一个子集在端口443
上接收网络流量,并在端口6379
上向数据库 Pods 发送流量。为了做到这一点,我们只需要创建一个新的网络策略,通过策略的附加性质,允许这种流量。
这就是网络政策的样子:
覆盖-限制-网络-策略. yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: override-restriction-policy
namespace: development
spec:
podSelector:
matchLabels:
app: server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 443
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 6379
在这个网络策略中,我们允许development
命名空间中的服务器 Pods 从端口443
上的前端 Pods 接收流量,并将流量发送到端口6379
上的数据库 Pods。
相反,如果我们希望在没有任何限制的情况下开放所有 Pod 到 Pod 的通信,同时仍然实际制定网络策略,我们可以通过以下 YAML 来实现:
全开放网络政策
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
ingress:
- {}
policyTypes:
- Egress
- Ingress
现在,我们已经讨论了如何使用网络策略来设置 Pod 到 Pod 流量的规则。但是,也可以将网络策略用作某种面向外部的防火墙。为此,我们创建的网络策略规则不是基于 Pods 作为起点或终点,而是基于外部 IP。
让我们看一个示例网络策略,其中我们限制与 Pod 的通信,以特定 IP 范围为目标:
外部-IP-网络-策略. yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: specific-ip-policy
spec:
podSelector:
matchLabels:
app: worker
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 157.10.0.0/16
except:
- 157.10.1.0/24
egress:
- to:
- ipBlock:
cidr: 157.10.0.0/16
except:
- 157.10.1.0/24
在这个网络策略中,我们指定了单个Ingress
规则和单个Egress
规则。这些规则中的每一条都不是基于流量来自哪个 Pod,而是基于网络请求的源 IP 来接受或拒绝流量。
在我们的例子中,我们已经为我们的Ingress
和Egress
规则选择了/16
子网掩码范围(带有指定的/24
CIDR 例外)。这有一个副作用,那就是阻止我们集群内的任何流量到达这些 Pod,因为我们的 Pod IPs 都不符合默认集群网络设置中的规则。
但是,来自指定子网掩码(且不在例外范围内)中群集外部的流量将能够向worker
Pods 发送流量,也能够接受来自worker
Pods 的流量。
随着我们对网络策略讨论的结束,我们可以进入一个完全不同的安全栈层——运行时安全和入侵检测。
一旦您设置了您的 Pod 安全策略和网络策略,并且通常确保您的配置尽可能地防水,那么在 Kubernetes 中仍然有许多可能的攻击媒介。在本节中,我们将重点讨论来自 Kubernetes 集群的攻击。即使有非常具体的 Pod 安全策略(明确地说,这确实有帮助),在您的集群中运行的容器和应用也有可能执行意外或恶意的操作。
为了解决这个问题,许多专业人员寻求运行时安全工具,它允许对应用进程进行持续的监控和警报。对于 Kubernetes 来说,一个流行的开源工具可以实现这一点是法尔科。
法尔科称自己为 Kubernetes 进程的行为活动监视器。它可以监视运行在 Kubernetes 上的容器化应用以及 Kubernetes 组件本身。
Falco 是如何工作的?实时地,Falco 解析来自 Linux 内核的系统调用。然后,它通过规则过滤这些系统调用,这些规则是可以应用于 Falco 引擎的配置集。每当系统调用违反规则时,Falco 都会触发警报。就这么简单!
Falco 附带了一套广泛的默认规则,在内核级别增加了显著的可观察性。Falco 当然支持自定义规则,我们将向您展示如何编写它们。
但是,首先我们需要在集群上安装 Falco!幸运的是,可以使用 Helm 安装 Falco。但是,非常重要的是要注意,有几种不同的方法来安装 Falco,它们在发生违规时的有效性方面有很大的不同。
我们将使用 Helm 图表安装 Falco,该图表非常简单,适用于托管 Kubernetes 集群,或者您可能无法直接访问工作节点的任何场景。
但是,为了获得最佳的安全状态,应该在 Linux 级别将 Falco 直接安装到 Kubernetes 节点上。使用 DaemonSet 的 Helm 图表非常易于使用,但本质上不如直接安装 Falco 安全。要将 Falco 直接安装到您的节点,请查看https://falco.org/docs/installation/的安装说明。
有了这个警告,我们可以使用 Helm 安装 Falco:
-
First, we need to add the
falcosecurity
repo to our local Helm:helm repo add falcosecurity https://falcosecurity.github.io/charts helm repo update
接下来,我们可以继续使用 Helm 实际安装 Falco。
重要说明
Falco Helm 图表有许多可能的变量,可以在值文件中更改–要全面查看这些变量,您可以查看官方 Helm 图表回购位于https://github.com/falcosecurity/charts/tree/master/falco。
-
要安装 Falco,请运行以下命令:
helm install falco falcosecurity/falco
该命令将使用默认值安装 Falco,您可以在https://github . com/Falcosecurity/charts/blob/master/Falco/values . YAML上看到这些默认值。
接下来,让我们深入了解一下法尔科为具有安全意识的 Kubernetes 管理员提供了什么。
如前所述,Falco 附带了一组默认规则,但是我们可以使用新的 YAML 文件轻松添加更多规则。由于我们使用的是 Helm 版本的 Falco,将自定义规则传递给 Falco 就像创建一个新的值文件或者用自定义规则编辑默认的值文件一样简单。
添加自定义规则如下所示:
自定义猎鹰 yaml
customRules:
my-rules.yaml: |-
Rule1
Rule2
etc...
现在是讨论法尔科规则结构的好时机。为了说明这一点,让我们借用法尔科掌舵图附带的Default
法尔科规则集的几行规则。
在 YAML 指定 Falco 配置时,我们可以使用三种不同类型的键来帮助构建规则。这些是宏、列表和规则本身。
我们在这个例子中看到的具体规则叫做Launch Privileged Container
。该规则将检测权限容器何时被启动,并将关于该容器的一些信息记录到STDOUT
中。在警报方面,规则可以做各种事情,但是当高风险事件发生时,登录STDOUT
是增加可观察性的好方法。
首先,让我们看看规则条目本身。这条规则使用了几个助手条目、几个宏和列表,但是我们马上会用到它们:
- rule: Launch Privileged Container
desc: Detect the initial process started in a privileged container. Exceptions are made for known trusted images.
condition: >
container_started and container
and container.privileged=true
and not falco_privileged_containers
and not user_privileged_containers
output: Privileged container started (user=%user.name command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
priority: INFO
tags: [container, cis, mitre_privilege_escalation, mitre_lateral_movement]
如你所见,法尔科规则有几个部分。首先,我们有规则名称和描述。然后,我们指定规则的触发条件——它充当 Linux 系统调用的过滤器。如果系统调用与condition
块中的所有逻辑过滤器匹配,则触发规则。
当规则被触发时,输出键允许我们设置输出文本的显示格式。priority
键让我们分配优先级,可以是emergency
、alert
、critical
、error
、warning
、notice
、informational
和debug
中的一个。
最后,tags
键将标签应用于有问题的规则,使规则更容易分类。当使用不仅仅是纯文本STDOUT
条目的警报时,这一点尤其重要。
condition
的语法在这里特别重要,我们将重点介绍这个过滤系统是如何工作的。
首先,因为过滤器本质上是逻辑语句,所以您会看到一些熟悉的语法(如果您曾经编程或编写过伪代码的话)——还有,还有,没有,等等。这个语法非常容易学习,关于它的完整讨论——系统挖掘过滤器语法——可以在https://github . com/draios/Sysdig/wiki/Sysdig-用户指南#过滤中找到。
需要注意的是,Falco 开源项目最初是由 Sysdig 创建的,这就是为什么它使用了通用的 Sysdig 过滤器语法。
接下来,您将看到对container_started
和container
以及falco_privileged_containers
和user_privileged_containers
的引用。这些不是普通的字符串,而是宏的使用——对 YAML 其他块的引用,这些块指定了附加功能,并且通常使编写规则变得更加容易,而无需重复大量配置。
为了了解该规则的实际工作原理,让我们来看一下前面规则中引用的所有宏的完整引用:
- macro: container
condition: (container.id != host)
- macro: container_started
condition: >
((evt.type = container or
(evt.type=execve and evt.dir=< and proc.vpid=1)) and
container.image.repository != incomplete)
- macro: user_sensitive_mount_containers
condition: (container.image.repository = docker.io/sysdig/agent)
- macro: falco_privileged_containers
condition: (openshift_image or
user_trusted_containers or
container.image.repository in (trusted_images) or
container.image.repository in (falco_privileged_images) or
container.image.repository startswith istio/proxy_ or
container.image.repository startswith quay.io/sysdig)
- macro: user_privileged_containers
condition: (container.image.repository endswith sysdig/agent)
您将在 YAML 之前的中看到,每个宏实际上只是一个可重用的Sysdig
过滤器语法块,通常使用其他宏来实现规则功能。此处未显示的列表类似于宏,只是它们没有描述过滤器逻辑。相反,它们包含一个字符串值列表,可以作为使用过滤器语法进行比较的一部分。
例如,falco_privileged_containers
宏中的(
trusted_images)
引用了一个名为trusted_images
的列表。以下是该列表的来源:
- list: trusted_images
items: []
正如您所看到的,这个特定的列表在默认规则中是空的,但是一个自定义规则集可以使用这个列表中的一个受信任映像列表,然后这个列表将自动被所有其他使用trusted_image
列表作为其过滤规则一部分的宏和规则使用。
如前所述除了跟踪 Linux 系统调用,Falco 还可以跟踪 Kubernetes 控制平面事件,截止到 Falco v0.13.0。
在结构上,这些 Kubernetes 审计事件规则的工作方式与 Falco 的 Linux 系统调用规则相同。这里有一个 Falco 中默认的 Kubernetes 规则的例子:
- rule: Create Disallowed Pod
desc: >
Detect an attempt to start a pod with a container image outside of a list of allowed images.
condition: kevt and pod and kcreate and not allowed_k8s_containers
output: Pod started with container not in allowed list (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
priority: WARNING
source: k8s_audit
tags: [k8s]
此规则作用于 Falco 中的 Kubernetes 审核事件(本质上是控制平面事件),以在创建不在列表allowed_k8s_containers
上的 Pod 时发出警报。默认的k8s
审核规则包含许多类似的规则,大多数规则在触发时会输出格式化的日志。
现在,我们在本章前面讨论了 Pod 安全策略,您可能会看到 PSPs 和 Falco Kubernetes 审计事件规则之间的一些相似之处。例如,从默认的 Kubernetes Falco 规则中提取这个条目:
- rule: Create HostNetwork Pod
desc: Detect an attempt to start a pod using the host network.
condition: kevt and pod and kcreate and ka.req.pod.host_network intersects (true) and not ka.req.pod.containers.image.repository in (falco_hostnetwork_images)
output: Pod started using host network (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
priority: WARNING
source: k8s_audit
tags: [k8s]
当 Pod 试图开始使用主机网络时触发的规则直接映射到主机网络 PSP 设置。
Falco 利用了这种相似性,让我们使用 Falco 作为一种方法来trial
新的 Pod 安全策略,而不会在集群范围内应用它们,也不会导致运行 Pod 时出现问题。
为此,falcoctl
(Falco 命令行工具)附带了convert psp
命令。该命令接受 Pod 安全策略定义,并将其转换为一组 Falco 规则。这些 Falco 规则在被触发时只会向STDOUT
输出日志(而不是像 PSP 不匹配那样导致 Pod 调度失败),这使得在现有集群中测试新的 Pod 安全策略变得更加容易。
要了解如何使用falcoctl
转换工具,请查看位于https://falco.org/docs/psp-support/的官方 Falco 文档。
现在我们已经对 Falco 工具有了一个很好的基础,让我们讨论如何使用它来实现合规性控制和运行时安全性。
由于其可扩展性和审计低级 Linux 系统调用的能力,Falco 是持续合规性和运行时安全性的绝佳工具。
在合规方面,可以利用专门映射到合规标准(例如 PCI 或 HIPAA)要求的 Falco 规则集。这允许用户快速检测任何不符合所讨论标准的过程并采取行动。有几种标准的开源和闭源 Falco 规则集。
同样,为了运行时安全,Falco 公开了一个警报/事件系统,这意味着任何触发警报的运行时事件也可以触发自动干预和补救过程。这对安全性和合规性都有好处。例如,如果一个 Pod 触发了一个 Falco 违规警报,一个进程可以关闭该警报并立即删除违规的 Pod。
在本章中,我们学习了 Kubernetes 环境中的安全性。首先,我们回顾了 Kubernetes 上的安全基础知识——安全栈的哪些层与我们的集群相关,以及如何管理这种复杂性的一些粗略方法。接下来,我们了解了 Kubernetes 遇到的一些主要安全问题,并讨论了 2019 年安全审计的结果。
然后,我们在 Kubernetes 中的两个不同的栈级别上实现了安全性——首先,在配置中使用 Pod 安全策略和网络策略,最后,使用 Falco 实现运行时安全性。
在下一章中,我们将学习如何通过构建自定义资源使 Kubernetes 成为您自己的。这将允许您向集群添加重要的新功能。
- 自定义准入控制器可以使用的两个 webhook 控制器的名称是什么?
- 一个空白
NetworkPolicy
对于进入有什么影响? - 为了防止攻击者改变 Pod 的功能,什么样的 Kubernetes 控制平面事件是值得跟踪的?