Привет, хабравчане!
Слышали ли вы, что такое 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/Gatekeeper, Kubewarden и jsPolicy.
Про сравнение этих решений есть много информации в сети, например тут сравнивают Kyverno и Gatekeeper.
Зачем это нам?
В 1.21 PodSecurityPolicy стала депрекейтед фичей Kubernetes, а в 1.25 была вовсе удалена.
Перед нами стал вопрос, а как дальше жить? И мы решили потестить разные решения, выбор остановился на Kyverno. Нас она очень сильно порадовала и удовлетворила наши потребности, как замена PSP.
На фоне альтернатив Kyverno выделился большим функционалом сразу из коробки и простой лексикой (обычный yaml), что позволяет новым сотрудникам гораздо быстрее разобраться, как с ним работать.
Как она работает?
Kyverno работает как динамический контроллер допуска в кластере Kubernetes. Kyverno получает проверяющие и изменяющие вебхуки допуска от kube-apiserver. Применяет соответствующие политики для возврата результатов, которые применяют политики допуска или отклоняют запросы.
Для чего используем?
Изначально мы планировали использовать 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://kyverno.io/docs/
Набор готовых полиси - https://kyverno.io/policies/
Replacing PSPs? Keep Bad Pods out of your cluster using Kyverno - https://youtu.be/AmJUFH7n33c
PodSecurityPolicy Migrator - https://appvia.github.io/psp-migration/ , https://github.com/appvia/psp-migration
Governing Multi-Tenant Kubernetes Clusters with Kyverno - https://medium.com/compass-true-north/governing-multi-tenant-kubernetes-clusters-with-kyverno-3e11ba4a64ad
D1abloRUS
Ох уж эта yaml разработка...
Хватило вот у кого то фантазии взять нормальный язык
r0binak
Как по мне, Kyverno один из самых удобных Policy Engine, только потому что правила там описываются декларативно в YAML. Kubernetes Native всё-таки)
D1abloRUS
Так то клиент yaml в json конвертит... но суть не в этом, снова скрестили ужа с ежом, синтаксис такой, что без доки это невозможно не читать, не писать