Привет, Хабр!

Представим, что вы отвечаете за десятки конфигурационных файлов 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. Как быстро находить, устранять и предотвращать сбои в системе».
Записаться

Комментарии (0)