diff --git a/vpn-node/Dockerfile b/vpn-node/Dockerfile index 4090a1a..cd64067 100644 --- a/vpn-node/Dockerfile +++ b/vpn-node/Dockerfile @@ -14,11 +14,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ net-tools openresolv \ && rm -rf /var/lib/apt/lists/* -# ── Allow passwordless sudo for all (container is already isolated) ─────────── -RUN echo "ALL ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers +# ── Non-root vpnuser ───────────────────────────────────────────────────────── +# purevpn-cli is designed to run as non-root; it calls sudo internally for +# privileged VPN setup. Home is /root so login tokens written by root are shared. +RUN useradd -M -d /root -s /bin/bash vpnuser + +# ── Sudoers: passwordless + correct PATH for vpnuser ───────────────────────── +RUN echo "vpnuser ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers \ + && echo 'Defaults:vpnuser secure_path="/opt/purevpn-cli/bin:/opt/purevpn-cli:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"' >> /etc/sudoers # ── Stub openvpn-systemd-resolved ──────────────────────────────────────────── -# Not in Debian repos; purevpn-cli checks for it before calling sudo. RUN mkdir -p /usr/lib/openvpn \ && printf '#!/bin/sh\nexit 0\n' \ | tee /usr/local/bin/openvpn-systemd-resolved \ diff --git a/vpn-node/entrypoint.sh b/vpn-node/entrypoint.sh index b6ed760..c1ca7fe 100755 --- a/vpn-node/entrypoint.sh +++ b/vpn-node/entrypoint.sh @@ -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