Files
OpenVPN-Monitoring-Simple/DOCS/Operations/IMPLEMENTATION-02-privilege-separation.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

69 lines
7.6 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.
# Артефакт внедрения: п.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. Порядок внедрения
1. Бэкап: `/root/backup-p2-2026-09-30-1229.tar`, `/root/app-bak/p2/` (init-скрипты, `process.py`, `server.py`).
2. Создание `ovpmon`, установка помощника и doas-правил; проверка помощника от имени `ovpmon` (сервисы ещё root).
3. Патч `process.py` / `server.py` (+ `except HTTPException: raise`, чтобы отказ 400 не превращался в 500).
4. `chown` данных, `command_user` в init-скриптах; перезапуск gatherer → api → profiler с проверкой каждого.
5. Функциональные и негативные тесты; рестарт OpenVPN через API.
6. Документация и коммит (исправлен эпизод с порчей `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. Откат
```sh
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` в настройках оставлено выключенным, как было.