Kyverno — замена PodSecurityPolicy или нечто большее?

от автора

Привет, хабравчане!

Слышали ли вы, что такое Kyverno и зачем он нужен? В этой статье расскажу и покажу на примерах, как мы его используем.

Меня зовут Макарий Балашов, я SRE в Ak Bars Digital. Наша команда занимается подготовкой и развитием инфраструктуры для команд разработки.

Что такое этот ваш Kyverno?

Kyverno — policy engine разработанный специально для Kubernetes.

Kyverno позволяет администраторам кластеров управлять специфическими конфигурациями среды независимо от конфигураций ресурсов, применять передовые методы настройки для своих кластеров. Kyverno можно использовать для сканирования существующих ресурсов на наличие best practises или для соблюдения их путем блокировки или изменения запросов API.

Вот небольшой список крутых фишек Kyverno:

  • policy как ресурсы kubernetes (yaml манифесты);

  • возможность валидировать, изменять или создавать ресурсы k8s;

  • проверка образов контейнеров для обеспечения безопасности цепочки поставок ПО;

  • проверка метадаты образов;

  • синхронизация конфигурации в Namespace’ах;

  • блокировка несовместимые ресурсы с помощью элементов управления доступом или сообщать о нарушениях политики;

  • управление политиками как кодом с помощью знакомых инструментов, таких как git и kustomize;

  • постоянно расширяемая библиотека готовых политик (уже 244).

В качестве альтернатив Kyverno можно выделить OPA/GatekeeperKubewarden и jsPolicy.

Про сравнение этих решений есть много информации в сети, например тут сравнивают Kyverno и Gatekeeper.

Зачем это нам?

В 1.21 PodSecurityPolicy стала депрекейтед фичей Kubernetes, а в 1.25 была вовсе удалена.

Перед нами стал вопрос, а как дальше жить? И мы решили потестить разные решения, выбор остановился на Kyverno. Нас она очень сильно порадовала и удовлетворила наши потребности, как замена PSP.

На фоне альтернатив Kyverno выделился большим функционалом сразу из коробки и простой лексикой (обычный yaml), что позволяет новым сотрудникам гораздо быстрее разобраться, как с ним работать.

Как она работает?

Kyverno работает как динамический контроллер допуска в кластере Kubernetes. Kyverno получает проверяющие и изменяющие вебхуки допуска от kube-apiserver. Применяет соответствующие политики для возврата результатов, которые применяют политики допуска или отклоняют запросы.

На рисунке показана верхнеуровневая архитектура работы Kyverno. Источник.
На рисунке показана верхнеуровневая архитектура работы Kyverno. Источник.

Для чего используем?

Изначально мы планировали использовать Kyverno только как замену PodSecurityPolicy. Начали с переписования уже существующих манифестов PSP в манифесты политик Kyverno c Validate правилами.

Validate Resources

Например, мы сразу хотели запретить запуск privilege контейнеров, так как это чревато тем, что под может получить доступ к ресурсам хоста и возможностям ядра.

Политика которую мы используем
apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata:   name: disallow-privileged-containers   annotations:     policies.kyverno.io/title: Disallow Privileged Containers     policies.kyverno.io/category: Pod Security Standards (Baseline)     policies.kyverno.io/severity: medium     policies.kyverno.io/subject: Pod     kyverno.io/kyverno-version: 1.6.0     kyverno.io/kubernetes-version: "1.22-1.23"     policies.kyverno.io/description: >-       Privileged mode disables most security mechanisms and must not be allowed. This policy       ensures Pods do not call for privileged mode.       spec:   validationFailureAction: audit   background: true   rules:     - name: privileged-containers       match:         any:         - resources:             kinds:               - Pod       validate:         message: >-           Privileged mode is disallowed. The fields spec.containers[*].securityContext.privileged           and spec.initContainers[*].securityContext.privileged must be unset or set to `false`.                   pattern:           spec:             =(ephemeralContainers):               - =(securityContext):                   =(privileged): "false"             =(initContainers):               - =(securityContext):                   =(privileged): "false"             containers:               - =(securityContext):                   =(privileged): "false"

Mutating Resources

Далее, мы решили сразу выставлять нужный нам securityContext для всех ресурсов, которые будут создаваться в нашем кластере. Вот что у нас получилось:

Политика для добавления ресурсам securityContext
apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata:   name: add-default-securitycontext   annotations:     policies.kyverno.io/title: Add Default securityContext     policies.kyverno.io/category: Sample     policies.kyverno.io/subject: Pod     policies.kyverno.io/description: >-       A Pod securityContext entry defines fields such as the user and group which should be used to run the Pod.       Sometimes choosing default values for users rather than blocking is a better alternative to not impede       such Pod definitions. This policy will mutate a Pod to set default securityContext     spec:   background: false   rules:   - name: add-default-securitycontext-containers     match:       any:       - resources:           kinds:           - Pod     preconditions:       all:       - key: "{{request.operation}}"         operator: In         value:           - CREATE           - UPDATE     mutate:       foreach:       - list: "request.object.spec.containers"         patchStrategicMerge:           spec:             securityContext:               runAsUser: 10001               runAsGroup: 10001               fsGroup: 10001               seccompProfile:                 type: RuntimeDefault             containers:             - name: "{{ element.name }}"               securityContext:                 runAsUser: 10001                 runAsGroup: 10001                 capabilities:                   drop:                     - ALL                 runAsNonRoot: true                 allowPrivilegeEscalation: false privileged: false                 seccompProfile:                   type: RuntimeDefault   - name: add-default-securitycontext-initContainers     match:       any:       - resources:           kinds:           - Pod     preconditions:       all:       - key: "{{request.operation}}"         operator: In         value:           - CREATE           - UPDATE       - key: "{{ request.object.spec.initContainers[] || '' | length(@) }}"         operator: GreaterThanOrEquals         value: 1     mutate:       foreach:       - list: "request.object.spec.initContainers"         patchStrategicMerge:           spec:             securityContext:               runAsUser: 10001               fsGroup: 10001               seccompProfile:                 type: RuntimeDefault             initContainers:             - name: "{{ element.name }}"               securityContext:                 runAsUser: 10001                 runAsGroup: 10001                 capabilities:                   drop:                     - ALL                 runAsNonRoot: true                 allowPrivilegeEscalation: false privileged: false                 seccompProfile:                   type: RuntimeDefault

Generate Resources

Как пример использования generate-политик сразу приходит в голову копирование секрета с кредами от private registry (dockerconfigjson) в каждый Namespace.

Мы также нашли следующее применение — копирование секрета с самоподписанным рутовым сертификатом, который генерирует cert-manager, в каждый namespace для последующей инъекции при помощи другой политики в контейнеры. Обратите внимание на synchronize, который позволяет Kyverno автоматически обновлять содержимое всех созданных ею ресурсами при изменении изначального. В нашем случае — это обновление сертификата во всех секретах после автоматического перевыпуска cert-manager’ом.

Копирование секрета по Namespac’ам
apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata:   name: clone-cert   annotations:     policies.kyverno.io/title: Clone Certificate's secret     policies.kyverno.io/category: Cert-Manager     policies.kyverno.io/subject: Certificate, Namespace     policies.kyverno.io/description: >-       Clone certificate's secret to every namespace spec:   background: true   rules:   - name: clone-cert     match:       any:       - resources:           kinds:           - Namespace     exclude:       any:       - resources:           namespaces:           - "kube-system"           - "kube-public"           - "default"     generate:       synchronize: true       apiVersion: v1       kind: Secret       name: base-cert       namespace: "{{request.object.metadata.name}}"       clone:         namespace: cert-manager         name: base-cert

Пример политики, которой делаем инъекцию
apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata:   name: add-certificates-volume   annotations:     policies.kyverno.io/title: Add Certificates as a Volume     policies.kyverno.io/category: Sample     policies.kyverno.io/subject: Pod,Volume     kyverno.io/kyverno-version: 1.5.2     kyverno.io/kubernetes-version: "1.21"     policies.kyverno.io/minversion: 1.5.0     pod-policies.kyverno.io/autogen-controllers: DaemonSet,Deployment,Job,StatefulSet     policies.kyverno.io/description: >-       In some cases you would need to trust custom CA certificates for all the containers of a Pod.       It makes sense to be in a Secret so that you can automount them by only setting an annotation.       This policy adds a volume to all containers in a Pod containing the certificate if the annotation       called `inject-certs` with value `enabled` is found.       spec:   background: true   rules:   - name: add-ssl-certs     match:       any:       - resources:           kinds:           - Pod     exclude:       any:       - resources:           namespaces:           - "kube-system"           - "kube-public"           - "default"     preconditions:       all:       - key: "{{request.operation}}"         operator: In         value:           - CREATE           - UPDATE     mutate:       foreach:       - list: "request.object.spec.containers"          patchStrategicMerge:           spec:             containers:             - name: "{{ element.name }}"               volumeMounts:               - name: ssl-certs                 mountPath: /etc/ssl/certs             volumes:             - name: ssl-certs               secret:                 secretName: base-cert

Verify Image

Также, при помощи Kyverno и Sigstore Cosign можно проверять и подписывать образы контейнеров. Мы только внедряем эти фичи. Подборку статей по этой теме можно посмотреть в  телеграмм канале k8s (in)security

Заключение

Kyverno великолепен, потому что позволяет нам последовательно управлять нашими кластерами на уровне кластера без реального кода. Недопустимые ресурсы могут быть заблокированы с всплывающими сообщениями об ошибках для пользователей. Неправильно настроенные ресурсы могут быть исправлены на лету, а новые ресурсы могут быть динамически созданы. С этим инструментом мы получили очень приятный опыт управления кластерами Kubernetes. И будем дальше его использовать.

Ссылки, которые могут быть вам полезны, если захотите использовать Kyverno в своих проектах:


ссылка на оригинал статьи https://habr.com/ru/company/akbarsdigital/blog/711102/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *