# md2vk — Безопасность ## Два уровня доступа ### 1. Внешний — Caddy basic_auth (на vps02) - Весь https://md2vk.nixg.ru закрыт HTTP Basic Auth. - Пользователь: **estorozhenko**; пароль — у пользователя, в конфиге только bcrypt-хэш. - Хэш в Caddyfile: `admin $2a$14$uzlQxJdcRZJ5gDpGG5B26OXnudIme1IYe7xh0WtDwFtbB6XiiDYTS` (сгенерирован 2026-09-18 `docker exec caddy caddy hash-password --plaintext '<пароль>'`). - Без basic auth сервис наружу не отдаётся. ### 2. Внутренний — API-ключ (FastAPI) - Все эндпоинты `/api/v1/*` (кроме `/health`) требуют API-ключ `md2vk_...`: - в заголовке `Authorization: Bearer ` (GET) или в теле `{"api_key": ...}` (POST); - API-ключ даёт право публиковать, но **не позволяет прочитать VK-токен** (raw токен не возвращается ни одним эндпоинтом). - Хранится только SHA-256 хэш (`api_key_hash`), сравнение constant-time (`hmac.compare_digest`). - Пользователь estorozhenko + API-ключ: создаётся скриптом, ключ кладётся в `secrets/estorozhenko_api_key.txt` (не в git). ## Блокировка после 5 неудачных попыток (fail2ban на vps02) - На vps02 установлен fail2ban, jail `md2vk`: - следит за логами Caddy (JSON: `/var/log/caddy/access.log`), - фильтр: basic auth failure (`401` с `"err"`, absence of `"user_id"`): ``` ^.*"status":401.* ``` - правило: ban IP после **5 неудачных попыток подряд** (`maxretry=5`, `findtime=600`), - наказание: **ban до ручного снятия** (`bantime = -1`), - действие: `iptables-allports` (ban на уровне ядра, DROP). - **Ручная разблокировка** (на vps02, sudo): ```bash fail2ban-client -c /etc/fail2ban unban fail2ban-client -c /etc/fail2ban status md2vk # проверить ``` - Логи fail2ban: `journalctl -u fail2ban -e` или `/var/log/fail2ban.log`. ## Шифрование VK-токенов - VK OAuth-токены в БД только в шифрованном виде: **Fernet (AES-128-CBC + HMAC-SHA256)**. - Ключ — Docker secret `/run/secrets/token_encryption_key` (файл на bigbox: `secrets/token_encryption_key`, не в git). - Расшифровка только в памяти при публикации. ## Rate limiting - В конфиге `RATE_LIMIT_PER_MINUTE=10` (запросов/мин на VK-аккаунт). - In-memory bucket — НЕ реализован (открытая задача). ## Аудит-лог - Пишется JSONL в `/opt/md2vk/logs/access.{YYYY-MM-DD}.log` (вне контейнера — `logs/` на bigbox). - Событие на каждый запрос `/api/v1/*`: `ts, ip, method, path, api_key_prefix, user_id, status, success, latency_ms, error`. - Используется для мониторинга (доступность, успешность, активная сессия) и расследований. - Caddy на vps02 пишет свой access-лог (используется fail2ban). ## Матрица рисков | Сценарий | Последствия | Защита | |---|---|---| | Утечка .db | Токены зашифрованы | Fernet + Docker secret | | Утечка API-ключа | Можно постить, но не украсть токен | API-ключ ≠ VK-токен | | Брутфорс basic auth | Полный доступ к веб-интерфейсу API | fail2ban ban после 5 попыток, ручной unban | | Перехват HTTP | — | HTTPS (Caddy/Let's Encrypt) | | Компрометация контейнера | Полный доступ к токенам | read-only rootfs, audit log | | Отзыв токена VK | Пост не выйдет | VK вернёт ошибку → error_message | ## Правило пользователя > Никогда не удалять файлы пользователя без явного подтверждения. Пароли и токены не сохранять в коде — > только через переменные окружения, Docker secrets или файлы, исключённые из git (.gitignore).