- 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>
24 KiB
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.
- Пакеты:
apk add python3 py3-pip nginx nodejs npm openvpn easy-rsa iptables bash(+fail2ban,doasуже был). - Backend: venv в
APP_CORE(+gunicorn) иAPP_PROFILER, зависимости изrequirements.txt. - UI:
npm install && npm run buildвAPP_UI,dist/→/var/www/ovpmon(предыдущая сборка:/var/www/ovpmon.bak). - Конфиг:
/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. - Сервисы (
supervisor=supervise-daemon,respawn_delay=3,command_user="ovpmon:ovpmon", окружение подгружается вstart_preиз/etc/ovpmon/env). - nginx:
/etc/nginx/http.d/ovpmon.conf— 8088 TLS;/статика UI,/api/→ :5001,/profiles-api/→ :8000/api/. - Для Alpine: каталог
APP_PROFILER/easy-rsaкопируется из/usr/share/easy-rsaвручную (в Docker это делает entrypoint); симлинк/etc/openvpn/openvpn.conf → server.conf, чтобы кнопка Start в UI (rc-service openvpn) работала. - Параметры 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, а помощник не пропустит опасные директивы. Остаточные пути:
- уязвимость в
ovpmon-helper,doasили ядре; - включение скриптов подключения (исполняются от
nobody, берутся только из root-каталога/etc/openvpn/scripts/); 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)
- Развёртывание приложения без контейнеров (OpenRC, nginx :8088), инициализация PKI (фикс каталога
easy-rsa). - Выход клиентов OpenVPN через
hytun→ hel (ovpn-hytun), сквозной тест пройден. - HTTPS на 8088, rate-limit, заголовки.
- Аудит безопасности; SSH key-only, fail2ban, валидация
username, CORS. - Смена имени администратора (данные + API + UI + без дефолтной учётки); исправление обхода 2FA и лога секретов; баг имени в шапке.
- Документация и коммиты (README, Docker/native, Changes), актуализация
docker-compose.yml. - Валидация настроек Profiler (п.1).
- API не от root, root-помощник (п.2), публикация CRL.
- Актуализация SUMMARY и README.
- Тест перезагрузки ENTRY: пройден (
REBOOT-TEST.md).