Почему правила управления инфраструктурой должны быть кодом

В вашей команде есть правило: ни одна группа безопасности не должна открывать SSH (порт 22) для всего интернета. Все согласны. Это записано в документации. Кто-то даже распечатал это и повесил на стену.

Но в пятницу после обеда разработчик создает новую группу безопасности для быстрого теста. Он указывает CIDR-блок 0.0.0.0/0 на порту 22, потому что хочет просто быстро что-то проверить. Изменение проходит. Никто не замечает до утра понедельника, когда команда безопасности получает оповещение об открытом SSH-порте.

Такой сценарий повторяется в разных командах каждую неделю. Не потому что люди невнимательны, а потому что ручное соблюдение политик не масштабируется. Когда изменения инфраструктуры происходят несколько раз в день, полагаться на человеческую память или правила в документах — верный путь к пробелам в безопасности.

Проблема политик в документах

Большинство команд начинают с хранения политик в документах. Google Doc, страница в Confluence или PDF в общей папке. В этих документах описано, что должно и не должно происходить: «Все S3-бакеты должны быть зашифрованы», «Можно использовать только одобренные AMI», «У каждого ресурса должен быть тег с кодом центра затрат».

Проблема в том, что документы ничего не enforcing. Они описывают намерения, но не выполняют их. Когда кто-то вносит изменение, нарушающее политику, документ не может его остановить. Он просто лежит, молча ожидая, пока кто-то вспомнит его проверить.

Документы также устаревают. Кто-то обновляет политику, но старая версия остается в закладках. Или команда растет, и новые участники даже не знают о существовании документа. Со временем разрыв между тем, что задокументировано, и тем, что реально развернуто, только увеличивается.

Что значит писать политики как код

Policy as Code — это превращение правил в формат, который машины могут читать и выполнять. Вместо документа «не открывайте SSH для всего мира» вы пишете правило, которое автоматически проверяет каждое изменение инфраструктуры на соответствие этому ограничению.

Политика живет в файле, как и ваш код приложения. Она проходит через систему контроля версий. Она проверяется в pull request'ах. Ее можно тестировать, обновлять и откатывать. Когда кто-то предлагает изменение, нарушающее политику, пайплайн перехватывает его до создания ресурса.

Этот переход от документа к коду меняет то, как команды взаимодействуют с правилами. Политики становятся частью инженерного процесса, а не запоздалой мыслью, которую кто-то проверяет во время аудита.

Конкретный пример с Open Policy Agent

Давайте сделаем это осязаемым. Open Policy Agent (OPA) — популярный инструмент для написания политик как кода. Он использует язык Rego. Вот как выглядит простое правило, блокирующее SSH-доступ отовсюду:

deny if {
    input.resource.type == "aws_security_group_rule"
    "0.0.0.0/0" in input.resource.cidr_blocks
    input.resource.port == 22
}

Это правило гласит: если кто-то пытается создать правило группы безопасности, открывающее порт 22 для всех IP-адресов, пометить его как запрещенное. Файл политики находится в вашем репозитории. Когда приходит pull request, добавляющий правило группы безопасности, CI-пайплайн запускает OPA для проверки предложенных изменений. Если правило срабатывает, пайплайн падает, и разработчик получает немедленную обратную связь.

Вы также можете написать обратное правило, разрешающее только определенные CIDR-блоки для SSH:

allow if {
    input.resource.type == "aws_security_group_rule"
    input.resource.cidr_blocks[_] != "0.0.0.0/0"
}

Точный синтаксис зависит от вашего инструмента и языка политик, но паттерн одинаков: правила — это код, и код выполняется автоматически.

Другой подход: Sentinel для пользователей Terraform

Если ваша команда активно использует Terraform, HashiCorp Sentinel предлагает более тесную интеграцию. Политики Sentinel пишутся специально для контекста выполнения Terraform. Вот то же ограничение SSH в Sentinel:

import "tfplan"

allowed_cidrs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]

main = rule {
    all tfplan.resource_changes as _, change {
        change.type is "aws_security_group_rule" implies
        change.change.after.cidr_blocks all allowed_cidrs
    }
}

Эта политика проверяет, что каждое правило группы безопасности использует только частные IP-диапазоны. Если кто-то попытается использовать публичный CIDR-блок, политика заблокирует изменение.

Разница между OPA и Sentinel в основном в экосистеме. OPA — универсальный инструмент, работающий со многими инструментами помимо Terraform. Sentinel глубоко интегрирован с продуктами HashiCorp, что упрощает настройку, если вы уже в этой экосистеме. Но основная идея идентична: политики — это код, который выполняется в вашем пайплайне.

Где хранить файлы политик

У вас есть два основных варианта размещения файлов политик:

В том же репозитории, что и код инфраструктуры. Это держит политики рядом с ресурсами, которыми они управляют. Когда кто-то изменяет инфраструктуру, он видит соответствующие политики в том же pull request'е. Это хорошо работает для политик, специфичных для команды.

В выделенном репозитории политик. Это централизует все политики в организации. Платформенная команда поддерживает репозиторий, а несколько репозиториев инфраструктуры забирают политики из него. Это лучше работает для правил соответствия на уровне организации, которые не должны различаться между командами.

Оба подхода валидны. Начните с подхода «один репозиторий», если вы новичок в Policy as Code. Переходите к выделенному репозиторию, когда вам нужно последовательно применять политики в нескольких командах.

Практический чеклист для начала

Прежде чем погружаться в написание политик, пройдитесь по этому чеклисту:

  • Определите три самых нарушаемых политики. Не пытайтесь кодифицировать всё сразу. Выберите правила, которые причиняют больше всего боли или риска.
  • Выберите один инструмент. Начните с OPA, если вам нужна гибкость между инструментами. Начните с Sentinel, если вы глубоко инвестировали в Terraform.
  • Напишите одну политику и протестируйте ее вручную. Запустите ее против известного нарушения, чтобы убедиться, что она ловит проблему.
  • Добавьте проверку политики в ваш CI-пайплайн. Сделайте так, чтобы она блокировала сборку, а не просто предупреждала. Предупреждения игнорируются.
  • Проверяйте и итерируйте. Через неделю проверьте, не поймала ли политика что-то неожиданное. Исправьте ложные срабатывания.

Реальная ценность в рабочем процессе

Выбранный инструмент имеет меньшее значение, чем принятый рабочий процесс. Когда политики — это код, они получают такое же обращение, как и код приложения. Они проверяются, тестируются, версионируются и улучшаются со временем. Когда политика вызывает ложное срабатывание, кто-то открывает pull request для ее исправления. Когда появляется новое требование соответствия, кто-то пишет новое правило и отправляет его через тот же пайплайн.

Этот рабочий процесс устраняет разрыв между намерением политики и фактическим ее применением. Правило «не открывать SSH для всего мира» больше не является документом, который кто-то может забыть проверить. Это строка кода, которая выполняется каждый раз при изменении инфраструктуры. В этом разница между надеждой, что ваша команда следует правилам, и уверенностью, что она это делает.