# security-audit Сбор журнала аудита kube-apiserver с мастеров в Kafka-топик `security.k8s-audit`. Одно сообщение — одно событие аудита в JSON (`encoding: raw`, без OTLP-обёртки), обогащённое полями `sarex_*`. Дальнейшие этапы (processor → MinIO/Parquet) — отдельными компонентами. С observability-коллектором (`infrastructure/opentelemetry-collector`) не связан: отдельный релиз, namespace и конфиг. | Оверлей | Статус | |---|---| | `uralkal` | манифесты готовы, **в `clusters/uralkal` не подключён** | ## Что делает коллектор DaemonSet `audit-collector-agent` на узлах `node-role.kubernetes.io/control-plane`. Он читает `/var/log/audit/kube-apiserver-audit*.log` (текущий и ротированные файлы), позиции хранит в `/var/lib/otelcol-audit` (hostPath), туда же складывает дисковую очередь отправки. Обработка (`transform/audit`): - **вырезание шума:** `metadata.managedFields` и аннотация `kubectl.kubernetes.io/last-applied-configuration` в `requestObject` / `responseObject`; - **маскирование:** значения параметров `password|passwd|pwd|token|secret|api_key|api-key` в `requestURI` (`token=abc` → `token=***`), сама команда `exec` не маскируется; - **классификация** — поля ниже. Правила применяются по порядку, последнее подходящее побеждает. | Поле | Значение | Условие | |---|---|---| | `sarex_contour` | имя контура | env `CONTOUR` из оверлея | | `sarex_dry_run` | `true` / `false` | `requestURI` содержит `dryRun=All` | | `sarex_action` | `read` | `verb` ∈ `get`, `list` | | | `mutate` | `verb` ∈ `create`, `update`, `patch`, `delete`, `deletecollection` | | | `status` | `objectRef.subresource == status` | | | `interactive` | `objectRef.subresource` ∈ `exec`, `attach`, `portforward` | | `sarex_outcome` | `applied` | код 2xx, не dry-run | | | `dry_run` | код 2xx, dry-run | | | `denied_authn` | 401 | | | `denied_authz` | 403 и `authorization.k8s.io/decision=forbid` | | | `denied_admission` | 4xx при `decision=allow`, кроме 404/409/422 | | | `invalid` | 422 | | | `conflict` | 409 | | | `not_found` | 404 | | | `error` | 5xx | | `sarex_actor_type` | `human` | по умолчанию | | | `serviceaccount` | `system:serviceaccount:*` | | | `controller` | `system:serviceaccount:kube-system:*`, `system:kube-*`, `system:apiserver` | | | `node` | `system:node:*` | | | `gitops` | `system:serviceaccount:flux-system:*` | `watch` не попадает ни в одну категорию `sarex_action`: поле не выставляется. Надёжность доставки: `required_acks: -1`, дисковая очередь (`sending_queue.storage`), бесконечные ретраи. При заполненной очереди срабатывает backpressure на `filelog`, чтение файла приостанавливается. Данные теряются, только если файл успеет уйти из ротации (10 × 100 МБ) раньше, чем Kafka вернётся. ## Предусловия подключения в контуре 1. **Аудит apiserver включён** (INFRA-3057): флаги `--audit-policy-file`, `--audit-log-path=/var/log/audit/kube-apiserver-audit.log`, `--audit-log-maxsize=100`, `--audit-log-maxbackup=10`. Каталог `/var/log/audit` есть на всех мастерах. 2. **Kafka:** топик и SCRAM-пользователь. - Топик `security.k8s-audit`: 1 партиция, `retention.ms=259200000` (3 суток), `max.message.bytes=5242880`, RF по контуру (в `uralkal` один controller → RF 1). - Пользователь `security-audit` (SCRAM-SHA-512). ACL не нужны. Заводится terraform'ом: `infra/terraform`, ветка `contour`, модуль `kafka-topics-incluster`, блок `environments.<контур>` в `infrastructure.yaml`. Если нужно вручную — из пода контроллера, через внутренний листенер (`INTERNAL`, 9094, `SASL_PLAINTEXT`/`PLAIN`, `inter_broker_user`): ```bash kubectl -n kafka exec -it kafka-kafka-contour-controller-0 -c kafka -- bash ``` ```bash cat > /tmp/admin.properties <]' rm /tmp/admin.properties ``` 3. **Vault** (`secrets/` — KV v2): - секрет `secrets/data/security-audit/kafka` с ключами `username`, `password`, `ca_crt`; `ca_crt` — содержимое `ca.crt` из Secret `kafka/kafka-kafka-contour-tls` (CA генерирует чарт `kafka-contour`, срок 365 дней; при перевыпуске обновить и здесь); - политика `read` на `secrets/data/security-audit/kafka`; - роль `security-audit` в `auth/kubernetes`: bound SA `audit-collector`, namespace `security-audit`, политика выше. Заводится в `infra/terraform`, ветка `contour`, модуль `vault-platform-secrets` (роли `vault_kubernetes_auth_backend_role`, KV-секреты). 4. **Образ** `cr.yandex/crp3ccidau046kdj8g9q/opentelemetry-collector/opentelemetry-collector-contrib:0.121.0-amd64` доступен в registry контура (тот же, что дефолт чарта `opentelemetry-collector-prod` 0.117.1), `regcred` есть в namespace `security-audit`. ## Подключение Отдельным MR после выполнения предусловий — добавить в `clusters/uralkal/kustomization.yaml`: ```yaml - ../../infrastructure/security-audit/uralkal ``` `dependsOn` в оверлее ждёт HelmRelease `kafka/kafka` и `vault/vault`. ## Проверка после подключения - DaemonSet `security-audit/audit-collector-agent` только на мастерах, все поды `Ready`: `kubectl -n security-audit get pods -o wide`. - В логах коллектора нет ошибок: `kubectl -n security-audit logs ds/audit-collector-agent -c opentelemetry-collector-prod`. - События в топике (из пода контроллера Kafka, с `admin.properties` выше): `kafka-console-consumer.sh --bootstrap-server localhost:9094 --consumer.config /tmp/admin.properties --topic security.k8s-audit --max-messages 5`. - `kubectl create secret generic audit-probe --from-literal=k=v` → событие с `objectRef.resource=secrets` уровня `Metadata`, без `requestObject`/`responseObject`. - Рестарт пода коллектора (`kubectl delete pod`) → нет дублей и пропусков: сверить `auditID` в топике с файлом журнала на мастере за интервал рестарта. ## Известное ограничение Значения env с паролями внутри `requestObject` / `responseObject` workload-ресурсов на уровне `RequestResponse` коллектор не маскирует: OTTL плохо работает с массивами (`spec.containers[].env[]`). Маскирование будет в processor'е. До его готовности не повышать уровень для workload-ресурсов выше `Request` в контурах, где пароли могут быть в env. ## Проверить перед подключением Сверено по репозиторию и чартам 01.11.2026, доступа к кластеру не было. | Что | Результат | Как проверено | |---|---|---| | Образ и `command.name` | `.../opentelemetry-collector/opentelemetry-collector-contrib:0.121.0-amd64`, `otelcol-contrib` | дефолт `values.yaml` чарта `opentelemetry-collector-prod` 0.117.1 (appVersion 0.120.0, тег образа 0.121.0); `otelcol-contrib components` в образе: filelog, transform, kafka, file_storage, health_check, memory_limiter, batch есть | | Синтаксис kafka exporter | для 0.121.0 корректен (`topic`/`encoding` наверху, `auth.tls`) | `otelcol-contrib validate` в том же образе — OK | | Kafka Service и порт | `kafka-kafka-contour.kafka.svc.cluster.local:9092`, `SASL_SSL` (**не** `kafka.kafka...`) | `helm template` чарта `kafka-contour` 0.1.15 с values `uralkal`; тот же адрес у `apps/*/uralkal` | | CA Kafka | Secret `kafka/kafka-kafka-contour-tls`, ключ `ca.crt` (helm `genCA`, 365 дней) | шаблон `tls-secret.yaml` чарта; `apps/issues/uralkal` монтирует его же | | Kafka 4.0.0 и `protocol_version: 2.8.0` | совместимо (Kafka 4 требует ≥ 2.1) | `appVersion` чарта `kafka-contour` | | Vault | HelmRelease `vault/vault`; injector ходит на `http://vault-vault-contour.vault.svc:8200` | `infrastructure/vault/uralkal`, `helm template` `vault-contour` 0.2.3 | | Узлы мастеров | метка и taint `node-role.kubernetes.io/control-plane` | так же сажается `istio-gateway/uralkal` | | Рендер чарта | нет `hostPort`, Service, ClusterRole; nodeSelector/tolerations/securityContext как заданы | `helm template` 0.117.1 с values из собранного HelmRelease | | Логика `transform` | 5 тестовых событий классифицированы верно, `token=***`, `managedFields` вырезаны | запуск коллектора локально в том же образе с `file`-экспортёром вместо kafka | Остаётся открытым: - `TODO(verify)` **nodelocaldns** (`169.254.25.10`) в NetworkPolicy: в репозитории не подтверждается. При дефолте kubespray включён, если нет — правило безвредно. - Некритичные предупреждения 0.121.0: `service::telemetry::metrics::address` устарел (так же во всех коллекторах репо); OTTL-пути без префикса `log.` (контекст выводится автоматически).