在 Amazon EKS 通过 Kyverno 实现策略即代码

22389559020fb26a39fa2c221f37dc26.png

fa2bd462db0cc5409002ae79402e60c8.gif

点击上方【凌云驭势 重塑未来】

一起共赴年度科技盛宴!

outside_default.png

前  言

outside_default.png

Amazon Elastic Kubernetes Service (  Amazon EKS  )  是一项云上的托管 Kubernetes(K8S)服务,使用该服务您可以轻松地在亚马逊云上部署、管理和扩展容器化的应用程序。

 Amazon EKS:

https://aws.amazon.com/cn/eks/

在越来越多用户采用 Amazon EKS 集群部署运行容器应用时,随着使用的深入,大家会发现,或无意的配置错误,或恶意的容器注入,预期外的更改可能会被应用到集群中,这可能会中断集群的操作或破坏集群的完整性,一个典型的例子就是在集群中创建了不必要的特权容器造成了风险敞口。为了控制Pod安全,Kubernetes 提供了 Pod 安全策略 (PSP)资源。PSP 指定一组安全设置,Pod 在集群中创建或更新之前必须满足这些设置。

Pod 安全策略 (PSP):

https://kubernetes.io/docs/concepts/security/pod-security-policy/

但是,从 Kubernetes 1.21 版开始,PSP 已被弃用,并在 Kubernetes 1.25 版中被彻底删除。Kubernetes 项目记录了 PSP 被弃用的原因。简而言之,PSP 让大多数用户感到困惑。这种混乱导致了许多错误配置;使集群受到过度限制或过度宽松的设置而不受保护。所有这些问题都促使人们需要一种新的、更加用户友好和确定性的 Pod 安全解决方案。

记录:

https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/

在新的Kubernetes设计中,PSP 会被 Pod 安全准入 (PSA) 取代,这是一种内置准入控制器,可实现 Pod 安全标准 (PSS) 中概述的安全控制。PSA 和 PSS 在 Kubernetes 1.23 中都达到了 beta 状态,并在Amazon EKS 1.23版本中默认启用。除PSA和PSS以外,用户也可以通过开源社区的策略即代码(PaC)解决方案用以替换PSP。

Pod 安全准入 (PSA):

https://kubernetes.io/docs/concepts/security/pod-security-admission/

Pod 安全标准 (PSS):

https://kubernetes.io/docs/concepts/security/pod-security-standards/

PSS 作为一种原生的安全实践,它提供了一种简单但强大的安全准入控制,用户只需要进行简单的 annotation 配置即可让这些安全检查生效,防止规定外的资源进入集群。

但 PSS 也有其局限性,PSS 目前仅可以使用内置的3种级别的策略,无法自定义安全策略。企业在使用 EKS 的过程中,会逐渐形成各种规范,从容器的命名,标签,到资源中各种属性字段配置的最佳实践,以及根据企业合规安全方面要求禁止使用的用法等等,这些规范的会形成一组策略(Policy),用于集群控制资源的创建等方面的行为。

出于对控制的日益增长的需求,已经出现了策略即代码 (PaC) 解决方案来满足这些需求。在 Kubernetes 集群中强制执行行为并限制更改范围是客户面临的共同挑战, 使用策略来应用基于规则的 Kubernetes 资源控制是 的一种动态且公认 管理 Kubernetes 配置的方法。启用自动化的策略是 Kubernetes 管理上共同关心的问题。PaC 解决方案是启用 Kubernetes 集群并提供规定和自动化控制的最佳实践。

PaC 解决方案为组织提供了通过代码编写策略规则的能力。通过这种方法,组织可以重用 DevOps 和 GitOps 策略来管理和跨容器集群应用这些策略。

outside_default.png

什么是 Kyverno

outside_default.png

对于 Kubernetes,开源软件 (OSS)社区中提供了多种 PaC 解决方案,例如 OPA(Open Policy Agent),Kyverno 等。其中 OPA 是较成熟的策略引擎方案,但由于 OPA 的策略使用一种“Rego”语言,且不同于已知编程语言语法,虽然 OPA 在除 Kubernetes 外的多数领域都有着广泛应用,但仍然有一定的学习曲线。

Rego:

https://www.openpolicyagent.org/docs/latest/policy-language/

而 Kyverno 作为一种专用于 Kubernetes 的策略引擎,其最大的特点是其策略语言完全沿用 Kubernetes 的 manifest YAML 文件的写法,熟悉编写 Kubernetes YAML 的开发运维人员可快速上手编写所需的策略。因此,本文将使用 Kyverno 在 Amazon EKS 实施 PaC 进行探讨。

Kyverno 的原理是通过扩展 Kubernetes 的准入控制器(Admission Controller),进行动态准入控制,其流程可以简述为在 Kubernetes 的 Mutating 准入控制器和 Validating 准入控制器注入其自身的 webhook, 当资源的创建/更新请求进入准入控制器后,Kyverno 的 webhook 会收到准入控制器的 Admission Review 对象,然后通过 Kyverno 的控制器进行对应的处理并将处理结果返回给准入控制器

380ddcd5d85dc1ef6babfc8b2a8f5d54.jpeg

下面就让我们通过几个例子从实战中了解一下如何通过 Kyverno 实现策略即代码。

outside_default.png

工具安装

outside_default.png

eksctl:https://eksctl.io/introduction/#installation

kubectl:https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html

helm:https://helm.sh/docs/intro/install/

outside_default.png

部署 EKS 测试集群

outside_default.png

用户可以通过Amazon控制台界面或 CLI 工具 eksctl 对Amazon EKS 进行集群操作。本文下述测试将使用eksctl进行集群的创建/删除,同时基于 Kyverno 1.8.1 版本进行安装验证。

eksctl:https://eksctl.io/

01

首先通过 eksctl CLI 创建一个用于测试的EKS集群,Kyverno 策略会影响资源创建的结果,请务必不要在生产环境中测试。

eksctl create cluster --name eks-kyverno-test

该命令会在 Amazon CLI 配置的默认的 Amazon region 中创建一个带2个 m5.large 节点的 EKS 集群,API 节点访问类型为 Public

02

通过 Helm 部署 Kyverno,在本文中,我们将通过 Helm 安装Kyverno, 安装Chart version 2.6.1,对应Kyverno version 1.8.1。

helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno --version 2.6.1 --create-namespace --set replicaCount=1

左滑查看更多

如果在生产环境部署使用时,应将 replicaCount 设置至少为3以保证高可用

03

确认部署情况

正确部署后,Kyverno 应有2个 service 和1个 deployment 运行在 kyverno namespace 下

3a39c9ec81be03845ee3b49b7a525a70.jpeg

同时观察 validatingwebhookconfigurations 和 mutatingwebhookconfigurations 资源,在未应用任何策略前,我们可以看到如下图所示,kyverno 的部分 webhook 已注册成功,随着 validate/mutate 策略的应用后,其对应的 resource webhook 也会随之进行注册。

dae16f5a9ac5c3cf19665159e11f0e25.jpeg

outside_default.png

Kyverno 策略模板

outside_default.png

Kyverno 的策略模板跟 Kubernetes 的 YAML 格式一致,这也是 Kyverno 的最大的特点,只要熟悉 Kubernetes 资源 YAML 的写法就能很快上手编写 Kyverno 策略。

Kyverno 的 YAML 结构分为以下几个部分:

– Policy 本身的属性,包含一组 Rule 的集合

– 每个 Rule 包含一个匹配/排除的资源

– 每个 Rule 包含一个行为模式(validate/generate/mutate/verify)

4a6722d84decfa38ccb45fc068d0ea00.jpeg

我们接下来来看一下这些策略如何编写和应用。

outside_default.png

应用 Kyverno 策略

outside_default.png

Kyverno 支持多种策略,常见的有验证(validate),变更(mutate),生成(generate)以及更多的使用方式,在本文中将主要对这3种较常见的策略进行解读,其他策略的应用可参考 Kyverno 官方文档说明。

1

兼容 PSS 的策略

Kyverno 提供了兼容 PSS 的策略模式,可通过 PSS 类似的方式快速应用预置的安全策略,建议在安装 Kyverno 后至少需要安装配置 baseline 的安全模式以保证集群的安全运行。

首先我们通过 helm 安装 Kyverno 提供的 PSS 策略。

 helm install kyverno-policies kyverno/kyverno-policies -n kyverno 

以上命令会部署默认 Baseline 组的策略,提供基础限制性的策略,禁止已知的策略提升。默认情况下,这些策略会被设置为 Audit 模式,在该模式下,不会真正的阻挡资源的创建,而是在 Policy Report(Kyverno的一种资源)中报告相关资源。关于 Kyverno 的 Policy Report 详见下文描述。我们可以配置不同的 helm value 来更改安装策略时的参数,例如对上面的命令增加下列参数,就会安装策略为 Restricted 组的策略并设置为 enforce (阻止)模式。更多的 helm 配置可参考其 kyverno policies charts 的 github 页面。以下示例仍以 baseline 以及 audit 模式进行演示。

--set podSecurityStandard=restricted --set validationFailureAction=enforce

我们测试在 baseline 策略下运行特权容器,  会发现集群并不会阻止 pod 的创建,但是对应 validate 失败的信息会反馈在 Kyverno 的 Policy Report 中。

cat > baseline-pod.yaml << EOF
 apiVersion: v1
 kind: Pod
 metadata:
   name: baseline-pod-1
   namespace: default
 spec:
   containers:
   - name: busybox
     image: busybox
     command: ['sh', '-c', 'sleep 999']
     securityContext:
        privileged: true
EOF
kubectl apply -f baseline-pod.yaml

左滑查看更多

执行完毕可以看到Pod可以正常创建在 default namespace 下。

9deb4952c27c0dcbecb54f2225d3bb6f.jpeg

运行 kubectl get polr -A,可看到 PSS 每个 Policy 对应一个 report,在特权容器检查项 disallow-privileged-containers 有一项 FAIL。

0476d60462c7a0ee9bfd26d535994d4e.jpeg

用 kubectl describe polr/cpol-disallow-privileged-containers 查看对应 Policy Report 的内容,可以看到其中详细描述了该 Pod 在 disallow-privileged-containers 策略检查时失败。

e2e65caa7503a8fb5f5b5ab93440ad1f.jpeg

通常在开始使用 Kyverno 时,我们可以安装上述策略并维持在 audit 模式以保证对集群无影响,当通过一段时间的运行并检查 Policy Report 的检查结果后,如果策略运行符合我们的预期,可以将策略配置为 enforce 模式,让其阻挡不符合要求的资源进入集群。

清理环境

 kubectl delete -f baseline-pod.yaml 

2

validate(验证)行为

首先我们先部署一个 validate 行为的示例策略。

cat > cpol-pod-require-labels.yaml << EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: pod-require-labels
  annotations:
    policies.kyverno.io/category: Compliance
    policies.kyverno.io/description: Rules to enforce labels on Deployment and Pod resources
spec:
  validationFailureAction: enforce
  rules:
  - name: pod-labels
    match:
      resources:
        kinds:
        - Pod
    validate:
      message: "labels app, owner, env are required"
      pattern:
        metadata:
          labels:
            app: "?*"
            owner: "?*"
            env: "?*"
EOF
kubectl apply -f cpol-pod-require-labels.yaml

左滑查看更多

上面这个策略定义了:

– 策略模式为 validationFailureAction: enforce,当验证失败时,阻止资源在集群中创建

– 策略应用在所有的pod上,pod上的必须有 app,owner, env 3个label

将策略应用到集群中后我们可以通过 kubectl get cpol 来查看策略是否已正确创建

cb51450d5896a062d33b5742f26c7a24.jpeg

接下来我们尝试创建一个不带任何 label 的 pod 到集群中

cat > pod-without-label.yaml << EOF
 apiVersion: v1
 kind: Pod
 metadata:
   name: pod-without-label
   namespace: default
 spec:
   containers:
   - name: busybox
     image: busybox
     command: ['sh', '-c', 'sleep 999']
EOF
kubectl apply -f pod-without-label.yaml

左滑查看更多

我们可以看见执行 apply 时,会报错被策略阻挡,并给出了被哪条规则阻挡以及验证出错时的消息(定义在上述 Policy 中的 validate/message 字段)

f665789e47dd3a52996a3d39e3868b7a.jpeg

接下来我们新建一个带指定 label 的 pod,再次尝试创建

cat > pod-with-label.yaml << EOF
 apiVersion: v1
 kind: Pod
 metadata:
   name: pod-with-label
   namespace: default
   labels:
     app: test-app
     owner: barry
     env: test
 spec:
   containers:
   - name: busybox
     image: busybox
     command: ['sh', '-c', 'sleep 999']
EOF
kubectl apply -f pod-with-label.yaml

左滑查看更多

这次我们可以看到 pod 可以成功创建了,这说明策略已正常生效

77aabcdbe3112ba1e1c62d92d59d45e8.jpeg

validate 策略是使用最多的策略,通过配置满足业务和合规上的要求,我们可以在资源创建到集群中之前记录(audit)或阻挡(enforce)其行为。例如,不满足 label 要求的资源不允许创建,或者指定 namespace 中的资源的 image 必须来自某个指定的 repo 前缀等等。

清理环境以避免上述应用的策略对后续测试产生影响

kubectl delete -f cpol-pod-require-labels.yaml
kubectl delete -f pod-with-label.yaml

左滑查看更多

3

mutate(变更)行为

首先我们先创建一个 mutate 行为的示例策略

cat > cpol-set-image-pull.yaml << EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: set-image-pull-policy
spec:
  rules:
    - name: set-image-pull-policy
      match:
        any:
        - resources:
            kinds:
            - Pod
      mutate:
        patchStrategicMerge:
          spec:
            containers:
              - (image): "*:latest"
                imagePullPolicy: "IfNotPresent"
EOF
kubectl apply -f cpol-set-image-pull.yaml

左滑查看更多

这个策略定义了:

– 策略应用在所有pod上

– 匹配 image 使用“latest“结尾,将其 imagePullPolicy 修改为 IfNotPresent 。这里 (image) 是一种叫做 Conditional Anchor 的特殊语法,它的作用时仅当 image 这个 tag 匹配它的值中声明的模式时,才会进行后续的动作,否则跳过

Conditional Anchor:

https://kyverno.io/docs/writing-policies/mutate/#conditional-anchor

我们用一个测试 pod 来验证策略是否生效,下面这个测试 pod 的 image 的格式使用了 *:latest 格式,但 imagePullPolicy 设置为了 Never 。假如镜像是未被拉取过的,在这个策略下就会因不主动拉取镜像而导致启动失败。我们来看看如何通过mutate行为来避免此类错误。

cat > pod-never-pull-image.yaml << EOF
 apiVersion: v1
 kind: Pod
 metadata:
   name: pod-never-pull-image
   namespace: default
 spec:
   containers:
   - name: busybox
     image: busybox:latest
     imagePullPolicy: Never
     command: ['sh', '-c', 'sleep 999']
EOF
kubectl apply -f pod-never-pull-image.yaml

左滑查看更多

Pod 成功创建后,我们查看一下这个Pod的详细信息,kubectl get pod/pod-never-pull-image -o yaml 可以看到其 imagePullPolicy 不再是之前声明的 Never,而是被策略修改为了 IfNotPresent

d9f04e8df833a595f3e4c598dcc9201d.jpeg

接下来我们来看看 mutate 的另一种用法,mutate 不仅可以修改资源的配置,也可以增加配置项,例如下面这个示例就实现了一个类似容器注入的功能。

首先我们先编写策略并应用到集群中。

cat > cpol-add-image.yaml << EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: strategic-merge-patch
spec:
  rules:
  - name: add-image-policy
    match:
      any:
      - resources:
          kinds:
          - Pod
    mutate:
      patchStrategicMerge:
        metadata:
          labels:
            name: "{{request.object.metadata.name}}"
        spec:
          containers:
            - name: "nginx"
              image: "nginx:latest"
              imagePullPolicy: "Never"
              command:
              - ls
EOF
kubectl apply -f cpol-add-image.yaml

左滑查看更多

这个策略定义了:

– 策略应用在所有 pod 上

– 对 Pod 增加一个叫 name 的 label,其值取自自身的name

– 在 Pod 中增加一个 nginx 的容器,由于这里我们将注入的 nginx 容器的镜像拉取策略设置为 Never,如果集群中没有 nginx 的 image 的话,这个 Pod 会运行失败,但并不影响我们验证 mutate 对 Pod 变更的结果。

接下来我们用下面的测试 Pod 来验证一下策略

cat > pod-with-single-image.yaml << EOF
 apiVersion: v1
 kind: Pod
 metadata:
   name: pod-with-single-image
   namespace: default
 spec:
   containers:
   - name: busybox
     image: busybox
     command: ['sh', '-c', 'sleep 999']
EOF
kubectl apply -f pod-with-single-image.yaml

左滑查看更多

当 Pod 成功创建后,我们观察一下 Pod 的详细信息,会发现 label 和容器都如预期一样添加到原 Pod 中了

e25c2037815848d992d5ae787d9b8a2d.jpeg

dcab76086a411ecb681b316018d1d7d4.jpeg

mutate 是一种很强大的工具,我们可以将一些常见的集群配置要求或最佳实践通过 mutate 配置到集群,例如镜像拉取策略,默认 label,注入容器等等,将这些工作变成集群的“默认”配置并自动应用到匹配的资源上。对于这类可以用“默认”行为处理的场景,mutate 是很好的选择。同时 mutating admission 发生在 validating admission 之前的阶段,所以资源是以 mutate 后再进行 validate,并不会因为 mutate 导致逃逸 validate 的检查。

e68ede60a092b5b4d35abd713b9927fa.jpeg

但必须强调的是,mutate 也是一把双刃剑,因为对原资源进行修改是可能发生副作用的,比如修改了错误的字段,或将同名的镜像错误更新,又或者是更新导致后续validate阶段无法通过等等,都可能影响应用的正常使用,因此,在使用 mutate 策略时,务必要了解 mutate 的行为模式以及进行充分测试。

清理环境以避免上述应用的策略对后续测试产生影响

kubectl delete -f cpol-add-image.yaml
kubectl delete -f cpol-set-image-pull.yaml
kubectl delete -f pod-never-pull-image.yaml
kubectl delete -f pod-with-single-image.yaml

左滑查看更多

4

generate(生成)行为

generate 策略与 mutate 策略不同的地方是,mutate策略是修改准入集群的资源本身,比如创建一个Pod时,mutate 的应用范围只会在这个 pod 本身。而generate 策略是根据准入集群的资源,创建出额外的Kubernetes 资源。

下面这个示例,我们会看到通过一个 generate 策略,在创建一个新的 namespace 时,额外在其中生成一个 ConfigMap。

首先我们编写一个 generate 策略并应用到集群中

cat > cpol-gen-cm.yaml << EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: gen-default-cm
spec:
  rules:
  - name: generate ConfigMap
    match:
      any:
      - resources:
          kinds:
          - Namespace
    exclude:
      any:
      - resources:
          namespaces:
          - kube-system
          - default
          - kube-public
          - kyverno
    generate:
      synchronize: true
      apiVersion: v1
      kind: ConfigMap
      name: test-cm
      namespace: "{{request.object.metadata.name}}"
      data:
        kind: ConfigMap
        metadata:
          labels:
            somekey: somevalue
        data:
          KEY1: "Value1"
          KEY2: "Value2"
EOF
kubectl apply -f cpol-gen-cm.yaml

左滑查看更多

这个策略定义了:

– 应用在 namespace 资源上,但排除 kube-system , default , kube-public , kyverno 这几个 namesapce

– 创建一个新的叫 test-cm 的 configmap,并置于请求创建的同名 namespace 中

– 这个 ConfigMap 带有默认的预设值

接下来我们创建一个叫 test-ns-1的namespace,创建后我们可以看到在这个 namespace 下已经出现了一个 test-cm 的 ConfigMap

824a581660b23f7230a114e1ed06e27a.jpeg

查看这个 ConfigMap,我们可以看到它的 label 和预设的值都如预期在策略中定义的一样。

1309ebf61cc872225952b9a74578ea1b.jpeg

generate 策略通常会用于集群环境配置上,例如当新建资源时,创建一些支撑性的资源,或者是创建默认的 NetworkPolicy, RoleBinding 等访问控制相关资源。

常见的例子如在多租户的 EKS 集群中,创建租户的 namespace 时一并创建对象 RBAC 等访问控制资源。通过将此类行为编写为策略,可减少人工操作的负担和避免遗漏。generate 也可以复制已有的资源来产生新的资源,限于篇幅,此处不再展开。

outside_default.png

清理环境

outside_default.png

通过 Amazon 控制台或 eksctl 命令删除该测试 EKS 集群以清理环境,避免产生额外的费用。

eksctl delete cluster --name eks-kyverno-test

outside_default.png

总  结

outside_default.png

通过上面几个简单的例子,我们可以快速了解到 Kyverno 是什么,能做什么。但限于篇幅,无法对 Kyverno 所有的功能进行介绍,有兴趣的读者可以参考 Kyverno 官方文档进行深入阅读。

除此之外,Kyverno 也提供了 CLI 工具,以协助用户在 Policy 部署到集群前进行验证以避免预期外的效果。

 CLI 工具:

https://kyverno.io/docs/kyverno-cli/

策略即代码解决方案提供了自动防护机制,既可以启用用户又可以防止不需要的行为。选择正确的策略即代码解决方案并非易事,Kyverno 解决方案亦并非唯一选择,组织在做出正确选择时必须从多个因素进行考虑, 根据测试和概念验证的结果选择合适企业自身的策略即代码方案。

无论选择哪种解决方案,策略即代码正在成为 DevOps  和纵深防御策略的基础组成部分。

参 考 链 接

·Kyverno文档 :https://kyverno.io/docs/

·Kyverno Policy库 :https://kyverno.io/policies/

·Kyverno Policy on Amazon EKS示例 :https://github.com/aws/aws-eks-best-practices/tree/master/policies/kyverno

本篇作者

5b7132b340515eeb9112e6f1efe5c904.jpeg

王冰

亚马逊云科技金融行业解决方案架构师, 主要负责企业客户上云,帮助客户进行云架构设计和技术咨询,专注于容器、数据库等技术方向。

5705a59485ffdfdde0e44782197aae0d.png

2022亚马逊云科技 re:Invent 全球大会

中国行现已开启!

👇👇👇点击下方图片即刻注册👇👇👇

80081cca56d4fb64baea94f6493263b8.jpeg

985bee21ed776c2937dc5d9a6864e868.gif

b46e704e187014b6a5b042c653f11e59.gif

听说,点完下面4个按钮

就不会碰到bug了!

a3a9e7d72702aa84f5c81806ce3e22ef.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值