- 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>
7.6 KiB
Артефакт внедрения: п.2 — API без root + root-помощник
Дата: 2026-09-30. Узел: 213.226.125.13 (Alpine, OpenRC). Приложение: /opt/OpenVPN-Monitoring-Simple. План: PLAN-validation-and-privilege-drop.md.
Коммит в репозитории приложения на узле: 6f9e800 («Run API services unprivileged; add root helper…»; после --amend, ранее упоминался как 10020ca), дополнение по CRL — 9ffdbfa, пуш не выполнялся. Полное описание: DOCS/Changes/2026-09-30_Privilege_Separation.md.
1. Что внедрено
| Компонент | Расположение | Суть |
|---|---|---|
| Пользователь | ovpmon (uid 109, nologin) |
владелец данных: /var/lib/ovpmon, /var/log/ovpmon, easy-rsa, client-config, логи, __pycache__ |
| OpenRC | /etc/init.d/ovpmon-{api,gatherer,profiler} |
command_user="ovpmon:ovpmon"; /etc/ovpmon/env остаётся root 600 |
| Помощник | /usr/local/sbin/ovpmon-helper (root, 755) |
install-config (allowlist директив, атомарная установка) и service start|stop|restart|status |
| doas | /etc/doas.d/ovpmon.conf |
ровно 5 команд помощника, без других аргументов |
| Код Profiler | services/process.py, routers/server.py |
без root: staging → помощник; ветки root/контейнера не менялись |
2. Порядок внедрения
- Бэкап:
/root/backup-p2-2026-09-30-1229.tar,/root/app-bak/p2/(init-скрипты,process.py,server.py). - Создание
ovpmon, установка помощника и doas-правил; проверка помощника от имениovpmon(сервисы ещё root). - Патч
process.py/server.py(+except HTTPException: raise, чтобы отказ 400 не превращался в 500). chownданных,command_userв init-скриптах; перезапуск gatherer → api → profiler с проверкой каждого.- Функциональные и негативные тесты; рестарт OpenVPN через API.
- Документация и коммит (исправлен эпизод с порчей
Deployment_Native.md: восстановлен из предыдущего коммита и переделан, коммит переписан--amend, пуша не было).
3. Доказательства
| Проверка | Результат |
|---|---|
| Процессы | gunicorn, uvicorn, gatherer — ovpmon; root остались только supervise-daemon |
Помощник принимает рабочий server.conf |
да, файл в /etc/openvpn не изменился (sha256 совпал) |
17 инъекций директив, отсутствие user nobody, cert вне PKI, CR, симлинк staged-файла |
все отклонены, боевой конфиг не тронут |
| doas: произвольная команда, другие аргументы помощника | Operation not permitted |
ovpmon не может: читать /etc/shadow, /root, /etc/hysteria/*.yaml, /etc/ovpmon/env; писать в /etc/openvpn, /etc/init.d, authorized_keys, код приложения; запускать iptables |
все отказы |
API: мониторинг, config, process stats, server/configure (конфиг идентичен), создание/скачивание/отзыв профиля |
всё 200 |
| Рестарт OpenVPN через API (helper) | 200; OpenVPN и tun0 подняты, статус-лог читается gatherer, ip rule 102 и MASQUERADE на месте, ошибок в логе gatherer нет |
Побочный эффект тестов: один рестарт OpenVPN (~секунды разрыва VPN-сессий; клиент переподключается сам).
4. Откат
cp /root/app-bak/p2/ovpmon-{api,gatherer,profiler} /etc/init.d/
chown -R root:root /var/lib/ovpmon /var/log/ovpmon /opt/OpenVPN-Monitoring-Simple/APP_PROFILER/{easy-rsa,client-config}
for s in ovpmon-gatherer ovpmon-api ovpmon-profiler; do rc-service $s restart; done
(помощник и doas-правила можно оставить; код Profiler при root работает по прежней ветке).
5. Оговорки
crl_verify: ограничение снято (2026-09-30, коммит9ffdbfa) — см. раздел 6 ниже.- Проверка путей PKI в помощнике выполняется на момент установки; каталог PKI принадлежит
ovpmon— держать его только за этим пользователем. - Скрипты подключения по-прежнему требуют
/etc/openvpn/scripts/(root, 755) — каталога нет по умолчанию.
6. Дополнение: публикация CRL для crl_verify (2026-09-30)
Проблема: OpenVPN после старта работает от nobody, а pki/ создаётся с правами 700 (владелец ovpmon), поэтому при включённом crl_verify все клиенты получили бы отказ.
Решение: новая команда помощника publish-crl (6-е правило doas) копирует pki/crl.pem в /etc/openvpn/crl.pem (root:root 644) с проверками (файл внутри PKI, не симлинк, владелец ovpmon, ≤ 1 МиБ, PEM-маркеры и разбор openssl crl). Вызывается после gen-crl (init/revoke), при server/configure и при каждом service start|restart; ошибка публикации при отзыве возвращается как ошибка API. Генератор без root пишет crl-verify /etc/openvpn/crl.pem, помощник разрешает для crl-verify только этот путь.
Проверка (отдельный временный экземпляр OpenVPN на другом порту и подсети, от nobody, тот же PKI и CRL; боевой OpenVPN не перезапускался):
| Проверка | Результат |
|---|---|
publish-crl от ovpmon; права копии; чтение nobody |
ok; root:root 644; читается; исходный pki/crl.pem для nobody по-прежнему недоступен |
| мусорный файл / поддельные PEM-маркеры / симлинк вместо CRL | отклонены |
crl_verify=true через API + server/configure |
200, в конфиге crl-verify /etc/openvpn/crl.pem, помощник принял; настройка возвращена, боевой server.conf идентичен исходному |
| валидный клиент при активной проверке CRL | подключается |
| отзыв через API и повторное подключение | certificate revoked, клиент отклонён, ошибок чтения CRL нет |
Побочное: в списке профилей остались записи со статусом revoked от тестов (ok-user1, p2test-*, crl-e2e-*). Тестовый экземпляр и файлы удалены, боевой OpenVPN (pid 8884) и правила выхода через hel не затронуты. Значение crl_verify в настройках оставлено выключенным, как было.