Files
OpenVPN-Monitoring-Simple/DOCS/Operations/PLAN-validation-and-privilege-drop.md
T

82 lines
7.3 KiB
Markdown
Raw Normal View History

# План: валидация настроек (п.1) и запуск API не от root (п.2)
Дата: 2026-09-30. Узел: 213.226.125.13 (Alpine, OpenRC). Приложение: `/opt/OpenVPN-Monitoring-Simple`.
Основание: SUMMARY-ovpn-monitor.md, раздел 6.5 — администратор панели фактически равен root, т.к. Profiler работает от root, а настройки без проверки попадают в `server.conf`.
## Исходные факты
- `APP_CORE` (API, gatherer) root не нужен: читает `openvpn-status.log`, пишет свою SQLite-БД и лог.
- Profiler требует root только для: записи `/etc/openvpn/server.conf`, `rc-service openvpn`, чтения PKI-файлов самим OpenVPN.
- В коде уже есть fallback `staging/server.conf`.
- В шаблоне `script-security 2` стоит внутри `{% if user_defined_cdscripts %}`.
- `doas` установлен; конфигурация только `permit nopass :wheel`.
## Пункт 1. Валидация настроек (3 слоя)
**Слой A. Схемы (`APP_PROFILER/schemas.py`)** — валидаторы только на `*Update`-схемах (старые значения из БД не ломают `GET`).
| Поле | Правило |
|---|---|
| `port`, `management_port` | 1–65535 |
| `vpn_network`, `vpn_netmask` | `ipaddress`: сеть и корректная маска |
| `split_routes[]` | CIDR или «сеть маска», нормализация через `ipaddress` |
| `dns_servers[]` | IPv4/IPv6 через `ipaddress` |
| `public_ip` | IP или hostname по маске, без пробелов и кавычек |
| `management_interface_address` | только loopback |
| `tun_mtu` / `mssfix` | 576–9000 / 536–1500 |
| `connect_script`, `disconnect_script` | пусто или `^/etc/openvpn/scripts/[A-Za-z0-9_.-]{1,64}$` |
| PKI `fqdn_ca`, `fqdn_server` | маска как у `username` |
| PKI `easyrsa_dn` | `cn_only` или `org` |
| PKI `req_country` | `^[A-Z]{2}$` |
| PKI `req_province/city/org/ou` | `^[A-Za-z0-9 .,_-]{0,64}$` |
| PKI `req_email` | простая маска email |
| PKI `key_size` | 2048 / 3072 / 4096 |
| PKI сроки | целые в разумных пределах |
**Слой B. Скрипты (`routers/server.py`, до генерации):** `realpath` внутри `/etc/openvpn/scripts/`, файл существует, владелец root, нет прав записи для group/other.
**Слой C. Генератор (`services/generator.py`):** перед записью проверить, что ни одно значение не содержит `\n`, `\r`, `"`, `\`; иначе ошибка и файл не пишется.
Тесты: невалидные значения (перевод строки в DNS, `../` в скрипте, `/` в организации, порт 0) → 422; валидный набор → 200 и корректный `server.conf`.
## Пункт 2. Profiler и API не от root
Схема: пользователь `ovpmon` + маленький root-помощник, сам валидирующий конфиг (защита в глубину: инъекция в конфиг не даёт root даже при обходе слоёв A–C).
1. **Пользователь и права.** Системный `ovpmon`. Владелец: `/var/lib/ovpmon`, `/var/log/ovpmon`, `APP_PROFILER/{easy-rsa,client-config,staging,*.db,profiler.log}`, `APP_CORE/{*.db,*.log}`. `pki/`: каталоги 755, `crl.pem` 644, приватные ключи 600. Код и `venv` остаются root-owned. `/etc/ovpmon/env` остаётся root 600.
2. **OpenRC:** `command_user="ovpmon:ovpmon"` в `ovpmon-*`.
3. **Помощник `/usr/local/sbin/ovpmon-helper`** (root, 755, Python). Подкоманды без пользовательских аргументов:
- `install-config`: читает только `/var/lib/ovpmon/staging/server.conf` (один раз в память, защита от TOCTOU), проверяет allowlist директив (запрещены `up`, `down`, `plugin`, `route-up`, `tls-verify`, `auth-user-pass-verify`, `learn-address`, неизвестные; `client-connect/disconnect` и `script-security` — только со скриптом из `/etc/openvpn/scripts/` с проверкой владельца и прав), пишет `/etc/openvpn/server.conf` атомарно (root:root 644).
- `service start|stop|restart|status` только для `openvpn`.
4. **`doas`:** `/etc/doas.d/ovpmon.conf`, точные `args` для каждой подкоманды.
5. **Код Profiler:** на хосте `services/process.py` и `routers/server.py` вызывают `doas ovpmon-helper …`; ветка контейнера и `staging` не меняются.
6. **`APP_CORE`:** только смена владельца и `command_user`.
7. **Каталог скриптов:** `/etc/openvpn/scripts/` root:root 755, по умолчанию пустой.
### Порядок
1. Бэкап `/etc/openvpn`, БД, `/etc/init.d/ovpmon-*`, код, `easy-rsa/`.
2. Слои A–C + тесты, перезапуск Profiler.
3. Пользователь, `chown`, помощник, `doas`; ручная проверка помощника.
4. Переключить `process.py` и `server.py` на помощник.
5. Переключить init-скрипты на `command_user`, перезапускать по одному.
6. Обновить документацию и SUMMARY, отдельные коммиты.
### Проверка (п.2)
- `ps`: Profiler и API под `ovpmon`.
- От `ovpmon`: нет доступа к `/etc/shadow`, `/root`, `/etc/hysteria/*.yaml`, запись в `/etc/openvpn` запрещена.
- UI: Initialize PKI, генерация и запись конфига, Start/Stop/Restart OpenVPN, создание/скачивание/отзыв профиля, мониторинг.
- Инъекция директивы в `staging/server.conf` отвергается помощником, `/etc/openvpn/server.conf` не меняется.
- Прямые вызовы `doas` с другими аргументами отклоняются.
- OpenVPN-клиент подключается и выходит через hel.
- Не сломаны fail2ban, HTTPS 8088, `ip rule 102`.
## Риски
| Риск | Смягчение |
|---|---|
| Сервис не стартует из-за прав | бэкап, `chown` до переключения, откат правкой init-скриптов |
| Allowlist блокирует легальный конфиг | первый прогон в режиме проверки без записи; список директив из `server.conf.j2` |
| Ошибка в `doas` `args` | негативные тесты от `ovpmon`, запасная root-SSH-сессия |
| Потеря ключей PKI | копия `easy-rsa/` перед `chown` |
## Статус
- Пункт 1 — реализован (2026-09-30), см. SUMMARY раздел 6.6 и DOCS/Changes/2026-09-30_Settings_Validation.md.
- Пункт 2 — реализован (2026-09-30), см. IMPLEMENTATION-02-privilege-separation.md и DOCS/Changes/2026-09-30_Privilege_Separation.md.