gendesign/docs/runbooks/basic_auth_management.md
lekss361 6f36f298ec fix(security): log_credentials + auth_audit.log → username из 401
- Caddyfile: global servers { log_credentials } — Caddy перестаёт
  редактировать Authorization header в "REDACTED"
- Caddyfile: отдельный log auth_audit { } → /var/log/caddy/auth_audit.log
  (retention 7d, roll 10MiB×3) для изоляции auth-событий
- docker-compose.prod.yml: CADDY_LOG_FILE → auth_audit.log (forwarder
  читает меньший файл, снижает false reads от non-auth requests)
- forwarder.py: обновлены docstrings — требования к log_credentials явные
- docs/runbooks: секция «Аудит логи» + security note про log_credentials

Результат: _extract_attempted_username получает raw Basic auth header
(не "REDACTED") → attempted_username tag в GlitchTip event заполнен.

Caddy validate: Valid configuration (caddy:2 v2.11.3).
2026-05-23 12:51:53 +03:00

132 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Basic Auth Management Runbook
**Установлено**: 2026-05-23 (PR #426, commit `cc2d148`)
**Realm**: `"GenDesign Pilot"`
**URL**: <https://gendsgn.ru/>
## Добавить пользователя
```bash
./scripts/auth/add_user.sh <username>
# Вводи пароль интерактивно; скрипт добавляет в caddy/users.caddy.snippet
```
После: commit snippet → PR → merge → GHA deploy → Caddy reload (автоматически в deploy.yml).
## Удалить пользователя
1. `./scripts/auth/remove_user.sh <username>` ASAP
2. Commit + push + PR (merge приоритетным)
3. `docker compose exec caddy caddy reload` после deploy
4. Сгенерировать новый credential через `add_user.sh`
5. Уведомить остальных стейкхолдеров о замене
## Просмотр текущих пользователей
```bash
./scripts/auth/list_users.sh
```
## Credencial rotation
При компрометации пароля: remove_user + add_user с новым паролем + PR-merge + разослать новый credential.
## Аудит логи
- **Auth audit**: `/var/log/caddy/auth_audit.log` — все события basic_auth (включая raw Authorization).
Содержит plain credentials в Base64 — accessible только root на VPS.
Используется forwarder'ом (см. `ops/glitchtip-auth-forwarder/forwarder.py`).
Retention: 7 дней (roll_size 10MiB, keep 3, 168h).
- **Access log**: `/var/log/caddy/gendsgn.ru.log` — все requests, format JSON.
Тоже содержит raw Authorization из-за global `log_credentials` (Caddy 2.x — опция только global, нельзя per-block).
Retention: 30 дней (roll_size 50MiB, keep 5, 720h).
Failed auth последние 100:
```bash
docker compose -f docker-compose.prod.yml exec caddy \
grep -P '"status":401' /var/log/caddy/auth_audit.log | tail -100 | jq
```
### Security note
`log_credentials` — опция под `servers { }` global block, нельзя per-virtual-host. Plain Base64 credentials попадают в **оба** log файла. Mitigation:
- VPS root-only (только SSH key authorized owner)
- Retention: gendsgn.ru.log = 30 дней, auth_audit.log = 7 дней
- Не выгружать логи на внешние системы без redaction
## Диагностика
```bash
# Посмотреть 401-ошибки в auth audit log
ssh gendesign 'docker compose -p gendesign exec caddy tail -f /var/log/caddy/auth_audit.log' | grep '"status":401'
# Reload конфига Caddy без перезапуска
ssh gendesign 'docker compose -p gendesign exec -T caddy caddy reload --config /etc/caddy/Caddyfile'
```
## GlitchTip auth event forwarding
Sidecar `gendesign-auth-forwarder` шлёт 401 события в GlitchTip как Sentry warning events.
- Состояние: `docker compose -p gendesign ps | grep auth-forwarder`
- Logs: `docker compose -p gendesign logs -f glitchtip-auth-forwarder`
- GlitchTip dashboard: <https://errors.gendsgn.ru/> — фильтр `event_type:basic_auth_failed`
- Storm digest: при >10 failed auth за 60s → отдельный `event_type:basic_auth_storm` event
- Мониторы (GlitchTip uptime, SentryUptimeBot) — получают 401 стабильно из-за отсутствия credentials; они фильтруются как individual events, учитываются только в storm digest
Если sidecar упал — failed auth события **не теряются** (offset persistent в `auth_forwarder_state` volume), но в GlitchTip не доезжают пока container down.
### Перезапуск forwarder
```bash
ssh gendesign 'docker compose -p gendesign restart glitchtip-auth-forwarder'
```
### Переменные окружения forwarder
Задаются в `/opt/gendesign/.env` на VPS:
| Переменная | Описание | Обязательна |
|---|---|---|
| `GLITCHTIP_DSN` | Sentry DSN GlitchTip (project backend) | Да |
## Deploy
Sidecar собирается локально на VPS на каждом запуске `deploy.yml` (после merge в main).
Не требует pre-push image build, не использует GHCR.
Forwarder контейнер пересоздаётся `--force-recreate` чтобы подхватить новый image при изменении кода.
В случае sidecar не запустился:
```bash
ssh gendesign 'docker compose -p gendesign -f docker-compose.prod.yml logs --tail=100 glitchtip-auth-forwarder'
```
Если build fail на VPS — проверь `ops/glitchtip-auth-forwarder/Dockerfile` локально:
```bash
docker build ops/glitchtip-auth-forwarder/
```
## Phase 2 — реализовано (PR #430)
GlitchTip forwarder sidecar добавлен в `ops/glitchtip-auth-forwarder/`.
Архитектура: Python 3.12 slim, sentry-sdk 2.18, tail live Caddy log, offset в named volume.
Deploy автоматизирован через `deploy.yml` — ручной SSH на VPS не нужен.
## Phase 3 — реализовано (PR #436)
`log_credentials` + `auth_audit.log` → forwarder получает реальный username из 401.
- Global `log_credentials` в Caddyfile — Caddy перестаёт редактировать Authorization header.
- Отдельный `auth_audit.log` (retention 7d) — forwarder читает именно его.
- `_extract_attempted_username` декодирует Basic auth → `attempted_username` tag в GlitchTip event.
- Результат: GlitchTip event получает `attempted_username:testbruteforce` вместо `(none)`.
## Phase 4 (TODO)
- При >30 users — миграция на OAuth/NextAuth
- Разделение admin/user по доступу через Caddy path matcher или app-level RBAC