Files
OpenVPN-Monitoring-Simple/DOCS/Operations/PLAN-validation-and-privilege-drop.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

83 lines
7.3 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.
# План: валидация настроек (п.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.