Перейти к содержимому

Как настроить kubernetes audit policy без ошибок и опасных исключений

Как не превратить Kubernetes Audit Policy в решето: разбор типовых ошибок

Аудит-политика Kubernetes часто появляется в кластере один раз - на этапе первоначальной настройки. Затем её постепенно дополняют исключениями для новых контроллеров, операторов и сетевых компонентов, но целиком пересматривают крайне редко. Через несколько лет такой файл может превратиться в настоящий археологический слой: правила продолжают работать, а комментарии вроде `temporary` и `TODO remove` давно потеряли связь с реальностью.

Проблема не только в размере конфигурации. Неудачно составленная Audit Policy способна одновременно перегружать хранилище логов и скрывать действительно опасные действия. В результате команда получает много технического шума, но не видит изменения RBAC, удаление важных объектов или подозрительную активность скомпрометированной сервисной учётной записи.

Ниже - практический разбор принципов, которые помогают проверить политику и не превратить её в набор случайных исключений.

Как устроена Audit Policy

Audit Policy Kubernetes представляет собой последовательность правил. Каждое правило определяет, к каким запросам оно применяется: учитываются пользователь, группы, verbs, ресурсы, namespace и другие параметры. Для совпавшего запроса задаётся уровень аудита:

- `None` - событие не записывается;
- `Metadata` - сохраняются сведения о запросе, пользователе, ресурсе и результате, но не тела запроса и ответа;
- `Request` - дополнительно фиксируется тело запроса;
- `RequestResponse` - записываются и запрос, и ответ целиком.

Самая важная особенность заключается в порядке обработки. Правила не суммируются и не применяются независимо друг от друга. Kubernetes проверяет их сверху вниз и использует первое совпавшее правило. Иными словами, логика работает по принципу `if / else if / else`.

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

Принцип первый: отделяйте шум от полезных событий

В рабочем кластере значительная часть API-трафика приходится на операции `get`, `list` и `watch`. Kubelet получает сведения о состоянии узлов, контроллеры отслеживают объекты, системы мониторинга регулярно обращаются к API, а операторы наблюдают за собственными ресурсами.

Если записывать все такие запросы на высоком уровне детализации, поток аудита быстро превратится в шум. Хранилище будет заполняться повторяющимися событиями, а поиск реального инцидента станет сложнее.

Рациональная стратегия - подавлять только заведомо безопасный и предсказуемый трафик. Однако исключения необходимо делать максимально узкими: ограничивать их конкретным пользователем или ServiceAccount, перечислять допустимые verbs и по возможности указывать ресурсы.

Опасность широкого исключения

Один из распространённых вариантов выглядит примерно так:

```yaml
- level: None
users:
- system:serviceaccount:monitoring:collector
```

На первый взгляд кажется, что правило отключает шум от сборщика метрик. На деле оно гасит аудит всех действий этой учётной записи - не только чтения, но и потенциальных изменений.

Если позднее сервисному аккаунту по ошибке добавят права на изменение объектов, его действия также не попадут в журнал. То же произойдёт при компрометации токена: злоумышленник сможет выполнять операции в пределах выданных полномочий, а аудит не зафиксирует активность.

Безопаснее явно обозначить границы:

```yaml
- level: None
users:
- system:serviceaccount:monitoring:collector
verbs:
- get
- list
- watch
```

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

Комментарии не заменяют проверку логики

Комментарии в YAML помогают ориентироваться в большом файле, но иногда они создают ложное ощущение безопасности. Например, рядом с правилом может быть написано `critical to monitor`, хотя само правило имеет уровень `None` и фактически отключает аудит для указанных сервисных аккаунтов.

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

Комментарии должны отвечать реальной логике конфигурации:

- объяснять, почему правило существует;
- указывать, какой шум оно подавляет;
- фиксировать ограничения по verbs и ресурсам;
- содержать причину исключения, а не только его название;
- удаляться или обновляться вместе с изменением архитектуры.

Полезно проводить ревью комментариев так же внимательно, как ревью самого YAML.

Удаляйте мёртвые правила

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

Правило для больше не существующего `system:kube-router` после миграции на Cilium, скорее всего, никогда не сработает. Оно не обязательно создаёт прямую уязвимость, но:

- увеличивает объём конфигурации;
- затрудняет анализ порядка правил;
- вводит в заблуждение при расследовании;
- скрывает историю реальных изменений;
- создаёт риск копирования устаревшего шаблона.

Периодическая инвентаризация должна включать проверку существования пользователей, групп, ServiceAccount, namespaces и компонентов, упомянутых в политике. Всё, что больше не используется, лучше удалить, а не оставлять "на всякий случай".

Принцип второй: чувствительные данные требуют осторожности

Для большинства операций достаточно уровня `Metadata`. Он позволяет понять, кто, когда и с каким объектом работал, не записывая содержимое запроса и ответа.

Это особенно важно для секретов. Использование `Request` или `RequestResponse` при обращении к объектам типа `Secret` может привести к попаданию конфиденциальных значений в систему аудита. Даже если само хранилище логов защищено, круг людей и сервисов, имеющих к нему доступ, часто шире, чем круг пользователей Kubernetes.

Для чувствительных ресурсов следует заранее определить:

- какие действия нужно видеть;
- нужна ли детализация тела запроса;
- могут ли данные содержать токены или пароли;
- кто получит доступ к аудит-логам;
- как долго эти события будут храниться.

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

Принцип третий: RequestResponse оправдан не всегда

Высокий уровень аудита полезен для отдельных операций, где важно знать не только факт действия, но и его содержимое. Например, это может быть создание или изменение критичных объектов, связанных с RBAC, admission-контролем или настройками безопасности.

Однако включать `RequestResponse` для всего API-трафика не стоит. Такой режим:

- резко увеличивает объём логов;
- повышает стоимость хранения и обработки;
- может раскрывать конфиденциальные данные;
- усложняет поиск нужных событий;
- создаёт дополнительную нагрузку на компоненты аудита.

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

Принцип четвёртый: порядок правил должен быть осознанным

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

1. Какое правило сработает первым?
2. Не перекрывается ли оно более широким исключением?
3. Что произойдёт после изменения RBAC?
4. Будет ли видна операция при компрометации ServiceAccount?
5. Не скрывает ли общее правило действия с критичными ресурсами?

Особое внимание нужно уделять правилам `level: None`, расположенным в верхней части файла. Они наиболее опасны, если ограничены только пользователем или группой. Узкое правило для изменения роли, привязки полномочий или удаления объекта должно находиться выше общего исключения, если они могут совпадать по субъекту.

Полезно мыслить не категориями "правило правильное или неправильное", а категориями "какие запросы оно перехватывает раньше остальных".

Не забывайте о действиях, которые часто недооценивают

Аудит должен покрывать не только очевидные изменения Deployment или Pod. Для расследования инцидентов особенно важны:

- создание и изменение `Role`, `ClusterRole`, `RoleBinding` и `ClusterRoleBinding`;
- выдача прав сервисным аккаунтам;
- изменение admission-настроек;
- удаление событий и объектов, связанных с расследованием;
- операции с конфигурациями контроллеров;
- неожиданные действия от имени системных пользователей;
- массовые удаления ресурсов;
- обращения к секретам из необычных namespaces.

Отдельно стоит проверять операции удаления. Именно удаление может уничтожить следы активности, нарушить работу приложения или скрыть признаки компрометации. Даже если обычные чтения подавляются, мутации и удаления должны оставаться видимыми.

Проверяйте политику на реальных запросах

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

Для проверки можно составить небольшую таблицу сценариев:

| Сценарий | Ожидаемый уровень |
|---|---|
| Системный компонент выполняет `get/list/watch` | `None` или минимальный уровень |
| Пользователь изменяет RBAC | `Metadata` или выше |
| Удаляется критичный объект | `Metadata` |
| Создаётся объект с чувствительными данными | Уровень с учётом риска раскрытия |
| Сервисный аккаунт выполняет неожидаемую мутацию | Обязательно фиксируется |

Тестировать следует не только штатное поведение, но и отрицательные сценарии: что будет, если тот же ServiceAccount выполнит `patch`, `delete` или `create`.

Учитывайте жизненный цикл конфигурации

Audit Policy не должна быть ручным файлом, который меняют непосредственно на сервере. Её необходимо хранить вместе с инфраструктурным кодом, проверять на ревью и обновлять при изменениях в архитектуре кластера.

Полезные практики:

- валидировать YAML до применения;
- проверять отсутствие дублирующихся и недостижимых правил;
- документировать причину каждого исключения;
- связывать исключения с конкретным компонентом;
- пересматривать политику после миграций;
- контролировать срок хранения аудит-логов;
- ограничивать доступ к системе логирования;
- отслеживать изменения самой Audit Policy.

Отдельно важно помнить: безопасность зависит не только от того, что записывается, но и от того, кто может читать или удалять эти записи. Аудит-лог, доступный тем же администраторам, чьи действия он должен контролировать, не обеспечивает полноценной независимости контроля.

Мини-чек-лист ревью

Перед публикацией изменений в политике проверьте:

- правила идут в правильном порядке;
- широкие исключения не перекрывают критичные действия;
- каждое подавление ограничено пользователем, verbs и ресурсами;
- для системных чтений явно перечислены `get`, `list`, `watch`;
- мутации и удаления не скрываются случайно;
- секреты не записываются в теле запросов без необходимости;
- `RequestResponse` используется только для обоснованных сценариев;
- комментарии соответствуют фактическому поведению;
- отсутствуют правила для удалённых компонентов и сервисных аккаунтов;
- политика протестирована на реальных и негативных сценариях;
- доступ к аудит-логам защищён не слабее, чем доступ к самому API.

Хорошая Kubernetes Audit Policy - это не самая подробная конфигурация и не самая короткая. Её задача - сохранить важные свидетельства, не утонув в потоке повторяющихся технических запросов. Узкие исключения, корректный порядок правил, осторожное обращение с чувствительными данными и регулярное удаление устаревших фрагментов позволяют превратить аудит из формального журнала в рабочий инструмент обнаружения и расследования инцидентов.

Прокрутить вверх