|
@@ -0,0 +1,117 @@
|
|
|
|
|
+#!/bin/sh
|
|
|
|
|
+
|
|
|
|
|
+# Requests a forwarded port from PIA, keeps it bound and points deluge's
|
|
|
|
|
+# listen port at it. PIA only forwards ports on non-US regions.
|
|
|
|
|
+
|
|
|
|
|
+VPN_DEV="${VPN_DEV:-tun0}"
|
|
|
|
|
+TOKEN_URL="https://www.privateinternetaccess.com/api/client/v2/token"
|
|
|
|
|
+BIND_INTERVAL=900
|
|
|
|
|
+
|
|
|
|
|
+log() {
|
|
|
|
|
+ echo "pia-port-forward: $*"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# PIA_USER/PIA_PASS win; otherwise reuse the auth-user-pass file from the VPN config
|
|
|
|
|
+load_credentials() {
|
|
|
|
|
+ if [ -n "${PIA_USER:-}" ] && [ -n "${PIA_PASS:-}" ]; then
|
|
|
|
|
+ return 0
|
|
|
|
|
+ fi
|
|
|
|
|
+
|
|
|
|
|
+ auth_file=$(awk '$1 == "auth-user-pass" { print $2; exit }' /vpn/vpn 2>/dev/null)
|
|
|
|
|
+ if [ -z "$auth_file" ]; then
|
|
|
|
|
+ log "set PIA_USER and PIA_PASS, or use auth-user-pass <file> in the VPN config"
|
|
|
|
|
+ return 1
|
|
|
|
|
+ fi
|
|
|
|
|
+ case "$auth_file" in
|
|
|
|
|
+ /*) ;;
|
|
|
|
|
+ *) auth_file="/vpn/$auth_file" ;;
|
|
|
|
|
+ esac
|
|
|
|
|
+
|
|
|
|
|
+ PIA_USER=$(sed -n 1p "$auth_file" | tr -d '\r')
|
|
|
|
|
+ PIA_PASS=$(sed -n 2p "$auth_file" | tr -d '\r')
|
|
|
|
|
+ if [ -z "$PIA_USER" ] || [ -z "$PIA_PASS" ]; then
|
|
|
|
|
+ log "could not read credentials from $auth_file"
|
|
|
|
|
+ return 1
|
|
|
|
|
+ fi
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+wait_for_tunnel() {
|
|
|
|
|
+ until ip -4 addr show dev "$VPN_DEV" 2>/dev/null | grep -q inet; do
|
|
|
|
|
+ sleep 2
|
|
|
|
|
+ done
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# The port forwarding API listens on the VPN gateway that redirect-gateway routes through
|
|
|
|
|
+vpn_gateway() {
|
|
|
|
|
+ ip -4 route show dev "$VPN_DEV" | awk '
|
|
|
|
|
+ $1 == "0.0.0.0/1" && $2 == "via" { print $3; found = 1; exit }
|
|
|
|
|
+ $2 == "via" && !first { first = $3 }
|
|
|
|
|
+ END { if (!found && first) print first }'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+get_token() {
|
|
|
|
|
+ printf '%s' "$PIA_PASS" |
|
|
|
|
|
+ curl -s -m 15 -X POST "$TOKEN_URL" \
|
|
|
|
|
+ --form "username=$PIA_USER" \
|
|
|
|
|
+ --form "password=<-" |
|
|
|
|
|
+ jq -r '.token // empty'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# -k: the API is only reachable inside the tunnel and its certificate is issued
|
|
|
|
|
+# for the server's hostname, which we don't know here
|
|
|
|
|
+pf_api() {
|
|
|
|
|
+ endpoint=$1
|
|
|
|
|
+ shift
|
|
|
|
|
+ curl -sk -m 15 -G "$@" "https://$gateway:19999/$endpoint"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+set_deluge_port() {
|
|
|
|
|
+ log "setting deluge listen port to $1"
|
|
|
|
|
+ until su deluge -c "deluge-console -c /config 'connect 127.0.0.1:$PORT; config --set listen_ports ($1, $1)'" 2>&1 |
|
|
|
|
|
+ grep -q "Configuration value successfully updated"; do
|
|
|
|
|
+ sleep 5
|
|
|
|
|
+ done
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+load_credentials || exit 1
|
|
|
|
|
+
|
|
|
|
|
+while true; do
|
|
|
|
|
+ wait_for_tunnel
|
|
|
|
|
+ gateway=$(vpn_gateway)
|
|
|
|
|
+ if [ -z "$gateway" ]; then
|
|
|
|
|
+ log "no gateway found on $VPN_DEV"
|
|
|
|
|
+ sleep 30
|
|
|
|
|
+ continue
|
|
|
|
|
+ fi
|
|
|
|
|
+
|
|
|
|
|
+ token=$(get_token)
|
|
|
|
|
+ if [ -z "$token" ]; then
|
|
|
|
|
+ log "failed to get a PIA token, check the username and password"
|
|
|
|
|
+ sleep 60
|
|
|
|
|
+ continue
|
|
|
|
|
+ fi
|
|
|
|
|
+
|
|
|
|
|
+ response=$(pf_api getSignature --data-urlencode "token=$token")
|
|
|
|
|
+ if [ "$(echo "$response" | jq -r '.status // empty' 2>/dev/null)" != "OK" ]; then
|
|
|
|
|
+ log "getSignature failed (does this region support port forwarding?): $response"
|
|
|
|
|
+ sleep 300
|
|
|
|
|
+ continue
|
|
|
|
|
+ fi
|
|
|
|
|
+ payload=$(echo "$response" | jq -r .payload)
|
|
|
|
|
+ signature=$(echo "$response" | jq -r .signature)
|
|
|
|
|
+ port=$(echo "$payload" | base64 -d | jq -r .port)
|
|
|
|
|
+ log "PIA assigned port $port"
|
|
|
|
|
+
|
|
|
|
|
+ deluge_port_set=false
|
|
|
|
|
+ while [ "$(pf_api bindPort --data-urlencode "payload=$payload" --data-urlencode "signature=$signature" |
|
|
|
|
|
+ jq -r '.status // empty' 2>/dev/null)" = "OK" ]; do
|
|
|
|
|
+ if [ "$deluge_port_set" = false ]; then
|
|
|
|
|
+ set_deluge_port "$port"
|
|
|
|
|
+ deluge_port_set=true
|
|
|
|
|
+ fi
|
|
|
|
|
+ sleep "$BIND_INTERVAL"
|
|
|
|
|
+ done
|
|
|
|
|
+
|
|
|
|
|
+ log "bindPort failed, requesting a new port"
|
|
|
|
|
+ sleep 10
|
|
|
|
|
+done
|