Compare commits

..

2 Commits

3 changed files with 73 additions and 11 deletions
+62
View File
@@ -0,0 +1,62 @@
# AGENTS.md — инструкции для агентов (Hermes и др.), работающих с /opt/md2vk
## О проекте
md2vk — сервис публикации Markdown на стену VK через официальный VK API (wall.post + format_data).
FastAPI + SQLAlchemy async + SQLite + Fernet. Документация — в `docs/`, начни с `docs/index.md` и `docs/access.md`.
## Правила работы
1. **Сначала читай доки проекта**: `docs/index.md`, `docs/access.md`, `docs/status.md`,
`docs/deploy.md` и прошлые сессии (`session_search`), потом действуй.
2. **Не удаляй файлы и данные пользователя** без явного подтверждения (БД `data/md2vk.db`,
`secrets/`, пользователи). Перезапись/миграция БД — только по явной команде.
3. **Секреты в git не коммитятся**: `secrets/token_encryption_key`,
`secrets/estorozhenko_api_key.txt`, `.env` — в .gitignore. Токены gitverse/gitea — в
`/opt/hermes/.hermes/secrets/git-tokens.env` (source его, не ищи по каталогам).
4. **Прод-порт 8420** (не 8000 — занят docker-search-api на bigbox). Контейнер слушает 127.0.0.1:8420.
5. **Изменения в прод-код** — через git: сначала commit/push в gitverse (истина), gitea подтянет pull mirror.
6. **Caddy/fail2ban живут на vps02** (ssh-алиас `vps02`, sudo по ключу):
- Caddyfile: `/opt/caddy/Caddyfile`, reload: `docker exec caddy caddy reload --config /etc/caddy/Caddyfile`
- fail2ban: jail `md2vk`, ban после 5 попыток, unban: `sudo fail2ban-client -c /etc/fail2ban unban <ip>`
- Пароль basic auth — только bcrypt-хэш в Caddyfile; генерировать `docker exec caddy caddy hash-password`.
7. **Рестарт docker на bigbox** — локально (`docker compose up -d`), не на vps02.
8. **Наблюдаемость по умолчанию** (делать сразу, не спрашивать): лог авторизации +
метрики доступности/успешности/активной сессии. Лог: `/opt/md2vk/logs/access.{date}.log` (JSONL).
9. **Хэштеги/теги — строчными буквами**; документация — с точными маппингами портов и доступов.
10. **Не запускать ручные бэкапы** — делает крон (homelab-backup, 2:45).
## Команды
```bash
cd /opt/md2vk
make docker-up # собрать+поднять (или: docker compose up -d --build)
curl -s http://127.0.0.1:8420/api/v1/health # {"status":"ok"}
docker logs md2vk # логи контейнера
ls -t logs/ # аудит-лог (JSONL)
```
## Публикация через API (пример)
```bash
API_KEY=$(cat secrets/estorozhenko_api_key.txt)
curl -s http://127.0.0.1:8420/api/v1/publish \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"$API_KEY\",\"vk_account_id\":1,\"message_md\":\"**Привет** из md2vk\"}"
```
Сначала добавить VK-аккаунт: `POST /api/v1/accounts` c `access_token` (VK OAuth-токен, scope wall).
## Git-схема
- Истина: gitverse.ru (kpa39l/md2vk) — push через `https://kpa39l:<PAT>@gitverse.ru/kpa39l/md2vk.git`
(SSH-ключ ~/.ssh/gitverse также работает); PAT в `/opt/hermes/.hermes/secrets/git-tokens.env` ($GITVERSE_PAT)
- Зеркало: gitea.nixg.ru (estorozhenko/md2vk) — pull mirror, обновляется автоматически (8h)
- Ветки: main (default)
## Ключевые лица/доступы
- Владелец: estorozhenko (пользователь Hermes), kpa39l (gitverse-аккаунт, email kpa39l@yandex.ru)
- vps02: 87.242.100.206 (estorozhenko, sudo по ключу), Caddy + fail2ban
- bigbox: 10.8.0.2 (локальный хост), проект /opt/md2vk
- DNS: nixg.ru (Jino), A-запись md2vk → 87.242.100.206
+3 -1
View File
@@ -4,7 +4,9 @@ services:
# Внутренний порт в контейнере — 8420; наружу слушает 127.0.0.1:8420 # Внутренний порт в контейнере — 8420; наружу слушает 127.0.0.1:8420
# (8000 на bigbox занят docker-search-api). Caddy на vps02 → 10.8.0.2:8420. # (8000 на bigbox занят docker-search-api). Caddy на vps02 → 10.8.0.2:8420.
ports: ports:
- "127.0.0.1:8420:8420" # Слушаем на WG-интерфейсе bigbox (10.8.0.2), т.к. Caddy на vps02 идёт по WireGuard.
# 127.0.0.1 не подходит — vps02 не видит loopback bigbox. Наружу (публичный IP) не слушаем.
- "10.8.0.2:8420:8420"
volumes: volumes:
- ./data:/data - ./data:/data
- ./logs:/logs - ./logs:/logs
+8 -10
View File
@@ -6,8 +6,8 @@
- Весь https://md2vk.nixg.ru закрыт HTTP Basic Auth. - Весь https://md2vk.nixg.ru закрыт HTTP Basic Auth.
- Пользователь: **estorozhenko**; пароль — у пользователя, в конфиге только bcrypt-хэш. - Пользователь: **estorozhenko**; пароль — у пользователя, в конфиге только bcrypt-хэш.
- Хэш в Caddyfile: `admin $2a$14$T5gAji7gmg1t3Ifxw0CJy.jat9vpFfOiJIE8j5bnHynEVH1q.QTPS` - Хэш в Caddyfile: `estorozhenko $2a$14$uzlQxJdcRZJ5gDpGG5B26OXnudIme1IYe7xh0WtDwFtbB6XiiDYTS`
(генерируется `docker exec caddy caddy hash-password --plaintext '<пароль>'`). (сгенерирован 2026-09-18 `docker exec caddy caddy hash-password --plaintext '<пароль>'`).
- Без basic auth сервис наружу не отдаётся. - Без basic auth сервис наружу не отдаётся.
### 2. Внутренний — API-ключ (FastAPI) ### 2. Внутренний — API-ключ (FastAPI)
@@ -20,15 +20,13 @@
## Блокировка после 5 неудачных попыток (fail2ban на vps02) ## Блокировка после 5 неудачных попыток (fail2ban на vps02)
- На vps02 установлен fail2ban, jail `md2vk`: - На vps02 установлен fail2ban (v1.0.2), jail `md2vk`:
- следит за логами Caddy (JSON: `/var/log/caddy/access.log`), - следит за site-логом Caddy: `/opt/caddy/caddy_data/logs/md2vk.access.log` (JSON, все запросы включая 401),
- фильтр: basic auth failure (`401` с `"err"`, absence of `"user_id"`): - фильтр `filter.d/caddy-md2vk.conf`: `^.*"request":\{"remote_ip":"<HOST>"[^}]*"host":"md2vk\.nixg\.ru"[^}]*\}.*"status":401`,
``` - правило: ban IP после **5 неудачных попыток** (`maxretry=5`, `findtime=600`; на 6-й срабатывает),
^.*"status":401.*
```
- правило: ban IP после **5 неудачных попыток подряд** (`maxretry=5`, `findtime=600`),
- наказание: **ban до ручного снятия** (`bantime = -1`), - наказание: **ban до ручного снятия** (`bantime = -1`),
- действие: `iptables-allports` (ban на уровне ядра, DROP). - действие: `iptables-allports` (цепочка `f2b-md2vk`, REJECT; на Debian 12 транслируется в nftables),
- ignoreip: `127.0.0.1/8 ::1 10.8.0.0/24` (не баним bigbox/vps02).
- **Ручная разблокировка** (на vps02, sudo): - **Ручная разблокировка** (на vps02, sudo):
```bash ```bash
fail2ban-client -c /etc/fail2ban unban <ip> fail2ban-client -c /etc/fail2ban unban <ip>