- README: short overview, quick start, config table and links. - DOCS/General: Deployment_Docker.md and Deployment_Native.md (system services, HTTPS, host hardening); refresh Index.md. - DOCS/Changes: security hardening, admin username change and egress via Hysteria2 with results and verification. - Drop mentions of the built-in admin/password account. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
4.2 KiB
Deployment: system services (no containers)
Tested on Alpine 3.23 (OpenRC); Debian/Ubuntu (systemd) differs only in service manager. Unit/init templates: systemd/, openrc/. Generic notes: Deployment, Service management, Nginx.
1. Packages
- Alpine:
apk add python3 py3-pip nginx nodejs npm openvpn easy-rsa iptables bash - Debian/Ubuntu:
apt install python3-venv nginx nodejs npm openvpn easy-rsa iptables
2. Backend
cd APP_CORE && python3 -m venv venv && venv/bin/pip install -r requirements.txt gunicorn
cd APP_PROFILER && python3 -m venv venv && venv/bin/pip install -r requirements.txt
mkdir -p APP_PROFILER/easy-rsa && cp -r /usr/share/easy-rsa/* APP_PROFILER/easy-rsa/ # Docker entrypoint does this automatically
3. Environment file
/etc/ovpmon/env (chmod 600), loaded by the services:
OVPMON_API_SECRET_KEY=<openssl rand -hex 32>
OVPMON_API_HOST=127.0.0.1
OVPMON_API_PORT=5001
OVPMON_OPENVPN_MONITOR_DB_PATH=/var/lib/ovpmon/openvpn_monitor.db
OVPMON_OPENVPN_MONITOR_LOG_PATH=/var/log/openvpn/openvpn-status.log
OVPMON_PROFILER_DB_PATH=/var/lib/ovpmon/ovpn_profiler.db
OVPMON_LOGGING_LEVEL=INFO
Create /var/lib/ovpmon, /var/log/ovpmon, /var/log/openvpn. For the first start also set OVPMON_INITIAL_ADMIN_USER / OVPMON_INITIAL_ADMIN_PASSWORD, then remove them.
4. Services
| Service | Command | Listens |
|---|---|---|
ovpmon-api |
APP_CORE/venv/bin/gunicorn -w 2 -b 127.0.0.1:5001 openvpn_api_v3:app (cwd APP_CORE) |
127.0.0.1:5001 |
ovpmon-gatherer |
APP_CORE/venv/bin/python openvpn_gatherer_v3.py (cwd APP_CORE) |
- |
ovpmon-profiler |
APP_PROFILER/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 (cwd APP_PROFILER) |
127.0.0.1:8000 |
OpenRC (Alpine): supervisor=supervise-daemon, respawn_delay=3, source /etc/ovpmon/env in start_pre, then rc-update add <svc> default && rc-service <svc> start. systemd: use the units from systemd/ with EnvironmentFile=/etc/ovpmon/env.
The Profiler restarts OpenVPN through rc-service openvpn (Alpine) or systemctl openvpn; on Alpine link the config: ln -s server.conf /etc/openvpn/openvpn.conf and enable the openvpn service.
5. UI and Nginx (HTTPS on 8088)
cd APP_UI && npm install && npm run build
mkdir -p /var/www/ovpmon && cp -r dist/. /var/www/ovpmon/
Certificate (self-signed, replace with a real one when a domain is available):
mkdir -p /etc/ovpmon/tls && cd /etc/ovpmon/tls
openssl req -x509 -nodes -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -days 825 \
-subj "/CN=ovpmon" -addext "subjectAltName=IP:<HOST-IP>,DNS:ovpmon" -keyout ovpmon.key -out ovpmon.crt
chmod 600 ovpmon.key
Nginx server (/etc/nginx/http.d/ovpmon.conf on Alpine): listen 8088 ssl, TLS 1.2/1.3, error_page 497 =301 https://$host:8088$request_uri, security headers, limit_req (5 r/min) on /api/auth/login, / → static UI, /api/ → 127.0.0.1:5001, /profiles-api/ → 127.0.0.1:8000/api/. Full listing: Nginx configuration. Do not declare a second ssl_session_cache shared:SSL zone with a different size than the main nginx.conf.
6. First run
https://<host>:8088/→ sign in with the seeded admin → Account: change username/password, enable 2FA.- PKI Configuration → Initialize PKI → generate server config → start OpenVPN → create profiles.
7. Host hardening (recommended)
- SSH: key-only (
PasswordAuthentication no,KbdInteractiveAuthentication no,PermitRootLogin prohibit-password); note that cloud-init drop-ins in/etc/ssh/sshd_config.d/can override the main file, so put settings in00-*.conf. - fail2ban: jails
sshdand one forPOST /api/auth/login(401/429/503) on the Nginx access log. - Backends bound to
127.0.0.1; only 8088 (UI/API) and the VPN port are public.
8. Verify
rc-status | grep -E "ovpmon|nginx|openvpn" # or: systemctl status ovpmon-*
ss -tlnp | grep -E ":(8088|5001|8000) "
curl -sk -o /dev/null -w "%{http_code}\n" https://127.0.0.1:8088/ # 200
curl -sk -o /dev/null -w "%{http_code}\n" https://127.0.0.1:8088/profiles-api/config # 401 without token