Files
OpenVPN-Monitoring-Simple/DOCS/Operations/SUMMARY-ovpn-monitor.md
T
iclaoudezinandClaude Sonnet 5.5 3836049230 Docs: add operations records (SUMMARY, Hysteria chain manifest, reboot test, plans)
- DOCS/Operations: current-state SUMMARY of the ENTRY deployment (access,
  install, egress via Hysteria2, security findings and fixes, risk
  assessment, commits/backups, open items), the Hysteria chain manifest
  with an OpenVPN Monitor section, reboot test results, and the plan and
  rollout records for settings validation and privilege separation.
- Link them from README and DOCS/General/Index.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 12:59:55 +00:00

170 lines
24 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.
# Summary: OpenVPN Monitor & Profiler на ENTRY (Timeweb) + выход через hysteria2 → hel
Актуализировано: 2026-09-30 (по всем доработкам). Узел: 213.226.125.13 (Alpine 3.23.6, OpenRC), доступ `ssh -i private.key root@213.226.125.13` (только по ключу).
Пароль администратора панели в документе **не хранится** (после развёртывания заменён владельцем; сгенерированный начальный пароль больше не действует). Документ не содержит секретов, но остаётся внутренним (`chmod 600`).
Связанные документы:
- Рядом в `DOCS/Operations/`: `Hysteria_Chain_Manifest.md` (манифест цепочки Hysteria + раздел про OpenVPN Monitor), `PLAN-validation-and-privilege-drop.md`, `IMPLEMENTATION-01-settings-validation.md`, `IMPLEMENTATION-02-privilege-separation.md`.
- В репозитории приложения на узле (`/opt/OpenVPN-Monitoring-Simple/DOCS/`): `General/Deployment_Docker.md`, `General/Deployment_Native.md`, `Changes/2026-09-30_*.md` (Security_Hardening, Admin_Username_Change, Settings_Validation, Privilege_Separation, Egress_via_Hysteria2), `General/privilege-separation/` (помощник и правила doas).
## 1. Текущее состояние
| Компонент | Состояние |
|---|---|
| Панель | `https://213.226.125.13:8088/` — nginx, TLS 1.2/1.3, самоподписанный сертификат |
| Учётная запись | логин `tstark` (не `admin`), 2FA включена владельцем, встроенной `admin/password` нет |
| Сервисы OpenRC (автозагрузка) | `ovpmon-api` (gunicorn, 127.0.0.1:5001), `ovpmon-gatherer`, `ovpmon-profiler` (uvicorn, 127.0.0.1:8000) — **от пользователя `ovpmon`**; `nginx`; `openvpn`; `ovpn-hytun`; `fail2ban` |
| OpenVPN | udp/1194, сеть клиентов 172.20.1.0/24, полный туннель, клиентский трафик уходит через hysteria2 на hel; сквозной тест реальным клиентом пройден |
| Root-помощник | `/usr/local/sbin/ovpmon-helper` + 6 правил `doas` (`/etc/doas.d/ovpmon.conf`) |
| SSH | только ключи (`PasswordAuthentication no`, `PermitRootLogin prohibit-password`, `MaxAuthTries 3`) |
| `crl_verify` | поддерживается (CRL публикуется помощником), в настройках сейчас **выключен** |
| Hysteria (entry/client, UDP 443, 1080, 8080, 9999) | не затрагивалась доработками |
## 2. Доступ к админке
| Параметр | Значение |
|---|---|
| URL | `https://213.226.125.13:8088/` (HTTP на этом порту → 301 на HTTPS) |
| Логин | `tstark` |
| Пароль / 2FA | задаются и меняются владельцем в UI: **Account** (Change Password, Change Username, Two-Factor Authentication) |
| Сертификат | самоподписанный EC P-256, SAN `IP:213.226.125.13, DNS:ovpmon`, до 2029-01-02, файлы `/etc/ovpmon/tls/ovpmon.{crt,key}` |
| SHA-256 отпечаток | `E1:7F:A1:C1:F9:E8:22:C0:32:59:82:EF:D2:D7:FC:75:F6:3D:7E:8E:48:FA:55:88:9F:69:4D:15:3E:2B:7B:58` |
- Проверка клиентом: `curl --cacert ovpmon.crt https://213.226.125.13:8088/` (скопировать `ovpmon.crt` с узла).
- Первая фаза уже выполнена (PKI инициализирован); далее в UI: генерация server.conf, запуск OpenVPN, профили клиентов.
- JWT-секрет: `/etc/ovpmon/env` (`root:root 600`).
- Аварийное восстановление (если таблица `users` пуста): временно задать `OVPMON_INITIAL_ADMIN_USER` / `OVPMON_INITIAL_ADMIN_PASSWORD` в `/etc/ovpmon/env`, `rc-service ovpmon-api restart`, затем удалить переменные и перезапустить сервис. Без переменных пользователь не создаётся (в логе ERROR).
## 3. Установка окружения (без контейнеров)
Приложение: `/opt/OpenVPN-Monitoring-Simple` (не `/opt/ovpn-bash-profiler` — там только bash-профайлер без веб-части). Docker-compose репозитория не используется; сервисы вынесены в OpenRC.
1. Пакеты: `apk add python3 py3-pip nginx nodejs npm openvpn easy-rsa iptables bash` (+ `fail2ban`, `doas` уже был).
2. Backend: venv в `APP_CORE` (+ `gunicorn`) и `APP_PROFILER`, зависимости из `requirements.txt`.
3. UI: `npm install && npm run build` в `APP_UI`, `dist/` → `/var/www/ovpmon` (предыдущая сборка: `/var/www/ovpmon.bak`).
4. Конфиг: `/etc/ovpmon/env` (600): общий JWT-секрет, пути БД `/var/lib/ovpmon/*.db`, путь `openvpn-status.log`, `OVPMON_CORS_ORIGINS=https://213.226.125.13:8088`. Каталоги: `/var/lib/ovpmon` (+ `staging/`), `/var/log/ovpmon`, `/var/log/openvpn`.
5. Сервисы (`supervisor=supervise-daemon`, `respawn_delay=3`, `command_user="ovpmon:ovpmon"`, окружение подгружается в `start_pre` из `/etc/ovpmon/env`).
6. nginx: `/etc/nginx/http.d/ovpmon.conf` — 8088 TLS; `/` статика UI, `/api/` → :5001, `/profiles-api/` → :8000/api/.
7. Для Alpine: каталог `APP_PROFILER/easy-rsa` копируется из `/usr/share/easy-rsa` вручную (в Docker это делает entrypoint); симлинк `/etc/openvpn/openvpn.conf → server.conf`, чтобы кнопка Start в UI (`rc-service openvpn`) работала.
8. Параметры OpenVPN (`/etc/openvpn/server.conf`, генерируется приложением): udp/1194, `server 172.20.1.0/24`, `tun-mtu 1400`, `mssfix 1360`, `redirect-gateway def1`, DNS 1.1.1.1 и 8.8.8.8, `user nobody`, `group nogroup`.
## 4. Трафик клиентов OpenVPN → hysteria2 → hel
```
OpenVPN client ─udp/1194→ tun0 (172.20.1.1/24)
→ ip rule 102: from 172.20.1.0/24 → table 100
→ table 100: default dev hytun (metric 10); blackhole default (metric 1000)
→ iptables nat POSTROUTING -s 172.20.1.0/24 -o hytun -j MASQUERADE
→ hytun (TUN клиента hysteria, 100.100.100.101/30, mtu 1400)
→ hysteria client ─hy2/QUIC UDP 443→ EXIT 85.234.86.140 → Интернет (IPv4)
```
OpenRC-сервис `ovpn-hytun` (`need hysteria-route`, `before openvpn`) ставит `ip rule 102`, MASQUERADE на `hytun`, TCP MSS clamp и анти-утечку `FORWARD -s 172.20.1.0/24 ! -o hytun -j DROP`, а также `net.ipv4.ip_forward=1` (`/etc/sysctl.d/98-forward.conf`). Правила идемпотентны, `stop` их снимает.
- Приоритеты: 100 — ответы `sport 443` в main, 101 — `uid hyedge`, 102 — подсеть OpenVPN; нужен `rp_filter=2`.
- Служебный трафик (SSH, порт 1194 OpenVPN) идёт мимо туннеля. При падении hysteria таблица 100 уходит в blackhole: клиенты теряют интернет, утечки нет. На EXIT изменений не потребовалось.
- Проверка: `ip rule | grep 102`, `iptables -t nat -S POSTROUTING`; клиент на `https://ifconfig.me` видит `85.234.86.140`; эталон на узле — `su -s /bin/sh hyedge -c "curl -4 https://ifconfig.me"`.
- Статус: TCP/UDP через `hytun` подтверждены на узле, сквозной тест с реальным VPN-клиентом пройден (1194/udp достижим снаружи).
## 5. HTTPS
nginx на 8088: TLS 1.2/1.3 (TLS 1.1 отклоняется), HSTS, `X-Content-Type-Options`, `X-Frame-Options: DENY`, `Referrer-Policy`, `Cache-Control: no-store`, `server_tokens off`, редирект HTTP→HTTPS (`error_page 497`), `limit_req` 5/мин на `POST /api/auth/login`. Бэкенды слушают только 127.0.0.1. Прежний HTTP-конфиг: `/root/ovpmon.conf.bak`. Не объявлять второй `ssl_session_cache shared:SSL` другого размера — конфликт с `nginx.conf`. Порт управления OpenVPN (management) не включён; при включении — только loopback.
## 6. Безопасность: находки и их устранение
| # | Критичность | Находка | Исправление | Проверка |
|---|---|---|---|---|
| 1 | Критично | SSH: пароль root по сети (drop-in `50-cloud-init.conf` возвращал `PasswordAuthentication yes`; уже шёл перебор — 113 неудачных попыток) | `/etc/ssh/sshd_config.d/00-hardening.conf`: пароли и kbd-interactive выключены, `PermitRootLogin prohibit-password`, `MaxAuthTries 3`, `LoginGraceTime 30`; бэкап `/root/sshd_config.bak` | вход по ключу ок, по паролю `Permission denied (publickey)` |
| 2 | Высоко | Path traversal в Profiler (`username` без валидации, сервис от root) | маска `^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$` в схеме, `validate_username()` в `pki.py`, проверки `realpath` в роутере и генераторе | `../../tmp/pwn`, `a/b`, `..`, `-x` → 422, файл не создан |
| 3 | Высоко | Обход 2FA: временный токен принимался всеми ручками | `token_required` (Flask) и `verify_token` (Profiler) отклоняют `is_2fa_pending`; принимает его только `verify-2fa` | temp-токен → 401 везде, полный токен → 200 |
| 4 | Средне | `enable_2fa` писал OTP и TOTP-секрет в лог | убрано из сообщения | в логе секретов нет |
| 5 | Средне | CORS `*` + credentials | origin из `OVPMON_CORS_ORIGINS` (по умолчанию только same-origin), для Profiler сужены методы/заголовки | чужой `Origin` не получает `Access-Control-Allow-Origin` |
| 6 | Средне | Нет защиты от перебора | fail2ban: jails `sshd`, `sshd-ddos`, `ovpmon-login` (401/429/503 на логине), IP администратора в `ignoreip` | jails активны, баны SSH-сканеров зафиксированы |
| 7 | Средне | Панель по HTTP | TLS в nginx (раздел 5) | HTTPS 200, TLS 1.1 отклонён, rate-limit 503 |
| 8 | Высоко | Инъекция в `server.conf` через настройки (админ-токен → root) | валидация настроек в 3 слоя (раздел 7) | 17 вредных значений → 422, обход схемы блокируется рендером |
| 9 | Высоко | Сервисы API работали от root | пользователь `ovpmon` + root-помощник (раздел 8) | процессы под `ovpmon`, инъекции директив отклоняются |
| 10 | Низко | Стандартное имя `admin`, встроенный `admin/password` | смена имени в приложении, дефолтная учётка удалена (раздел 9) | логин `tstark` 200, `admin` 401 |
| 11 | Функц. | `crl_verify` не работал бы без root (`nobody` не читает `pki/`) | публикация CRL в `/etc/openvpn/crl.pem` (раздел 8) | клиент проходит, отозванный отклоняется |
Проверено без замечаний: алгоритм JWT зафиксирован HS256, секрет — 32 случайных байта; SQL f-строки используют только внутренние имена таблиц/колонок; nginx работает под непривилегированным пользователем; порты 1194/udp и 443/udp открыты по назначению со своей аутентификацией.
## 7. Валидация настроек Profiler (п.1 плана)
Слои: **A** — схемы `SystemSettingsUpdate` / `PKISettingUpdate` (порты, подсеть+маска, маршруты, DNS, `public_ip`, management только loopback, MTU/MSS, пути скриптов, поля DN и FQDN для PKI, размер ключа, сроки); **B** — скрипты подключения только из `/etc/openvpn/scripts/` (root-владелец, без записи group/other, без симлинков наружу); **C** — генератор отказывается писать `server.conf`/`.ovpn`, если значение содержит `\n`, `\r`, NUL, управляющие символы, `"` или `\`.
Результат: 17 вредоносных значений → 422; 4 валидных изменения → 200; инъекции мимо схемы блокируются; рендер текущих настроек идентичен рабочему конфигу. Каталога `/etc/openvpn/scripts/` по умолчанию нет — создать `root:root 755`, файлы `755`, перед включением скриптов. Артефакт: `IMPLEMENTATION-01-settings-validation.md`.
## 8. API не от root: пользователь `ovpmon`, root-помощник, CRL (п.2 плана)
```
ovpmon-api / gatherer / profiler (user ovpmon)
└─ doas -n /usr/local/sbin/ovpmon-helper <фиксированные аргументы> (6 точных правил)
├─ install-config — проверка staged server.conf по allowlist директив, атомарная установка в /etc/openvpn
├─ publish-crl — копия pki/crl.pem → /etc/openvpn/crl.pem (root:root 644)
└─ service start|stop|restart|status — rc-service openvpn
```
- `ovpmon` владеет `/var/lib/ovpmon`, `/var/log/ovpmon`, `APP_PROFILER/{easy-rsa,client-config,profiler.log}`, логами и `__pycache__`; код и venv — root, только чтение. От root остались лишь supervise-daemon.
- Allowlist `install-config`: только директивы шаблона с проверенными аргументами (`dev tun`, `proto`, `port`, пути PKI, `server`, `log`, `status`, `push` трёх видов, `user nobody`, `group nogroup`, шифры, `client-connect/disconnect` только из `/etc/openvpn/scripts/` и т.д.); `up`, `down`, `plugin`, `route-up`, `tls-verify`, `setenv`, `config` и всё неизвестное отклоняются; файл читается один раз, только обычный файл `ovpmon`, ASCII, ≤ 64 КиБ.
- CRL: помощник копирует CRL после `gen-crl` (init/revoke), при `server/configure` и при каждом `start|restart`; для `crl-verify` разрешён только путь `/etc/openvpn/crl.pem`; ошибка публикации при отзыве возвращается ошибкой API.
- Проверено: 17 инъекций директив, отсутствие `user nobody`, cert вне PKI, CR, симлинк — всё отклонено, боевой конфиг не менялся; `doas` отказывает на любую иную команду; `ovpmon` не читает `/etc/shadow`, `/root`, `/etc/hysteria/*.yaml`, `/etc/ovpmon/env`, не пишет в `/etc/openvpn`, `/etc/init.d`, `authorized_keys`, код, не запускает `iptables`; API (мониторинг, конфиг, `server/configure`, создание/скачивание/отзыв профиля, рестарт OpenVPN) работает; CRL проверен на отдельном экземпляре OpenVPN от `nobody` (валидный клиент подключается, отозванный получает `certificate revoked`).
- Артефакт и откат: `IMPLEMENTATION-02-privilege-separation.md`.
## 9. Смена имени администратора
`POST /api/auth/change-username` (JWT + текущий пароль + OTP при 2FA; маска `^[A-Za-z][A-Za-z0-9_.-]{2,31}$`, запрещены `admin, administrator, root, user, test, guest`, уникальность без учёта регистра → 409, неверный пароль/OTP учитываются rate-limit); кнопка «Change Username» в Account; в 2FA-URI подставляется реальное имя; `ensure_default_admin` больше не создаёт `admin/password` (только из `OVPMON_INITIAL_ADMIN_*`); баг UI «в шапке всегда Admin» исправлен (синхронизация с `/user/me`, событие `ovpmon-user-changed`). JWT хранит `user_id`, поэтому смена имени сессии не рвёт. Бэкап БД: `/root/openvpn_monitor.db.bak-2026-09-30`.
Ручной тест владельца в UI (смена пароля, включение 2FA) пройден.
## 10. Оценка вероятности получения root (актуальная)
**Без учётных данных, извне — низкая.** SSH только по ключам + fail2ban; панель за TLS, rate-limit и fail2ban; все API за JWT; OpenVPN — mTLS + `tls-auth`; Hysteria — пароль на пользователя; служебные порты на loopback. Остаточный риск: неизвестные уязвимости OpenVPN/nginx/sshd/hysteria/ядра и отставание пакетов (актуальность индекса `apk` и ядра не проверялась).
**С учётными данными панели (пароль + 2FA / токен).** Раньше это давало root (через инъекцию в `server.conf` от root-процесса). Теперь: валидация настроек (слои A–C), API работает от `ovpmon`, а помощник не пропустит опасные директивы. Остаточные пути:
1. уязвимость в `ovpmon-helper`, `doas` или ядре;
2. включение скриптов подключения (исполняются от `nobody`, берутся только из root-каталога `/etc/openvpn/scripts/`);
3. `ovpmon` владеет PKI, включая ключ CA: компрометация API позволяет выпускать клиентские сертификаты (доступ к VPN), но не даёт root. Проверки путей PKI в помощнике выполняются на момент установки (теоретическое окно TOCTOU с подменой файла на симлинк до старта OpenVPN — влияет на работоспособность, не на выполнение кода).
Итог: цепочка «интернет → root» сводится к неизвестной уязвимости (0-day) в привилегированных компонентах; компрометация админа панели больше не эквивалентна root.
## 11. Репозиторий, резервные копии, откат
Репозиторий приложения на узле (`/opt/OpenVPN-Monitoring-Simple`, ветка `main`), коммиты **не запушены** (автор `iclaoudezin`, пуш за пользователем):
| Коммит | Содержание |
|---|---|
| `11c1b63` | смена имени, 2FA-исправления, валидация `username`, CORS, UI |
| `9b2882d` | README и документация (Docker/native, Changes) |
| `5de0501` | docker-compose: обязательный `JWT_SECRET`, seed админа, порты |
| `05f44b9` | валидация настроек Profiler |
| `6f9e800` | API не от root + root-помощник |
| `9ffdbfa` | публикация CRL для `crl_verify` |
| `e1146ed` | README приложения: раздел Security defaults, ссылки на `privilege-separation/` |
Не вошли в коммиты: `APP_CORE/config.ini` и `APP_UI/package-lock.json` (побочные правки приложения/npm), `venv/`, `easy-rsa/` и `client-config/` (приватные ключи — в `.gitignore` не добавлены, добавить отдельным коммитом).
Бэкапы на узле: `/root/backup-p2-2026-09-30-1229.tar` (`/etc/openvpn`, `/var/lib/ovpmon`, easy-rsa, init-скрипты, doas, код), `/root/openvpn_monitor.db.bak-2026-09-30`, `/root/ovpmon.conf.bak`, `/root/sshd_config.bak`, `/root/app-bak/` (файлы приложения по этапам, `README.md.orig`, `p1/ p2/ p3/`), `/var/www/ovpmon.bak`. Откат — в `IMPLEMENTATION-02-privilege-separation.md` и `DOCS/Changes/*`.
## 12. Открытые вопросы и рекомендации
- Пуш коммитов (учётные данные внешнего репозитория у пользователя).
- Тест перезагрузки ENTRY после доработок **пройден** (2026-09-30): снимок состояния до/после идентичен, все сервисы, правила выхода, hardening и панель поднялись сами; VPN-клиент переподключился сам через ~3,5 мин (ping-restart UDP-клиента), трафик идёт через `hytun`, утечек нет. Подробности — `REBOOT-TEST.md`, раздел «ENTRY reboot after the OpenVPN Monitor deployment». Нюанс: права `root:ovpmon 640` на `openvpn-status.log` сохраняются, пока файл на диске; при его удалении OpenVPN создаст файл с 600 до ближайшего `start|restart` через помощник.
- Самоподписанный сертификат: сверять отпечаток; при появлении домена — Let's Encrypt.
- Ограничить панель по IP или пускать через VPN; сократить срок жизни JWT (сейчас 8 ч) и отзывать токены при смене пароля/имени.
- Включить `crl_verify` в настройках, если нужна проверка отзыва (публикация CRL уже работает).
- Создать `/etc/openvpn/scripts/` (`root:root 755`) перед использованием скриптов подключения.
- В шаблоне клиентского `.ovpn` есть Windows-опция `windows-driver` — не подходит Linux-клиентам; при необходимости вынести в условие (шаблон не менялся).
- Регулярно `apk upgrade`, следить за advisory OpenVPN/nginx/OpenSSH/hysteria.
- В списке профилей остались записи `revoked` от тестов (`ok-user1`, `p2test-*`, `crl-e2e-*`).
## Журнал изменений (2026-09-30)
1. Развёртывание приложения без контейнеров (OpenRC, nginx :8088), инициализация PKI (фикс каталога `easy-rsa`).
2. Выход клиентов OpenVPN через `hytun` → hel (`ovpn-hytun`), сквозной тест пройден.
3. HTTPS на 8088, rate-limit, заголовки.
4. Аудит безопасности; SSH key-only, fail2ban, валидация `username`, CORS.
5. Смена имени администратора (данные + API + UI + без дефолтной учётки); исправление обхода 2FA и лога секретов; баг имени в шапке.
6. Документация и коммиты (README, Docker/native, Changes), актуализация `docker-compose.yml`.
7. Валидация настроек Profiler (п.1).
8. API не от root, root-помощник (п.2), публикация CRL.
9. Актуализация SUMMARY и README.
10. Тест перезагрузки ENTRY: пройден (`REBOOT-TEST.md`).