Привет, Хабр!
Представим, что вы отвечаете за десятки конфигурационных файлов Kubernetes (или Terraform, Ansible, не суть важно) в репозитории, и каждый pull request может потенциально привести к тому, что в кластер уйдёт что‑то не то. Наш любимый коллега случайно поставил контейнер с privileged‑правами, другой задеплоил образ из публичного репозитория Docker Hub, а третий вовсе забыл про лимиты памяти и CPU. Без автоматического контроля такие промахи легко попадут в продакшн. Ошибки в настройках сегодня одна из главных причин инцидентов безопасности в облачных средах.
Как же нам держать всё под контролем? Внедрить политики как код: формализованные правила, проверяемые автоматически на каждом шаге. В этой статье я расскажу, как применять Open Policy Agent и язык Rego, чтобы навести порядок в GitOps‑пайплайне и не допускать лишнего в конфигурациях.
Open Policy Agent и Rego: политики как код
Чтобы автоматизировать контроль конфигураций, придумали концепцию Policy as Code, «политики в виде кода». Здесь пригождается Open Policy Agent. Это открытый универсальный движок политик, который позволяет задавать различные правила (от требований безопасности до ограничений ресурсов) декларативно, в виде программного кода. OPA не привязан к какому‑то одному инструменту: он одинаково хорошо справляется с проверкой Kubernetes‑манифестов, Terraform‑планов или JSON‑файлов настроек приложений. Вы описываете политику на специальном языке Rego, OPA применяет её к входным данным (например, к YAML‑конфигурации) и выдаёт решение, можно пропустить изменение или нет.
Правила становятся прозрачными и версионируемыми. Храним Rego‑политики в Git, обсуждаем их в pull request, пишем к ним тесты, всё как с обычным кодом. Любое изменение политики отслеживается: видно, кто и зачем его внес, при необходимости можно откатиться. К тому же один и тот же набор политик можно применить повсеместно, в разных кластерах, проектах и средах, обеспечив единый стандарт управления.
Примеры политик, которые удобно реализовать через OPA/Rego, встречаются повсюду. Например:
Обязательные метки и аннотации для ресурсов. Каждый namespace или Deployment должен иметь метку
ownerс именем ответственного, все сервисы — корректныйapp‑лейбл и так далееОграничения на контейнеры. Запрет запускать образы из внешних репозиториев или использовать тег
:latestв продакшене; запрет привилегированных Pod«ов или запуск под root‑пользователем (требоватьrunAsNonRoot=true).»Ресурсные квоты. Все Pod«ы и Deployment»ы должны задавать
requests/limitsдля CPU и памяти; число реплик в dev‑окружении не превышает определённый максимум.Сетевые правила безопасности. Например, запрещены открытые всем NodePort‑сервисы; у каждого приложения должна быть NetworkPolicy с базовым набором правил.
Безопасные настройки по умолчанию. Включено ли шифрование для S3-бакетов, отключён ли протокол HTTP там, где нужен HTTPS, и многое другое.
С помощью Rego можно выразить практически любую логику проверки конфигурации.
Автоматизация проверки конфигураций: Conftest в CI/CD
Для интеграции политик на этапе Continuous Integration удобно использовать инструмент Conftest. Это утилита командной строки, которая запускает OPA‑политики на ваших файлах конфигурации. Вы пишете правила на Rego, а Conftest проверяет, что YAML/JSON‑файлы им соответствуют. Он поддерживает множество форматов (Kubernetes‑манифесты, Terraform, Dockerfile, JSON, INI и др.) и легко вписывается в любой CI/CD‑пайплайн.
Рассмотрим пример. Пусть у нас есть Kubernetes Deployment‑манифест deployment.yaml с потенциально опасными настройками:
# Пример проблемного Deployment (deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels: {} # отсутствует требуемый ключ 'app'
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: nginx:latest
# securityContext отсутствует – контейнер может запуститься от root
В этой конфигурации образ контейнера указан с тегом :latest, поле securityContext.runAsNonRoot отсутствует (то есть контейнер запустится с root‑правами по умолчанию), и к тому же мы намеренно оставили селектор Deployment пустым — без обязательного лейбла app. Напишем для неё политики на Rego в файле policy/deployment.rego:
package main
# Запрещаем запуск контейнера от root-пользователя
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.securityContext.runAsNonRoot
msg = "Containers must not run as root"
}
# Требуем указать метку 'app' в селекторе Deployment’а
deny[msg] {
input.kind == "Deployment"
not input.spec.selector.matchLabels.app
msg = "Deployment must have an 'app' label in selector"
}
Объявили пакет main (Conftest по умолчанию ищет правила в нём) и два правила типа deny. Первое срабатывает, если Deployment не содержит настройку runAsNonRoot=true (значит, контейнер потенциально запустится с root‑правами) — тогда формируем сообщение об ошибке. Второе правило проверяет, что у Deployment в селекторе указан лейбл app; если его нет, выдаётся сообщение о нарушении. Когда любое правило deny возвращает результат, Conftest пометит проверку как не пройденную и выведет заданный нами текст ошибки.
Теперь запустим проверку конфигурации с помощью Conftest:
$ conftest test deployment.yaml
FAIL - deployment.yaml - Containers must not run as root
FAIL - deployment.yaml - Deployment must have an 'app' label in selector
2 tests, 0 passed, 0 warnings, 2 failures, 0 exceptions
Conftest проанализировал YAML и выдал две ошибки, ровно те, что мы заложили в правилах.
Интегрировать Conftest в GitOps‑процесс несложно. Достаточно добавить шаг в ваш CI‑пайплайн: например, для GitHub Actions можно за пару команд установить Conftest и запустить conftest test на нужных директориях. Аналогично это делается в GitLab CI, Jenkins и прочих системах. Более того, Conftest поддерживает хуки для pre‑commit: можно настроить проверку политик ещё до коммита в Gitconf. В идеале все манифесты проходят через такой автоматический фильтр перед тем, как попасть в основную ветку и развернуться в инфраструктуре.
OPA Gatekeeper
Помимо CI‑проверок, стоит добавить слой защиты непосредственно в Kubernetes‑кластер. OPA Gatekeeper интегрирует OPA в кластер как Admission Controller: каждый раз, когда в Kubernetes API поступает запрос на создание или изменение ресурса, Gatekeeper перехватывает его и проверяет на соответствие политикам. Вся информация об объекте в виде манифеста json передаётся в OPA, который оценивает его согласно правилам на языке Rego. Если объект нарушает хоть одно из заданных правил, вебхук возвращает ошибку — и ресурс не создаётся.
Принцип работы OPA Gatekeeper в Kubernetes заключается в том, что все запросы от клиентов (будь то kubectl или GitOps‑автоматика) проходят через webhook с OPA. Если конфигурация не удовлетворяет политикам, OPA возвращает ошибку и объект отклоняется.
Gatekeeper использует CustomResourceDefinition (CRD) для хранения шаблонов и экземпляров политик. Проще говоря, вы создаёте в кластере два вида кастомных ресурсов:
ConstraintTemplate — шаблон политики. Описывает само правило на Rego (в секции
rego:) и параметры, которые можно передавать в политику.Constraint — конкретное ограничение. Ссылается на определённый Template и задаёт, куда он применяется (например, ко всем Pod«ам в таком‑то Namespace) и с какими параметрами. Constraint можно представить как „вызов“ шаблона с конкретными аргументами.»
Политики через Gatekeeper оформляются в виде YAML‑манифестов, так что их удобно хранить в репозитории рядом с остальными конфигами. В GitOps‑подходе вы просто коммитите новые ConstraintTemplate/Constraint в Git, и та же Argo CD (или Flux) доставляет их в кластер, обновляя тем самым набор правил в режиме реального времени.
Для примера возьмём политику, требующую запуск контейнеров только из приватного registry организации. Шаблон ConstraintTemplate на Rego может содержать логику проверки префикса образа (как в примерах выше), а Constraint к нему укажет: применять эту политику ко всем Pod в Namespace X, параметр repos = ["my-registry.company/"]. После развёртывания этих объектов, любая попытка запустить Pod с образом из внешнего репозитория приведёт к мгновенному отклонению.
Мы увидим ошибку прямо при применении манифеста:
Error from server (Forbidden): error when creating "bad-pod.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [allow-backend-repos] container image 'nginx:latest' is not from an allowed repository
Такое сообщение вернёт Gatekeeper, если мы, например, попробуем развернуть Pod, нарушающий политику, в скобках указано имя сработавшего Constraint, а далее текст, сформированный в шаблоне. Запрещённая конфигурация даже не попадёт в кластерЮ ни через kubectl apply, ни через автоматический sync от Argo CD.
Даже если кто‑то миновал проверки в CI или внёс изменения напрямую, политика не даст нарушить установленные правила. Многие компании включают Gatekeeper как обязательный компонент Kubernetes‑платформы, чтобы гарантировать базовый уровень compliance на всех окружения. (Для Kubernetes существуют и альтернативы, например Kyverno, но OPA выгодно отличается универсальностью — единый Rego‑движок можно применять не только в кластере, но и в Terraform, CI‑пайплайнах и так далее)
Политики конечно мощный, и обращаться с ним надо осторожно. Неосторожно написанное правило способно парализовать работу кластера, например, запретив вообще все деплойменты (если промахнуться с условиями и задеть лишнее). Поэтому новые правила рекомендуется сначала запускать в щадящем режиме. В Gatekeeper можно включить audit‑режим, при котором нарушения лишь логируются, но не блокируют операцию.
Не забывайте мониторить сами политики в работе. Gatekeeper, к примеру, ведёт аудит‑логи и метрики Prometheus, где видно сколько раз и какие правила срабатывают. Анализ этих данных покажет, где политика слишком строгая или, наоборот, никогда не срабатывает (может, уже не актуальна).
Если вы работаете с инфраструктурой и стремитесь повысить надежность сервисов, обратите внимание на курс «SRE: практики и инструменты». Пройдите бесплатное тестирование по курсу, чтобы оценить свои знания и навыки.
Кроме того, 17 ноября в 20:00 состоится открытый урок «Инцидент‑менеджмент в SRE. Как быстро находить, устранять и предотвращать сбои в системе».
Записаться