pia_port_forward.sh 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/bin/sh
  2. # Requests a forwarded port from PIA, keeps it bound and points deluge's
  3. # listen port at it. PIA only forwards ports on non-US regions.
  4. VPN_DEV="${VPN_DEV:-tun0}"
  5. TOKEN_URL="https://www.privateinternetaccess.com/api/client/v2/token"
  6. BIND_INTERVAL=900
  7. log() {
  8. echo "pia-port-forward: $*"
  9. }
  10. # PIA_USER/PIA_PASS win; otherwise reuse the auth-user-pass file from the VPN config
  11. load_credentials() {
  12. if [ -n "${PIA_USER:-}" ] && [ -n "${PIA_PASS:-}" ]; then
  13. return 0
  14. fi
  15. auth_file=$(awk '$1 == "auth-user-pass" { print $2; exit }' /vpn/vpn 2>/dev/null)
  16. if [ -z "$auth_file" ]; then
  17. log "set PIA_USER and PIA_PASS, or use auth-user-pass <file> in the VPN config"
  18. return 1
  19. fi
  20. case "$auth_file" in
  21. /*) ;;
  22. *) auth_file="/vpn/$auth_file" ;;
  23. esac
  24. PIA_USER=$(sed -n 1p "$auth_file" | tr -d '\r')
  25. PIA_PASS=$(sed -n 2p "$auth_file" | tr -d '\r')
  26. if [ -z "$PIA_USER" ] || [ -z "$PIA_PASS" ]; then
  27. log "could not read credentials from $auth_file"
  28. return 1
  29. fi
  30. }
  31. wait_for_tunnel() {
  32. until ip -4 addr show dev "$VPN_DEV" 2>/dev/null | grep -q inet; do
  33. sleep 2
  34. done
  35. }
  36. # The port forwarding API listens on the VPN gateway that redirect-gateway routes through
  37. vpn_gateway() {
  38. ip -4 route show dev "$VPN_DEV" | awk '
  39. $1 == "0.0.0.0/1" && $2 == "via" { print $3; found = 1; exit }
  40. $2 == "via" && !first { first = $3 }
  41. END { if (!found && first) print first }'
  42. }
  43. get_token() {
  44. printf '%s' "$PIA_PASS" |
  45. curl -s -m 15 -X POST "$TOKEN_URL" \
  46. --form "username=$PIA_USER" \
  47. --form "password=<-" |
  48. jq -r '.token // empty'
  49. }
  50. # -k: the API is only reachable inside the tunnel and its certificate is issued
  51. # for the server's hostname, which we don't know here
  52. pf_api() {
  53. endpoint=$1
  54. shift
  55. curl -sk -m 15 -G "$@" "https://$gateway:19999/$endpoint"
  56. }
  57. set_deluge_port() {
  58. log "setting deluge listen port to $1"
  59. until su deluge -c "deluge-console -c /config 'connect 127.0.0.1:$PORT; config --set listen_ports ($1, $1)'" 2>&1 |
  60. grep -q "Configuration value successfully updated"; do
  61. sleep 5
  62. done
  63. }
  64. load_credentials || exit 1
  65. while true; do
  66. wait_for_tunnel
  67. gateway=$(vpn_gateway)
  68. if [ -z "$gateway" ]; then
  69. log "no gateway found on $VPN_DEV"
  70. sleep 30
  71. continue
  72. fi
  73. token=$(get_token)
  74. if [ -z "$token" ]; then
  75. log "failed to get a PIA token, check the username and password"
  76. sleep 60
  77. continue
  78. fi
  79. response=$(pf_api getSignature --data-urlencode "token=$token")
  80. if [ "$(echo "$response" | jq -r '.status // empty' 2>/dev/null)" != "OK" ]; then
  81. log "getSignature failed (does this region support port forwarding?): $response"
  82. sleep 300
  83. continue
  84. fi
  85. payload=$(echo "$response" | jq -r .payload)
  86. signature=$(echo "$response" | jq -r .signature)
  87. port=$(echo "$payload" | base64 -d | jq -r .port)
  88. log "PIA assigned port $port"
  89. deluge_port_set=false
  90. while [ "$(pf_api bindPort --data-urlencode "payload=$payload" --data-urlencode "signature=$signature" |
  91. jq -r '.status // empty' 2>/dev/null)" = "OK" ]; do
  92. if [ "$deluge_port_set" = false ]; then
  93. set_deluge_port "$port"
  94. deluge_port_set=true
  95. fi
  96. sleep "$BIND_INTERVAL"
  97. done
  98. log "bindPort failed, requesting a new port"
  99. sleep 10
  100. done