Перейти к содержанию

Аудит и журнал действий

Журнал аудита фиксирует все административные действия в AppSec.AIGate: создание, изменение и удаление ресурсов (провайдеров, профилей, политик, экспортеров, пользователей). Это ключевой инструмент для расследования инцидентов, контроля действий персонала и выполнения требований 152-ФЗ по ведению журнала учёта обработки ПДн.

Отличие от событий безопасности

События безопасности фиксируют инциденты в потоке данных (jailbreak, PII, блокировки). Аудит фиксирует действия администраторов в интерфейсе управления.

Что логируется

AuditLogMiddleware автоматически перехватывает все запросы к Admin API и записывает аудитную запись.

Логируемые HTTP-методы:

Метод Действие в журнале Пример
POST CREATE Создание нового провайдера, профиля, экспортера
PUT UPDATE Полное обновление ресурса
PATCH UPDATE Частичное обновление (изменение одного поля)
DELETE DELETE Удаление ресурса

GET-запросы не логируются — они не изменяют состояние системы и генерировали бы слишком большой объём записей.

Исключённые эндпоинты (не логируются для любых методов):

Путь Причина исключения
/health Технический endpoint для мониторинга (вызывается каждые несколько секунд)
/metrics Prometheus metrics endpoint
/docs Swagger UI
/openapi.json OpenAPI schema
/redoc ReDoc UI

GET-запросы не логируются — они не изменяют состояние системы.

Поля аудитной записи

Каждая запись содержит полный контекст действия — кто, что, когда, откуда и с каким результатом.

Поле Тип Описание Пример
id UUID Уникальный идентификатор записи 550e8400-e29b-41d4-a716-446655440000
user_id UUID Идентификатор пользователя, выполнившего действие a1b2c3d4-...
user_email string Email пользователя (для удобства поиска без JOIN) admin@company.ru
user_role string Роль пользователя на момент действия admin, operator, viewer
action enum Тип действия CREATE, UPDATE, DELETE
resource_type string Тип ресурса, над которым выполнено действие providers, profiles, policies, exporters, reports, users, retention_settings
resource_id UUID Идентификатор затронутого ресурса b2c3d4e5-...
request_method string HTTP-метод запроса POST, PUT, PATCH, DELETE
request_path string URL-путь запроса /api/v1/providers/b2c3d4e5-...
request_body JSON Тело запроса с маскированными секретами (см. Маскирование секретов) {"name": "OpenAI", "api_key": "***REDACTED***"}
response_status int HTTP-статус ответа 200, 201, 400, 500
source_ip string IP-адрес клиента (из X-Forwarded-For или прямое соединение) 10.0.1.50
timestamp datetime Время выполнения запроса (UTC, ISO 8601) 2026-03-13T14:30:00Z

Просмотр журнала

Журнал аудита доступен через API. Доступ — только для роли admin.

# Последние 50 записей
curl "http://localhost:8001/api/v1/audit-log?limit=50&offset=0"

# Фильтр по пользователю
curl "http://localhost:8001/api/v1/audit-log?user_id=a1b2c3d4-..."

# Фильтр по типу действия и ресурса
curl "http://localhost:8001/api/v1/audit-log?action=DELETE&resource_type=providers"

Параметры фильтрации:

Параметр Обязательный Допустимые значения По умолчанию Описание
user_id нет UUID пользователя Показать действия только указанного пользователя
action нет CREATE, UPDATE, DELETE все Фильтр по типу действия
resource_type нет providers, profiles, policies, exporters, reports, users, retention_settings все Фильтр по типу ресурса
limit нет 1–1000 50 Количество записей на страницу
offset нет 0+ 0 Смещение для пагинации

Результат: массив аудитных записей, отсортированных по timestamp DESC (новые — первые).

Совет для расследования

Если произошло несанкционированное изменение, выполните запрос с resource_id изменённого ресурса — вы получите полную историю всех действий над ним, включая кто и когда создал, изменял и удалял.

Маскирование секретов

При записи request_body в журнал аудита middleware автоматически маскирует чувствительные поля. Это предотвращает утечку секретов через журнал аудита.

Маскируемые поля:

Имя поля (регистронезависимо) Что заменяется Результат
password Значение целиком ***REDACTED***
secret Значение целиком ***REDACTED***
token Значение целиком ***REDACTED***
api_key Значение целиком ***REDACTED***

Пример: запрос на создание провайдера с api_key будет записан в журнал как:

{
  "name": "OpenAI Production",
  "base_url": "https://api.openai.com",
  "api_key": "***REDACTED***",
  "model": "gpt-4"
}

Ограничение

Маскирование работает только на первом уровне вложенности JSON. Если секрет находится внутри вложенного объекта (например, {"config": {"api_key": "sk-..."}}), он также будет маскирован — поиск выполняется рекурсивно по всему дереву JSON.

Хранение и ротация

  • Записи аудита хранятся audit_retention_days дней (по умолчанию 1095 дней = 3 года).
  • Минимальное значение — 1095 дней (требование 152-ФЗ, система не позволит уменьшить).
  • Очистка выполняется автоматически воркером admin-api-retention-worker (см. Настройки хранения данных).
  • Записи удаляются только по истечении retention — ручное удаление невозможно (защита от уничтожения доказательной базы).

Права доступа

Права доступа (RBAC):

Действие admin operator viewer Комментарий
Просмотр журнала аудита да нет нет Только admin имеет доступ к журналу
Скачивание / экспорт нет нет нет Экспорт не предусмотрен через UI. Используйте API + jq для формирования выгрузки

Почему только admin?

Журнал аудита содержит информацию обо всех административных действиях, включая действия других операторов. Ограничение доступа предотвращает ситуацию, когда оператор видит действия коллег или пытается скрыть свои.

Типичные проблемы

Проблема Причина Решение
Журнал пуст, хотя действия выполнялись Middleware не подключен или запросы идут мимо Admin API Проверьте, что AuditLogMiddleware активен в конфигурации сервиса
В request_body видны реальные пароли Поле в JSON названо нестандартно (например, pwd вместо password) Добавьте имя поля в список маскирования в конфигурации middleware
Запись с source_ip: 127.0.0.1 Reverse proxy не передаёт X-Forwarded-For Настройте Nginx: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Нет записей о DELETE Soft delete не генерирует HTTP DELETE Проверьте, использует ли ресурс мягкое удаление (PATCH с is_active: false) — в этом случае запись будет с action=UPDATE