[Перевод] Управление политиками кластера Kubernetes через Gatekeeper OPA
Перевели статью о том, что такое Gatekeeper, зачем он нужен и как работает. Разберёмся с политиками, настроим инфраструктуру Gatekeeper в кластере, протестируем политики.
Что такое OPA Gatekeeper?
Говоря простым языком, Gatekeeper — это инструмент с открытым исходным кодом, который позволяет проводить аудит и применять политики на кластере Kubernetes в автоматическом режиме. Все эти политики написаны на языке rego. Это K8s-специфика OPA [Open Policy Agent], работающая как валидация Admission Controller.
Зачем нужен Gatekeeper?
Допустим, вы работаете в качестве Platform DevOps инженера и управляете несколькими кластерами Kubernetes для нескольких проектов. Но поскольку пользователи имеют доступ к развертыванию приложений на своих кластерах для своих проектов, они могут развернуть любой образ, любое количество реплик, развернуть приложения без соблюдения best practices и т.д. Это очень опасно для проекта. Вот какие с этим есть проблемы:
1. Отсутствует владелец. На многих проектах пользователи не используют метку.
2. Отсутствует лимит ресурсов. В конечном итоге это может привести к большим затратам.
3. Одно плохое приложение, взятое из ненадежного хранилища, может повлиять на все остальные приложения.
4. Возникают проблемы с границами безопасности и применением best practies в кластере.
Следовательно, чтобы упорядочить работу, нам необходимо внедрить такое управление, чтобы мы могли установить некоторые ограничения, правила с помощью политик в соответствии с лучшими практиками. Для настройки этих политик мы можем сконфигурировать OPA Gatekeeper.
Как работает OPA Gatekeeper?
Gatekeeper выполняет роль моста между сервером Kubernetes API и OPA. Это означает, что Gatekeeper проверяет каждый запрос, поступающий в кластер, на предмет нарушения какой-либо из предопределенных политик. OPA генерирует policy решения, оценивая вводимые запросы в соответствии с политиками и данными. Например, если вы пытаетесь создать 10 реплик для своего приложения dev, а ограничение установлено на максимум 5, OPA отклонит развертывание. Вы можете настроить несколько политик в соответствии с потребностями проекта. Некоторые примеры приведены ниже.
1. Лимит реплик, политики безопасности подсистемы.
2. Разрешение реестра образов для извлечения образов.
3. Обязательные метки для объектов и ресурсов.
4. Многое другое.
Понимание политики
Итак, когда мы говорим о политике, для ее понимания необходимо знать несколько обязательных вещей, таких как CRD, ConstraintTemplate и Constraints. Давайте попробуем разобраться.
CRD:
API CustomResourceDefinition (CRD) позволяет нам определять пользовательские ресурсы. При определении объекта CRD создается новый пользовательский ресурс с указанными нами именем и схемой. API Kubernetes обслуживает и обрабатывает хранение пользовательских ресурсов.
В данном случае Gatekeeper использует CRD для определения шаблонов ограничений и ограничений, позволяющих применять политики.
Constraint Templates:
ConstraintTemplates (шаблоны ограничений) определяют способ проверки некоторого набора объектов Kubernetes в Admission Controller. Здесь вы можете настраивать свои политики и адаптировать их в соответствии с вашими потребностями, а также в случае несоответствия политики вы можете настроить сообщение, чтобы конечный пользователь понял ошибку и мог её исправить. Эта политика должна быть написана на языке Rego.
Constraints:
Constraints (ограничение) — это объявление требований, которым должна удовлетворять система. Другими словами, ограничения используются для информирования Gatekeeper о том, что администратор хочет, чтобы шаблон ConstraintTemplate выполнялся, и каким образом. Говоря простым языком, это аргумент для вашей функции. Здесь вы можете ссылаться на функцию как на свой ConstraintTemplates.
Настройка Gatekeeper
Существует несколько способов установки Gatekeeper: Helm, Make File или запуск отдельных файлов манифеста в кластере Kubernetes.
Здесь я использую HELM, так как он мне больше нравится, чем другие способы, но да, если вы хотите знать, какие объекты мы используем, я бы рекомендовал просмотреть файлы манифеста.
Через Helm:
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install gatekeeper/gatekeeper gatekeeper --namespace gatekeeper-system --create-namespace
Здесь в качестве имени Helm Release используется «gatekeeper». Итак, когда все это будет сделано, вы должны увидеть, что некоторые подсистемы, сервисы, роли кластера, секреты и CRD созданы.
После установки Gatekeeper
Также будут созданы ресурсы mutating webhook configuration и validating webhook configuration.
После установки Gatekeeper
Итак, с установкой Gatekeeper мы разобрались. Теперь давайте настроим некоторые политики поверх него и поиграем с ними.
Тест политик Gatekeeper
Для начала мы введем политику, согласно которой вы не сможете создать пространство имен без ярлыков владельца проекта «owner». Ниже я добавил шаблоны ограничений и ограничения, а также несколько комментариев в разделе кода, чтобы вы могли лучше понять его.
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
annotations:
metadata.gatekeeper.sh/title: "Required Labels"
metadata.gatekeeper.sh/version: 1.0.0
description: >-
Requires resources to contain specified labels, with values matching
provided regular expressions.
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
message:
type: string
labels:
type: array
description: >-
A list of labels and values the object must specify.
items:
type: object
properties:
key:
type: string
description: >-
The required label.
allowedRegex:
type: string
description: >-
If specified, a regular expression the annotation's value
must match. The value must contain at least one match for
the regular expression.
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
## Defines the package name of the module
package k8srequiredlabels
get_message(parameters, _default) = msg {
not parameters.message
msg := _default
}
get_message(parameters, _default) = msg {
msg := parameters.message
}
## Defining the violation rule. The violation rule schema must be followed for it to be considered a valid ConstraintTemplate, which is {"msg": string, "details" set}
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
## 'provided' variable is a set with all the labels from the object currently being evaluated by gatekeeper
provided := {label | input.review.object.metadata.labels[label]}
## 'required' variable is a set with all the labels from the Constraint parameters
required := {label | label := input.parameters.labels[_].key}
## 'missing' variable is a set the difference in value between required and provided
missing := required - provided
## if there are more than zero values in the set 'missing' evaluate to true
count(missing) > 0
## 'msg' variable is declared.
def_msg := sprintf("you must provide labels: %v", [missing])
msg := get_message(input.parameters, def_msg)
}
violation[{"msg": msg}] {
value := input.review.object.metadata.labels[key]
expected := input.parameters.labels[_]
expected.key == key
# do not match if allowedRegex is not defined, or is an empty string
expected.allowedRegex != ""
not re_match(expected.allowedRegex, value)
def_msg := sprintf("Label <%v: %v> does not satisfy allowed regex: %v", [key, value, expected.allowedRegex])
msg := get_message(input.parameters, def_msg)
}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-owner
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
message: "All namespaces must have an `owner` label that points to your company username"
labels:
- key: owner
allowedRegex: "^[a-zA-Z]+.LearnOps$"
После применения обоих файлов для существующих пространств имен обнаружено 7 нарушений.
Шаблон ограничений и ограничения
Хорошо, попробуем создать пространство имен без метки и посмотрим, отклонит он запрос или нет.
apiVersion: v1
kind: Namespace
metadata:
name: expected-to-be-fail
Запрос на создание пространства имен отклонен, как и ожидалось, поскольку в нем отсутствует тег «owner». Попробуем теперь добавить тег «owner» и попытаемся создать его снова.
apiVersion: v1
kind: Namespace
metadata:
name: expected-to-be-working
labels:
owner: mondeepmaity.LearnOps
Вуаля! Наше пространство имен создано.
Таким образом, можно настроить и другие политики в соответствии с потребностями проекта.
Вот ссылки, которые могут быть полезны для настройки политик вашего кластера:
Принудительный Dry Run
При внедрении новых ограничений в работающие кластеры может быть полезна функция Dry Run. Она позволяет развернуть ограничения в кластере без внесения фактических изменений. Это позволяет протестировать ограничения на работающем кластере без их принудительного применения. Ресурсы кластера, затронутые ограничением Dry Run, отображаются как нарушения в поле status
ограничения.
Чтобы использовать функцию «сухого прогона», добавьте в спецификацию ограничения параметр enforcementAction: dryrun
, чтобы гарантировать отсутствие фактических изменений в результате действия ограничения. По умолчанию для параметра enforcementAction
установлено значение deny
, так как по умолчанию запросы на допуск с любыми нарушениями отклоняются.
Предупреждение о принудительном действии
Предупреждение о принудительном выполнении предлагает те же преимущества, что и dry run. Например, тестирование ограничений без их принудительного выполнения. Кроме того, в этом случае можно сразу же получить информацию о том, почему данное ограничение было отклонено.
Config Resource
Config Resource может использоваться для исключения пространств имен и синхронизации ресурсов с определенными процессами для всех ограничений в кластере. Звездочка может использоваться для сопоставления с подстановочным знаком (например, kube-*
). Чтобы исключить пространства имен на уровне ограничений, используйте excludedNamespaces
в ограничении.
Некоторые ограничения невозможно написать без доступа к большему количеству состояний, чем просто тестируемый объект. Например, невозможно узнать, является ли имя хоста ingress уникальным среди всех ингрессов, если правило не имеет доступа ко всем остальным ингрессам. Чтобы сделать такие правила возможными, мы включаем синхронизацию данных в OPA.
Функция аудита по умолчанию не требует репликации. Однако если флаг audit-from-cache
установлен в true
, то кэш OPA будет использоваться в качестве источника истины для запросов аудита; таким образом, объект должен быть сначала помещен в кэш, прежде чем он будет подвергнут аудиту на предмет нарушения ограничений.
Данные Kubernetes могут быть реплицированы в OPA с помощью sync config resource. В настоящее время в OPA будут синхронизироваться ресурсы, для которых задано значение syncOnly
. Обновление syncOnly
должно динамически обновлять синхронизируемые объекты. Ниже приведен пример:
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
name: config
namespace: "gatekeeper-system"
spec:
sync:
syncOnly:
- group: ""
version: "v1"
kind: "Namespace"
- group: ""
version: "v1"
kind: "Pod"
match:
- excludedNamespaces: ["kube-*"]
processes: ["*"]
- excludedNamespaces: ["audit-excluded-ns"]
processes: ["audit"]
Итоги
Валидирующие и мутирующие политики в Kubernetes можно реализовать с помощью OPA gatekeeper.
Rego позволяет определять сложную логику для политик. Но изучить rego нелегко. Однако «Gatekeeper-Library» — это очень хорошее репо для начала и изучения. Потом вы можете начать писать самостоятельно.
Вы можете создать ограничение определенного вида только в том случае, если шаблон ограничения этого вида уже определен.
При обкатке новых ограничений на работающих кластерах может быть полезна функция сухой прогона dry run.
Если вы хотите узнать об Open Policy Agent (OPA), заменить устаревший PSP, внедрить Gatekeeper в работающий кластер, то приходите на курс Kubernetes: Мега. Это продвинутый курс для тех, кто уже имеет опыт работы с K8s, хочет заглянуть под капот, научиться тонкой настройке и кастомизации. В курсе 7 онлайн-встреч со спикерами, практика на стендах и сертификация.
Ознакомиться с программой и оставить заявку можно на нашем сайте.