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

24 KiB
Raw Blame History

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