# 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
