diff --git a/DOCS/General/Index.md b/DOCS/General/Index.md index 651e908..c93c621 100644 --- a/DOCS/General/Index.md +++ b/DOCS/General/Index.md @@ -10,6 +10,9 @@ Welcome to the documentation for the OpenVPN Monitor suite. - [Nginx Configuration](Nginx_Configuration.md) - [Security Architecture](Security_Architecture.md): Details on Authentication, 2FA, and Security features. +## πŸ—‚ Operations +- [Deployment records](../Operations/README.md): state summary, reboot test, plans and rollout records. + ## πŸ›  Changes and results - [Security hardening (2026-09-30)](../Changes/2026-09-30_Security_Hardening.md) - [Admin username change (2026-09-30)](../Changes/2026-09-30_Admin_Username_Change.md) diff --git a/DOCS/Operations/Hysteria_Chain_Manifest.md b/DOCS/Operations/Hysteria_Chain_Manifest.md new file mode 100644 index 0000000..bb2d12a --- /dev/null +++ b/DOCS/Operations/Hysteria_Chain_Manifest.md @@ -0,0 +1,168 @@ +# Hysteria 2 entryβ†’exit chain (manifest) + +Client ─hy2/UDP443β†’ **ENTRY** (Alpine, OpenRC) ─hy2/UDP443β†’ **EXIT** (Debian, systemd) β†’ Internet (IPv4) + +Secrets live only in: `client-uris.txt`, `singbox-*.json`, `/etc/hysteria/*` on hosts (chmod 600). Never commit/print them. + +## Inventory (this deployment) +| Role | Host | OS | Access | +|---|---|---|---| +| ENTRY (Timeweb) | 213.226.125.13 | Alpine 3.23 | `ssh -i private.key root@` (key must be chmod 600) | +| EXIT (Gcore Hel) | 85.234.86.140 | Debian 13 | `ssh -i hpriv.key debian@` (sudo NOPASSWD) | + +## Prerequisites (verify first, they were the real blockers) +1. **UDP 443 open both ways** (Security Group on EXIT; ENTRY provider). Test: `tcpdump -ni udp port 443` on the receiver while `echo x | nc -u -w1 443` from the sender. ICMP/TCP passing proves nothing about UDP. +2. Use **official** binary `apernet/hysteria` (HyNetworks/hysteria is a fork). Verify `sha256sum` against `hashes.txt` of the release: + `https://github.com/apernet/hysteria/releases/latest/download/{hysteria-linux-amd64,hashes.txt}` +3. Hysteria server `socks5`/`http` outbounds are **TCP-only** β†’ for UDP use TUN + policy routing (below), not a SOCKS5 chain. + +## EXIT (Debian) β€” server +- `/usr/local/bin/hysteria`, `/etc/hysteria/{server.crt,server.key(600),config.yaml}` +- Self-signed cert: `openssl req -x509 -nodes -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -days 3650 -subj /CN= -keyout server.key -out server.crt` +- `config.yaml`: `listen: :443`, `tls{cert,key}`, `auth{type: password, password: }`, `masquerade{type: proxy, proxy{url: https://news.ycombinator.com/, rewriteHost: true}}` +- systemd unit `hysteria-server` (`ExecStart=hysteria server -c ...`, `Restart=always`, `AmbientCapabilities=CAP_NET_BIND_SERVICE`) +- `/etc/sysctl.d/99-quic.conf`: `net.core.rmem_max=7500000`, `net.core.wmem_max=7500000` +- No IPv6 uplink here β†’ IPv4 only. + +## ENTRY (Alpine) β€” tunnel client + client-facing server +Packages: `iproute2 libcap-utils openssl`; user `hyedge` (`adduser -S -D -H -s /sbin/nologin`); `/dev/net/tun` must exist and module `tun` must be in `/etc/modules` (loaded module, not builtin; without it the tunnel does not come up after reboot). + +1. **`/etc/hysteria/client.yaml`** (600): `server: :443`, `auth: `, `tls{sni, insecure: true, pinSHA256: }`, `lazy: true`, `bandwidth{up: 180 mbps, down: 180 mbps}` (Brutal CC; measured path β‰ˆ191-195 Mbps via `hysteria speedtest`, single-flow without it was ~44 Mbps), `socks5{127.0.0.1:1080}`, `http{127.0.0.1:8080}`, `tun{name: hytun, mtu: 1400, timeout: 5m, address{ipv4: 100.100.100.101/30}}` (no `route:` β†’ no auto-routes). +2. **`/etc/hysteria/edge.yaml`** (600, owner hyedge): `listen: :443`, own self-signed `edge.crt/edge.key`, `auth{type: userpass, userpass{: }}`, `resolver{type: udp, udp{addr: 1.1.1.1:53}}`, `outbounds[direct{mode: "4"}]`, `trafficStats{listen: 127.0.0.1:9999, secret}`, `acl.inline`: `reject` 10/8,172.16/12,192.168/16,127/8,169.254/16,100.64/10 then `direct(all)`. + - Do **not** add `reject(::/0)` β€” it matches any domain having AAAA and rejects dual-stack sites. + - DoH resolver timed out via tun; UDP resolver works. +3. Binary copy for low port: `cp hysteria hysteria-edge; setcap cap_net_bind_service=+ep /usr/local/bin/hysteria-edge`. +4. **OpenRC** (`supervisor=supervise-daemon`, `respawn_delay=3`, `respawn_max=0`): + - `hysteria` (client, root) ← `need net` + - `hysteria-route` ← `need hysteria`; waits for `hytun`, then: + ``` + ip route replace blackhole default metric 1000 table 100 # anti-leak fallback + ip route replace default dev hytun metric 10 table 100 + ip rule add priority 100 ipproto udp sport 443 lookup main # edge replies bypass tun + ip rule add priority 101 uidrange lookup 100 + ``` + - `hysteria-edge` (`command_user=hyedge`) ← `need net hysteria-route` + - `rc-update add hysteria/hysteria-route/hysteria-edge default` +5. **sysctl (critical):** `net.ipv4.conf.all.rp_filter=2` and `default=2` (`/etc/sysctl.d/99-hytun.conf`). With strict `1` the TUN SYN-ACKs are dropped β†’ TCP via tun silently hangs. +6. Only uid `hyedge` traffic and, since 2026-09-30, the OpenVPN client subnet (`172.20.1.0/24`, rule 102, see below) enter the tunnel; SSH/root traffic untouched. + +## OpenVPN Monitor & Profiler on ENTRY (added 2026-09-30) +ENTRY also runs the web suite for OpenVPN (native OpenRC services, no containers). OpenVPN clients exit through the same `hytun` tunnel to EXIT. + +| Item | Value | +|---|---| +| Panel | `https://213.226.125.13:8088/` (self-signed TLS, login `tstark`, 2FA; password is set by the owner, not stored here) | +| OpenVPN | `udp/1194`, clients `172.20.1.0/24`, full tunnel | +| Services | `ovpmon-api`, `ovpmon-gatherer`, `ovpmon-profiler` (user `ovpmon`), `nginx`, `openvpn`, `ovpn-hytun`, `fail2ban` | +| Egress | `ip rule 102 from 172.20.1.0/24 β†’ table 100` (hytun, blackhole fallback) + MASQUERADE on `hytun` + FORWARD DROP to any other interface (no leak) | +| Privileges | APIs run unprivileged; a root helper (`ovpmon-helper` via `doas`, 6 fixed commands) validates/installs `server.conf`, publishes the CRL and controls `openvpn` | +| Hardening | SSH key-only (`00-hardening.conf`), fail2ban (`sshd`, `sshd-ddos`, `ovpmon-login`), input validation, CORS restricted | +| Docs | [`SUMMARY-ovpn-monitor.md`](SUMMARY-ovpn-monitor.md) (state, results, security assessment, rollback), `IMPLEMENTATION-01/02-*.md`, `PLAN-validation-and-privilege-drop.md`; application repo `DOCS/` | + +Check: `rc-status | grep -E "ovpmon|openvpn|ovpn-hytun|nginx|fail2ban"`, `ip rule | grep 102`, a VPN client on `https://ifconfig.me` shows the EXIT IP (85.234.86.140). + +## Clients +- URI: `hy2://:@:443/?sni=&insecure=1&pinSHA256=#name` (strip `#name` when scripting pin extraction). +- Hiddify/NekoBox/Streisand/Shadowrocket: paste URI. sing-box (SFA/SFI, β‰₯1.12): JSON profile with `hysteria2` outbound, `password: ":"`, embedded `tls.certificate` PEM (edge.crt), TUN inbound, DoH via outbound, `hijack-dns`, private + ENTRY IP β†’ direct. + +### Recommended client apps +Protocol: Hysteria 2 (QUIC/UDP 443). Server cert is self-signed, so the app must support `pinSHA256`/custom certificate β€” all apps below do via the URI/QR or sing-box JSON. + +| Platform | App | Import | Notes | +|---|---|---|---| +| Android | **Hiddify** (primary) | hy2:// URI or QR | sing-box core, pin support, simplest | +| Android | NekoBox for Android | hy2:// URI or QR | alternative, more knobs | +| Android | SFA (sing-box) | JSON profile only | official sing-box client; use `profiles/singbox/*.json` | +| iOS | **Streisand** (primary) | hy2:// URI or QR | free | +| iOS | Shadowrocket | hy2:// URI or QR | paid, stable | +| iOS | SFI (sing-box) / Hiddify | JSON profile / URI | sing-box >= 1.12 required for the JSON | +| Desktop | Hiddify, sing-box CLI, `hysteria client` | URI / JSON / YAML | sing-box needs root/admin for TUN | + +Required client settings (otherwise many sites fail or hang): +- **IPv6 off** (Hiddify: IPv6 mode = Disable / IPv4 only). EXIT has no IPv6; apps sending IPv6 literals get `no IPv4 address available`. +- Remote DNS via the tunnel (DoH `https://1.1.1.1/dns-query`); do not leak DNS outside. +- QUIC blocking is **not** needed (UDP is carried end to end). +- Verify: `ifconfig.me` shows the EXIT IP (85.234.86.140); `test-ipv6.com` shows no IPv6. + +App availability/names vary by store and region and change over time; this list comes from the project's client testing (Hiddify confirmed working on a phone) and general knowledge, not a current store check. One profile per person. + +## Profiles (20 client users) +`profiles/` (dir 700, files 600; contains secrets): `hy2/clientNN.txt` (hy2:// URI), `singbox/clientNN.json` (sing-box >=1.12), `qr/.png` (QR of the hy2:// URI, 22 files incl. laptop/mobile; made with `qrencode -t PNG -s 8 -m 2 -l M`; decoding verified), `ALL-hy2.txt`, `INDEX.md` (no passwords). Mobile apps: scan QR -> imports hy2:// profile. Users `client01..client20` live in `edge.yaml` `auth.userpass` (backup `edge.yaml.bak4`; `laptop`, `mobile` kept). Wrong password -> client error `authentication error, HTTP status code: 404`. +Add/revoke a user: edit `auth.userpass` on ENTRY, `rc-service hysteria-edge restart` (all sessions reconnect within seconds), then regenerate/delete the profile files. Verified login for client01/10/20 via temp client (egress = EXIT IP). + +## Verification checklist +``` +su -s /bin/sh hyedge -c 'curl -4 -s https://ifconfig.me' # = EXIT ip (TCP via tun) +su -s /bin/sh hyedge -c 'nslookup example.com 1.1.1.1' # UDP via tun +ip route del default dev hytun table 100; ; ip route replace default dev hytun metric 10 table 100 +hysteria client (temp, socks 127.0.0.1:1081) -> curl -x socks5h://... https://ifconfig.me # = EXIT ip; http://10.0.0.1 rejected +``` +Logs: ENTRY `/var/log/hysteria.log`, `/var/log/hysteria-edge.log`; EXIT `journalctl -u hysteria-server`. + +## Tuning / diagnostics learned +- Path MTU ENTRY↔EXIT = 1500 (DF ping + tracepath); hytun mtu 1400 is safe; MTU is **not** the cause of site failures here. +- Capacity test: on EXIT temporarily add `speedTest: true` to config.yaml, restart, run `hysteria speedtest -c /etc/hysteria/client.yaml` on ENTRY, then remove it. +- `su hyedge -c curl ` is ~4s slower (system resolver = provider DNS, unreachable via EXIT). Not a client issue: hysteria-edge uses its own `resolver` (1.1.1.1). Use `--resolve` or `-x socks5h` when benchmarking. +- Clients that connect to **IPv6 literals** get `no IPv4 address available` (EXIT has no IPv6). Fix on the client: IPv6 off / IPv4-only DNS strategy (Hiddify: IPv6 mode = disable). Server cannot fix it without EXIT IPv6. +- `UdpRcvbufErrors` rises at ~190 Mbps (quic-go requests 7 MB socket buffers; rmem_max already 7.5 MB) β€” cosmetic. + +## Hysteria diagnostics (manual) +Version: v2.12.3. Replace IPs/paths if the layout differs. On ENTRY run as root. + +### Service state +``` +# ENTRY (OpenRC) +for s in hysteria hysteria-route hysteria-edge; do rc-service $s status; done +ss -lunp | grep -E ':443\b' # edge listener (hysteria-edge) +ss -unp | grep hysteria # client UDP socket to EXIT +tail -f /var/log/hysteria.log /var/log/hysteria-edge.log +# EXIT (systemd) +systemctl status hysteria-server --no-pager; ss -lunp | grep :443 +journalctl -u hysteria-server -f # "client connected" / "TCP error" +``` + +### Tunnel (ENTRY -> EXIT) +``` +hysteria ping -c /etc/hysteria/client.yaml 1.1.1.1:443 # RTT of a connect via the tunnel +hysteria speedtest -c /etc/hysteria/client.yaml # needs `speedTest: true` on EXIT (remove afterwards) +ip -br a show hytun; ip rule | grep -E '^10[01]'; ip route show table 100 +su -s /bin/sh hyedge -c 'curl -4 -s https://ifconfig.me' # must print the EXIT IP +tcpdump -ni hytun -c 20 # traffic entering the tun +tcpdump -ni eth0 'udp port 443 and host 85.234.86.140' # QUIC to EXIT (on EXIT: enp3s0, host 213.226.125.13) +``` +- Client log `connected to server ... count: N` – N grows on each reconnect. `TUN UDP error ... EOF` right after an EXIT restart is normal. +- `rc-service hysteria status` = `crashed` and `connect error: timeout` -> UDP 443 path/Security Group problem (see prerequisites). + +### Clients (Traffic Stats API on edge, enabled, 127.0.0.1 only) +Config block in `edge.yaml`: `trafficStats: {listen: 127.0.0.1:9999, secret: }`. +``` +S=$(cat /etc/hysteria/.stats-secret); A="Authorization: $S"; U=http://127.0.0.1:9999 +curl -s -H "$A" $U/online # {"mobile":1} devices per user +curl -s -H "$A" $U/traffic # {"mobile":{"tx":..,"rx":..}} add ?clear=1 to reset counters +curl -s -H "$A" -H 'Accept: text/plain' $U/dump/streams # live streams: user, bytes, lifetime, req-addr +curl -s -H "$A" -X POST -d '["mobile"]' $U/kick # drop a user's sessions (they reconnect if creds still valid) +``` +Without the header the API returns 401. To block a user permanently remove it from `auth.userpass` and restart `hysteria-edge`. + +### Useful subcommands +``` +hysteria share -c /etc/hysteria/client.yaml # hy2:// URI for a client config (prints the password!) +hysteria check-update # compare with the latest release +hysteria version +``` + +### Error -> meaning +| Log text | Meaning | +|---|---| +| `no IPv4 address available` (edge) | client asked for an IPv6 literal; EXIT has no IPv6 -> disable IPv6 on the client | +| `rejected` (edge) | ACL hit (private ranges) | +| `resolve error ... deadline exceeded` (edge) | edge `resolver` unreachable through the tunnel | +| `no certificate matches the pinned hash` | wrong `pinSHA256` (strip `#name` from the URI fragment) | +| `authentication failed` / 404 on auth | wrong `user:password` | +| curl via tun hangs, `tcpdump -i hytun` shows SYN-ACK from dst on hytun | strict `rp_filter` -> set `=2` | + +## Known issues / notes +- Backups on ENTRY: `/etc/hysteria/*.bak*`. Remote shell: zsh does not word-split variables β†’ use functions for ssh wrappers. +- ENTRY reboot tested (2026-09-30): all three services, hytun, rules, sysctl and tunnel came up by themselves; mobile client reconnected. EXIT reboot tested too: hysteria-server and sysctl came up, ENTRY client reconnected automatically (count 2). +- strongSwan/IKE to Gcore (62.112.222.168) is a separate, unresolved task (needs UDP 500/4500 reachability); `charon` started manually, not enabled at boot. +- SSH on ENTRY is key-only since 2026-09-30 (a cloud-init drop-in used to re-enable passwords; hardening lives in `/etc/ssh/sshd_config.d/00-hardening.conf`). A full ENTRY reboot test after the OpenVPN Monitor changes passed (2026-09-30, see `REBOOT-TEST.md`). diff --git a/DOCS/Operations/IMPLEMENTATION-01-settings-validation.md b/DOCS/Operations/IMPLEMENTATION-01-settings-validation.md new file mode 100644 index 0000000..6f9c8d6 --- /dev/null +++ b/DOCS/Operations/IMPLEMENTATION-01-settings-validation.md @@ -0,0 +1,50 @@ +# АртСфакт внСдрСния: ΠΏ.1 β€” валидация настроСк Profiler + +Π”Π°Ρ‚Π°: 2026-09-30. Π£Π·Π΅Π»: 213.226.125.13 (Alpine, OpenRC). ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅: `/opt/OpenVPN-Monitoring-Simple`. План: `PLAN-validation-and-privilege-drop.md`. +ΠšΠΎΠΌΠΌΠΈΡ‚ Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ прилоТСния Π½Π° ΡƒΠ·Π»Π΅: `05f44b9` (Β«Profiler: validate server/PKI settings before they reach OpenVPN configΒ»), ΠΏΡƒΡˆ Π½Π΅ выполнялся. + +## 1. Π§Ρ‚ΠΎ Π²Π½Π΅Π΄Ρ€Π΅Π½ΠΎ + +| Π‘Π»ΠΎΠΉ | Π€Π°ΠΉΠ» | Π‘ΡƒΡ‚ΡŒ | +|---|---|---| +| A | `APP_PROFILER/schemas.py` | Π²Π°Π»ΠΈΠ΄Π°Ρ‚ΠΎΡ€Ρ‹ `SystemSettingsUpdate`, `PKISettingUpdate` (422) | +| B | `APP_PROFILER/services/validation.py` (Π½ΠΎΠ²Ρ‹ΠΉ), `services/generator.py` | `check_script`: скрипт Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ· `/etc/openvpn/scripts/`, root-owned, Π±Π΅Π· записи group/other | +| C | `APP_PROFILER/services/generator.py`, `routers/server.py` | ΠΎΡ‚ΠΊΠ°Π· ΠΏΡ€ΠΈ `\n`, `\r`, NUL, ΡƒΠΏΡ€Π°Π²Π»ΡΡŽΡ‰ΠΈΡ… символах, `"`, `\`; `ValueError` β†’ HTTP 400 | + +## 2. ΠŸΠΎΡ€ΡΠ΄ΠΎΠΊ внСдрСния + +1. Бэкап измСняСмых Ρ„Π°ΠΉΠ»ΠΎΠ²: `/root/app-bak/p1/` (`schemas.py`, `generator.py`, `server.py`). +2. Π”ΠΎΠ±Π°Π²Π»Π΅Π½ `services/validation.py`; ΠΏΡ€Π°Π²ΠΊΠΈ `schemas.py`, `generator.py`, `routers/server.py` (Python-ΠΏΠ°Ρ‚Ρ‡ с ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΎΠΉ якорСй `assert`). +3. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΈΠΌΠΏΠΎΡ€Ρ‚Π°: `venv/bin/python -c "import schemas"`. +4. `rc-service ovpmon-profiler restart`. +5. ВСсты (скрипт запускался Π½Π° ΡƒΠ·Π»Π΅ с локально Π²Ρ‹ΠΏΡƒΡ‰Π΅Π½Π½Ρ‹ΠΌ Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠΌ, Ρ‚.ΠΊ. Π½Π° ΡƒΡ‡Ρ‘Ρ‚ΠΊΠ΅ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Π° 2FA). +6. ДокумСнтация: `DOCS/Changes/2026-09-30_Settings_Validation.md`, ссылки Π² `README.md`, `DOCS/General/Index.md`; ΠΊΠΎΠΌΠΌΠΈΡ‚. + +## 3. Π”ΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒΡΡ‚Π²Π° (Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ тСстов) + +| ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° | Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ | +|---|---| +| ΠšΡ€ΡƒΠ³ΠΎΠ²ΠΎΠΉ `PUT` Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΡ… настроСк server / pki | 200 / 200 | +| 17 врСдоносных ΠΈΠ»ΠΈ Π½Π΅Π²Π°Π»ΠΈΠ΄Π½Ρ‹Ρ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ | всС 422 | +| 4 Π²Π°Π»ΠΈΠ΄Π½Ρ‹Ρ… измСнСния | всС 200 | +| Π‘ΠΊΡ€ΠΈΠΏΡ‚Ρ‹: root 755 / group-writable / симлинк / Π½Π΅Ρ‚ Ρ„Π°ΠΉΠ»Π° / Π²Π½Π΅ ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π° / traversal / пусто | принят / ΠΎΡ‚ΠΊΠ»ΠΎΠ½Ρ‘Π½ / ΠΎΡ‚ΠΊΠ»ΠΎΠ½Ρ‘Π½ / ΠΎΡ‚ΠΊΠ»ΠΎΠ½Ρ‘Π½ / ΠΎΡ‚ΠΊΠ»ΠΎΠ½Ρ‘Π½ / ΠΎΡ‚ΠΊΠ»ΠΎΠ½Ρ‘Π½ / принят | +| Π‘Π»ΠΎΠΉ C ΠΏΡ€ΠΈ ΠΎΠ±Ρ…ΠΎΠ΄Π΅ схСмы (5 ΠΈΠ½ΡŠΠ΅ΠΊΡ†ΠΈΠΉ) | всС Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½Ρ‹, Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ записано | +| Π Π΅Π½Π΄Π΅Ρ€ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΡ… настроСк ΠΏΡ€ΠΎΡ‚ΠΈΠ² Ρ€Π°Π±ΠΎΡ‡Π΅Π³ΠΎ `server.conf` | ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π΅Π½ | +| ПослС тСстов | настройки восстановлСны ΠΈ ΡΠΎΠ²ΠΏΠ°Π΄Π°ΡŽΡ‚ с исходными; `rc-status`: всС сСрвисы `started` | + +## 4. ΠžΡ‚ΠΊΠ°Ρ‚ + +```sh +cd /opt/OpenVPN-Monitoring-Simple/APP_PROFILER +cp /root/app-bak/p1/schemas.py schemas.py +cp /root/app-bak/p1/generator.py services/generator.py +cp /root/app-bak/p1/server.py routers/server.py +rm services/validation.py +rc-service ovpmon-profiler restart +``` +Π›ΠΈΠ±ΠΎ `git revert 05f44b9` Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ. + +## 5. ΠžΠ³ΠΎΠ²ΠΎΡ€ΠΊΠΈ +- ΠšΠ°Ρ‚Π°Π»ΠΎΠ³Π° `/etc/openvpn/scripts/` ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ Π½Π΅Ρ‚ (для тСста создавался Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ ΠΈ ΡƒΠ΄Π°Π»Ρ‘Π½): ΠΏΠ΅Ρ€Π΅Π΄ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ΠΌ скриптов ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ `root:root 755`, Ρ„Π°ΠΉΠ»Ρ‹ `755` ΠΎΡ‚ root. +- Π’Π°Π»ΠΈΠ΄Π°Ρ‚ΠΎΡ€Ρ‹ Π΄Π΅ΠΉΡΡ‚Π²ΡƒΡŽΡ‚ Π½Π° обновлСния; Ρ€Π°Π½Π΅Π΅ сохранённыС значСния ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‚ΡΡ ΠΏΡ€ΠΈ Ρ€Π΅Π½Π΄Π΅Ρ€Π΅ (слой C). +- Π‘Π»ΠΎΠΉ B/C Π·Π°Ρ‰ΠΈΡ‰Π°Π΅Ρ‚ ΠΊΠΎΠ½Ρ„ΠΈΠ³, Π½ΠΎ сСрвис ΠΏΠΎ-ΠΏΡ€Π΅ΠΆΠ½Π΅ΠΌΡƒ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΎΡ‚ root β€” это Π·Π°ΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚ ΠΏΡƒΠ½ΠΊΡ‚ 2 (см. `IMPLEMENTATION-02-…` послС выполнСния). diff --git a/DOCS/Operations/IMPLEMENTATION-02-privilege-separation.md b/DOCS/Operations/IMPLEMENTATION-02-privilege-separation.md new file mode 100644 index 0000000..59d2821 --- /dev/null +++ b/DOCS/Operations/IMPLEMENTATION-02-privilege-separation.md @@ -0,0 +1,68 @@ +# АртСфакт внСдрСния: ΠΏ.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` Π² настройках оставлСно Π²Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹ΠΌ, ΠΊΠ°ΠΊ Π±Ρ‹Π»ΠΎ. diff --git a/DOCS/Operations/PLAN-validation-and-privilege-drop.md b/DOCS/Operations/PLAN-validation-and-privilege-drop.md new file mode 100644 index 0000000..4a37bd6 --- /dev/null +++ b/DOCS/Operations/PLAN-validation-and-privilege-drop.md @@ -0,0 +1,82 @@ +# План: валидация настроСк (ΠΏ.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. diff --git a/DOCS/Operations/README.md b/DOCS/Operations/README.md new file mode 100644 index 0000000..64bdc9a --- /dev/null +++ b/DOCS/Operations/README.md @@ -0,0 +1,12 @@ +# Operations records (deployment on ENTRY, Timeweb) + +Records of one concrete deployment (host addresses, results, rollback). No passwords or keys are stored here. + +| Document | Content | +|---|---| +| [SUMMARY-ovpn-monitor.md](SUMMARY-ovpn-monitor.md) | Current state, access, install, egress via Hysteria2, security findings/fixes, risk assessment, commits, backups, open items | +| [Hysteria_Chain_Manifest.md](Hysteria_Chain_Manifest.md) | Hysteria 2 entry-exit chain manifest, diagnostics, plus the OpenVPN Monitor section | +| [REBOOT-TEST.md](REBOOT-TEST.md) | Reboot tests (Hysteria chain; ENTRY after the OpenVPN Monitor deployment) | +| [PLAN-validation-and-privilege-drop.md](PLAN-validation-and-privilege-drop.md) | Plan: settings validation and running the API unprivileged | +| [IMPLEMENTATION-01-settings-validation.md](IMPLEMENTATION-01-settings-validation.md) | Rollout record: settings validation | +| [IMPLEMENTATION-02-privilege-separation.md](IMPLEMENTATION-02-privilege-separation.md) | Rollout record: privilege separation and CRL publishing | diff --git a/DOCS/Operations/REBOOT-TEST.md b/DOCS/Operations/REBOOT-TEST.md new file mode 100644 index 0000000..637830e --- /dev/null +++ b/DOCS/Operations/REBOOT-TEST.md @@ -0,0 +1,106 @@ +# Reboot test: ENTRY (Timeweb) and EXIT (Hel) + +Date: 2026-09-30. Goal: confirm the Hysteria 2 chain recovers by itself after a reboot of each node, with no manual steps. + +Nodes: ENTRY 213.226.125.13 (Alpine, OpenRC), EXIT 85.234.86.140 (Debian 13, systemd). + +## Methodology + +Order: ENTRY first, then EXIT. Each node is rebooted separately, so the other side is always up and its reconnect behavior is observable. + +### 1. Pre-flight (before each reboot) +| Check | ENTRY | EXIT | +|---|---|---| +| Services enabled | `rc-update show default` (hysteria, hysteria-route, hysteria-edge) | `systemctl is-enabled hysteria-server` | +| Kernel module for TUN persists | `lsmod \| grep tun`, `grep -x tun /etc/modules` | not needed | +| sysctl persisted | `ls /etc/sysctl.d/` (99-hytun.conf, 99-quic.conf) | `ls /etc/sysctl.d/99-quic.conf` | +| Temporary test settings removed | n/a | `grep -c speedTest /etc/hysteria/config.yaml` = 0 | + +Any gap found is fixed before rebooting. + +### 2. Reboot +`sync`, then a delayed `reboot` / `systemctl reboot` (detached, so the SSH command returns). Wait 45 s, then poll SSH every 6 s (up to 20 tries) until `uptime` answers. + +### 3. Post-boot verification +ENTRY: +1. `rc-service hysteria|hysteria-route|hysteria-edge status` β†’ `started`. +2. `ls -l /dev/net/tun`, `ip -br a show hytun`. +3. `ip rule | grep -E '^10[01]'` and `ip route show table 100` β†’ both rules, default via hytun, blackhole fallback. +4. `sysctl net.ipv4.conf.all.rp_filter net.core.rmem_max` β†’ 2 and 7500000. +5. `ss -lunp | grep :443` β†’ `hysteria-edge` listening. +6. Tunnel TCP: `su -s /bin/sh hyedge -c 'curl -4 -s https://ifconfig.me'` β†’ must equal the EXIT IP. +7. Tunnel UDP: `su -s /bin/sh hyedge -c 'nslookup example.com 1.1.1.1'` β†’ address returned. +8. Logs: `/var/log/hysteria.log`, `/var/log/hysteria-edge.log`; a real client (mobile) reconnecting is a bonus signal. + +EXIT: +1. `systemctl is-active hysteria-server`, `ss -lunp | grep :443`. +2. `sudo /usr/sbin/sysctl net.core.rmem_max net.core.wmem_max` (`sysctl` is not in a non-root user's PATH). +3. `journalctl -u hysteria-server -b` β†’ "server up and running" and "client connected" from ENTRY. +4. From ENTRY: client log shows a new `connected to server` (count incremented) and the `hyedge` curl test (step 6 above) returns the EXIT IP. + +## Results + +### ENTRY reboot (Timeweb) +| Check | Result | +|---|---| +| Pre-flight | **Gap found:** module `tun` loaded by hand but absent from `/etc/modules` β†’ after reboot `/dev/net/tun` could be missing and the tunnel would not start. Fixed (`echo tun >> /etc/modules`) before rebooting. | +| SSH back | Yes, about 45-60 s after the reboot command | +| Services | hysteria, hysteria-route, hysteria-edge all `started` | +| TUN / routing | `/dev/net/tun` present, `hytun` up (100.100.100.101/30), rules 100 and 101 present, table 100: default dev hytun + blackhole | +| sysctl | `rp_filter=2`, `rmem_max=7500000` applied | +| Listener | `hysteria-edge` on UDP 443 | +| Tunnel TCP | `85.234.86.140` (EXIT IP) | +| Tunnel UDP | DNS answer received | +| Client | Hiddify (`mobile`) reconnected on its own right after boot | +| Noise in logs | A few dial timeouts to individual remote hosts (unreachable destinations, not a tunnel fault) | + +Verdict: **PASS** (after the `/etc/modules` fix). + +### EXIT reboot (Hel) +| Check | Result | +|---|---| +| Pre-flight | `hysteria-server` enabled, `99-quic.conf` present, `speedTest` absent from config (0) | +| SSH back | Yes, about 45-50 s after the reboot command | +| Service | `hysteria-server` `active`, listening on UDP 443 | +| sysctl | `rmem_max` and `wmem_max` = 7500000 | +| Journal | "server up and running" at boot, then "client connected" from 213.226.125.13 about 30 s later | +| ENTRY side | Client reconnected automatically (`connected to server`, `count: 2`); brief `TUN UDP error ... EOF` while EXIT was down; `hyedge` curl returned `85.234.86.140` | + +Verdict: **PASS**. + +## Conclusions +- Both nodes recover unattended; the ENTRY hysteria client (supervise-daemon, `lazy: true`) reconnects without restart when EXIT comes back. +- During an EXIT outage the anti-leak blackhole route in table 100 keeps `hyedge` traffic from leaving via ENTRY's own uplink (leak test done separately: removing the tun default route makes the test curl fail instead of exposing the ENTRY IP). +- Not covered: simultaneous reboot of both nodes, power-loss/unclean shutdown, and IKE/strongSwan (separate task, `charon` is not enabled at boot). + +## ENTRY reboot after the OpenVPN Monitor deployment (2026-09-30) + +Goal: confirm the whole ENTRY stack (Hysteria chain + OpenVPN Monitor, OpenVPN, egress rules, hardening) recovers by itself. Only ENTRY was rebooted; EXIT stayed up. + +### Pre-flight +- `rc-update show default`: `hysteria`, `hysteria-route`, `hysteria-edge`, `ovpn-hytun`, `openvpn`, `ovpmon-api`, `ovpmon-gatherer`, `ovpmon-profiler`, `nginx`, `fail2ban`, `sshd`, `chronyd` enabled. +- `tun` in `/etc/modules`; sysctl files present (`ip_forward=1`, `rp_filter=2`); `sshd -t` and `doas -C /etc/doas.d/ovpmon.conf` valid. +- Baseline snapshot `/root/pre-reboot-snapshot.txt`: service states, `ip rule 100-102`, table 100, nat/FORWARD rules, listeners, process owners. + +### Reboot and result +`sync`, detached `reboot`; SSH answered again after ~45 s. Post-boot snapshot `/root/post-reboot-snapshot.txt` is **identical** to the baseline. + +| Check | Result | +|---|---| +| All services in `default` | started by themselves | +| API processes | gunicorn/uvicorn/gatherer run as `ovpmon` | +| `hytun`, `tun0`, `rp_filter=2`, `ip_forward=1` | present | +| `ip rule` 100/101/102, table 100 (hytun + blackhole), MASQUERADE to `hytun`, FORWARD DROP guard | restored by `hysteria-route` / `ovpn-hytun` | +| Tunnel TCP as `hyedge` | `85.234.86.140` (EXIT) | +| Hysteria client | reconnected to EXIT | +| Panel | `https://…:8088/` 200, profiler without token 401, HTTPβ†’HTTPS 301, login `tstark` works | +| Helper/doas | `service status` and `process stats` OK; gatherer reads `openvpn-status.log` (`root:ovpmon 640` persisted) | +| `/etc/openvpn/crl.pem`, `/var/lib/ovpmon` ownership | intact | +| fail2ban | 3 jails active | +| SSH | `passwordauthentication no`, `permitrootlogin prohibit-password` | +| Errors in `ovpmon-*` logs since boot | none (older entries predate the boot) | +| Real VPN client | reconnected by itself ~3.5 min after boot (UDP client waits for its `keepalive 10 120` timeout); ~5 MB downloaded through the tunnel, MASQUERADE counters rose, leak guard `FORWARD DROP` counter stayed 0 | + +### Notes +- Expected client gap after a server reboot is up to ~2-4 minutes for UDP OpenVPN clients (ping-restart), not a fault of the server. +- `openvpn-status.log` keeps `root:ovpmon 640` because the file persists on disk; if it is ever deleted, OpenVPN recreates it as `600` and the gatherer cannot read it until the next `ovpmon-helper service start|restart` (which fixes the mode). Not triggered by a reboot. diff --git a/DOCS/Operations/SUMMARY-ovpn-monitor.md b/DOCS/Operations/SUMMARY-ovpn-monitor.md new file mode 100644 index 0000000..e9c9e0c --- /dev/null +++ b/DOCS/Operations/SUMMARY-ovpn-monitor.md @@ -0,0 +1,169 @@ +# 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`). diff --git a/README.md b/README.md index 63db79a..bd73ba8 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ No default user is created. Seed the initial admin with `OVPMON_INITIAL_ADMIN_US - Index: [DOCS/General/Index.md](DOCS/General/Index.md) - Deployment: [Docker](DOCS/General/Deployment_Docker.md) Β· [System services](DOCS/General/Deployment_Native.md) Β· [Nginx](DOCS/General/Nginx_Configuration.md) Β· [Service management](DOCS/General/Service_Management.md) +- Deployment records (state, results, reboot test, plans): [DOCS/Operations](DOCS/Operations/README.md) - Security model: [Security Architecture](DOCS/General/Security_Architecture.md) Β· root helper and doas rules: [`DOCS/General/privilege-separation/`](DOCS/General/privilege-separation/) - APIs: [Monitoring](DOCS/Core_Monitoring/API_Reference.md) Β· [Profiler](DOCS/Profiler_Management/API_Reference.md)