fix: run purevpn-cli as non-root vpnuser with real sudo

purevpn-cli is designed to run as non-root and calls sudo internally for
privileged VPN setup. Running as root skips this flow and crashes.

- Add vpnuser (home=/root so login tokens are shared with root setup)
- Configure sudoers secure_path to include /opt/purevpn-cli/bin
- Wrap all purevpn-cli calls in entrypoint with pvpn() helper (su vpnuser)
- Keep iptables/danted running as root

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 18:44:03 +01:00
parent 34b5c4a8cd
commit a2a1ba3c37
2 changed files with 20 additions and 11 deletions

View File

@@ -21,6 +21,10 @@ VPN_WAIT=60
log() { echo "[$(date '+%H:%M:%S')] [$(hostname)] $*"; }
die() { log "FATAL: $*" >&2; exit 1; }
# purevpn-cli must run as non-root; it calls sudo internally for VPN setup.
# vpnuser has home=/root so login tokens written here are found automatically.
pvpn() { su -s /bin/bash -c "HOME=/root $(printf '%q ' "$@")" vpnuser; }
SOCKS_PID=""
# ── iptables: let HAProxy reach danted regardless of VPN kill-switch ─────────
@@ -106,9 +110,9 @@ manual_mode() {
log "═══ MANUAL CONNECT MODE ═══"
log "Connect to this container with:"
log " docker exec -it $(hostname) bash"
log "Then run:"
log " purevpn-cli --login"
log " purevpn-cli --connect \"Germany\" # or any location"
log "Then run (as vpnuser or root with HOME=/root):"
log " su -s /bin/bash -c 'HOME=/root purevpn-cli --login' vpnuser"
log " su -s /bin/bash -c 'HOME=/root purevpn-cli --connect \"Germany\"' vpnuser"
while true; do
log "Waiting for $VPN_IF to appear …"
@@ -165,13 +169,13 @@ auto_mode() {
log "Logging into PureVPN …"
expect -c "
set timeout 120
spawn purevpn-cli --login
spawn su -s /bin/bash -c {HOME=/root purevpn-cli --login} vpnuser
expect -re {[Ee]mail|[Uu]sername|[Ll]ogin} { send \"$PUREVPN_USER\r\" }
expect -re {[Pp]assword} { send \"$PUREVPN_PASS\r\" }
expect eof
" && log "Login OK" || {
log "expect login failed, trying stdin …"
printf '%s\n%s\n' "$PUREVPN_USER" "$PUREVPN_PASS" | purevpn-cli --login || true
printf '%s\n%s\n' "$PUREVPN_USER" "$PUREVPN_PASS" | pvpn purevpn-cli --login || true
}
# ── Connect loop (rotates through locations on failure/drop) ─────────────
@@ -181,7 +185,7 @@ auto_mode() {
USED_LOCATIONS+=("$loc")
log "Connecting → '$loc'"
purevpn-cli --connect "$loc" &
pvpn purevpn-cli --connect "$loc" &
VPN_PID=$!
if wait_for_tunnel "$VPN_WAIT"; then
@@ -192,13 +196,13 @@ auto_mode() {
monitor_loop || true
# Tunnel dropped — kill VPN process and try next location
kill "$VPN_PID" 2>/dev/null || true
purevpn-cli --disconnect 2>/dev/null || true
pvpn purevpn-cli --disconnect 2>/dev/null || true
sleep 3
log "Rotating to next location …"
else
log "'$loc' timed out — trying next location"
kill "$VPN_PID" 2>/dev/null || true
purevpn-cli --disconnect 2>/dev/null || true
pvpn purevpn-cli --disconnect 2>/dev/null || true
sleep 3
fi
done