Аудит и журнал действий¶
Журнал аудита фиксирует все административные действия в 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 |