# 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`).