diff --git a/README.md b/README.md index ac6cb5f..b20b0f0 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ The rest are optional: |```FWD_IFACE``` ```PF_DEST_IP```|If needed, the container can be used as a gateway for other containers or devices by setting these. See [issue #20](https://github.com/thrnz/docker-wireguard-pia/issues/20) for more info. Note that these are for a specific use case, and in many cases using Docker's ```--net=container:xyz``` or docker-compose's ```network_mode: service:xyz``` instead, and leaving these vars unset, would be an easier way of accessing the VPN and forwarded port from other containers. |`PRE_UP` `POST_UP` `PRE_DOWN` `POST_DOWN`|Custom commands and/or scripts can be run at certain stages if needed. See [below](#scripting) for more info. |`PIA_DIP_TOKEN`|A dedicated ip token can be used by setting this. When set, `LOC` is not used. +|`META_IP=x.x.x.x` `META_CN=hostname401` `META_PORT=443`|On startup, the container needs access to PIA's API in order to download the server list (`serverlist.piaservers.net`) and generate an auth token (`www.privateinternetaccess.com`). These requests can be sent via PIA's 'meta' servers instead by setting all three of these env vars. Available 'meta' servers are listed on the PIA [server list](https://serverlist.piaservers.net/vpninfo/servers/v6), and can be set to a different location than `LOC`. ## Scripting Custom commands and/or scripts can be run at certain stages of the container's life-cycle by setting the `PRE_UP`, `POST_UP`, `PRE_DOWN`, and `POST_DOWN` env vars. `PRE_UP` is run prior to generating the WireGuard config, `POST_UP` is run after the WireGuard interface is brought up, and `PRE_DOWN` and `POST_DOWN` are run before and after the interface is brought down again when the container exits. diff --git a/extra/pia-auth.sh b/extra/pia-auth.sh index d3336c1..b7b61b8 100644 --- a/extra/pia-auth.sh +++ b/extra/pia-auth.sh @@ -7,9 +7,17 @@ # Options: # -u # -p +# -i (Optional) +# -o (Optional) +# -n (Optional) +# -c (Optional) Path to ca cert used to secure communication with "meta" servers # -# Example: -# pia-auth.sh -u myusername -p mypassword > ~/.pia-token +# Examples: +# ./pia-auth.sh -u myusername -p mypassword > ~/.pia-token +# ./pia-auth.sh -u myusername -p mypassword -i 12.34.56.78 -n location401 -p 443 -c /path/to/ca.crt > ~/.pia-token +# +# By default, the www.privateinternetaccess.com API endpoint is used. +# If needed, 'meta' services on the VPN servers themselves can be used instead. # # deauth using: # curl --silent --show-error --request POST \ @@ -20,36 +28,62 @@ [ -n "$DEBUG" ] && set -o xtrace -while getopts ":u:p:" args; do +while getopts ":u:p:i:c:o:n:" args; do case ${args} in u) - user=$OPTARG + user="$OPTARG" ;; p) - pass=$OPTARG + pass="$OPTARG" + ;; + i) + meta_ip="$OPTARG" + ;; + c) + cacert="$OPTARG" + ;; + o) + meta_port="$OPTARG" + ;; + n) + meta_cn="$OPTARG" ;; esac done usage() { - echo "Options:" - echo " -u " - echo " -p " + echo 'Options: + -u + -p + -i (Optional) + -o (Optional) + -n (Optional) + -c (Optional) Path to ca cert used to secure communication with "meta" servers' exit 1 } get_auth_token () { + if [ -n "$meta_port" ] && [ -n "$meta_ip" ] && [ -n "$meta_cn" ] && [ -n "$cacert" ]; then + # https://github.com/pia-foss/desktop/blob/master/daemon/src/metaserviceapibase.h + token_response=$(curl --silent --location --show-error --request POST --max-time "$curl_max_time" \ + --resolve "$meta_cn:$meta_port:$meta_ip" \ + --form "username=$user" \ + --form "password=$pass" \ + --cacert "$cacert" \ + "https://$meta_cn:$meta_port/api/client/v2/token") + else token_response=$(curl --silent --location --show-error --request POST --max-time "$curl_max_time" \ 'https://www.privateinternetaccess.com/api/client/v2/token' \ --form "username=$user" \ --form "password=$pass") - TOK=$(jq -r .'token' <<< "$token_response") - if [ -z "$TOK" ]; then - echo "Failed to acquire new auth token. Response:" >&2 - echo "$token_response" >&2 - exit 1 - fi - echo "$TOK" + fi + TOK=$(jq -r .'token' <<< "$token_response") + if [ -z "$TOK" ]; then + echo "Failed to acquire new auth token. Response:" >&2 + echo "$token_response" >&2 + exit 1 + fi + echo "$TOK" } if [ -z "$pass" ] || [ -z "$user" ]; then diff --git a/extra/wg-gen.sh b/extra/wg-gen.sh index c3cb959..9c03d46 100644 --- a/extra/wg-gen.sh +++ b/extra/wg-gen.sh @@ -26,8 +26,11 @@ # To use a dedicated ip, the PIA_DIP_TOKEN env var must be set # eg: $ PIA_DIP_TOKEN=DIPabc123 wg-gen.sh -t ~/.token -o ~/wg.conf # +# API requests can be sent via PIA's 'meta' servers by setting the META_IP META_CN and META_PORT env vars +# eg: $ META_IP=123.45.67.89 META_CN=hostname401 META_PORT=443 ./wg-gen.sh -t ~/.token ~/wg.conf +# # Available servers can be found here: -# https://serverlist.piaservers.net/vpninfo/servers/v4 +# https://serverlist.piaservers.net/vpninfo/servers/v6 # The public key for verifying the server list can be found here: # https://github.com/pia-foss/desktop/blob/122710c6ada5db83620c63faff2d805ea52d7f40/daemon/src/environment.cpp#L30 # The PIA ca cert can be found here: @@ -43,6 +46,8 @@ # 3: Invalid server location # 4: Registration failed +[ -n "$DEBUG" ] && set -o xtrace + fatal_error () { cleanup [ -n "$1" ] && exit "$1" @@ -115,14 +120,26 @@ verify_serverlist () get_dip_serverinfo () { - echo "$(date): Fetching dedicated ip server info" - dip_response=$(curl --silent --show-error $curl_params --location --request POST \ - 'https://www.privateinternetaccess.com/api/client/v2/dedicated_ip' \ - --header 'Content-Type: application/json' \ - --header "Authorization: Token $(cat $tokenfile)" \ - --data-raw '{ - "tokens":["'"$PIA_DIP_TOKEN"'"] - }') + if [ -n "$META_IP" ] && [ -n "$META_CN" ] && [ -n "$META_PORT" ]; then + echo "$(date): Fetching dedicated ip server info via meta server" + dip_response=$(curl --silent --show-error $curl_params --location --request POST \ + "https://$META_CN:$META_PORT/api/client/v2/dedicated_ip" \ + --header 'Content-Type: application/json' \ + --header "Authorization: Token $(cat $tokenfile)" \ + --cacert "$pia_cacert" --resolve "$META_CN:$META_PORT:$META_IP" \ + --data-raw '{ + "tokens":["'"$PIA_DIP_TOKEN"'"] + }') + else + echo "$(date): Fetching dedicated ip server info" + dip_response=$(curl --silent --show-error $curl_params --location --request POST \ + 'https://www.privateinternetaccess.com/api/client/v2/dedicated_ip' \ + --header 'Content-Type: application/json' \ + --header "Authorization: Token $(cat $tokenfile)" \ + --data-raw '{ + "tokens":["'"$PIA_DIP_TOKEN"'"] + }') + fi [ "$dip_response" == "HTTP Token: Access denied." ] && echo "Auth failed" && fatal_error 2 @@ -143,9 +160,15 @@ get_dip_serverinfo () } get_servers() { - echo "Fetching next-gen PIA server list" - curl --silent --show-error $curl_params \ - "https://serverlist.piaservers.net/vpninfo/servers/v6" > "$servers_raw" + if [ -n "$META_IP" ] && [ -n "$META_CN" ] && [ -n "$META_PORT" ]; then + echo "Fetching next-gen PIA server list via meta server" + curl --silent --show-error $curl_params --cacert "$pia_cacert" --resolve "$META_CN:$META_PORT:$META_IP" \ + "https://$META_CN:$META_PORT/vpninfo/servers/v6" > "$servers_raw" + else + echo "Fetching next-gen PIA server list" + curl --silent --show-error $curl_params \ + "https://serverlist.piaservers.net/vpninfo/servers/v6" > "$servers_raw" + fi head -n 1 "$servers_raw" | tr -d '\n' > "$servers_json" tail -n +3 "$servers_raw" | base64 -d > "$servers_sig" [ -n "$pia_pubkey" ] && verify_serverlist diff --git a/run b/run index c97302f..8029407 100644 --- a/run +++ b/run @@ -12,6 +12,7 @@ [[ "$PORT_FATAL" =~ ^[0-1]$ ]] || PORT_FATAL=0 # Should be a positive integer [[ "$KEEPALIVE" =~ ^[0-9]+$ ]] || KEEPALIVE=0 +[[ "$META_PORT" =~ ^[0-9]+$ ]] || META_PORT=443 # Maybe also check the following. They are all blank by default. # LOCAL_NETWORK= # PIA_CN= @@ -56,12 +57,15 @@ firewall_init () { iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT - # Temporarily allow DNS queries + # We also need to temporarily allow the following: + # DNS queries iptables -A OUTPUT -p udp --dport 53 -j ACCEPT - - # We also need to temporarily allow the following + # HTTPS to download the server list and access API for generating auth token iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT + # API access to register the public WireGuard key iptables -A OUTPUT -p tcp --dport 1337 -j ACCEPT + # Non-default API port if set + [ $META_PORT -ne 443 ] && iptables -A OUTPUT -p tcp --dport "$META_PORT" -j ACCEPT } # Alpine 3.19 changed the default iptables backend to iptables-nft @@ -137,7 +141,7 @@ get_auth_token () { [ -z "$USER" ] && echo "$(date): PIA username not set. Unable to retrieve new auth token." && fatal_error echo "$(date): Generating auth token" local token - if ! token=$(/scripts/pia-auth.sh -u "$USER" -p "$PASS"); then + if ! token=$(/scripts/pia-auth.sh -u "$USER" -p "$PASS" -n "$META_CN" -i "$META_IP" -o "$META_PORT" -c "$pia_cacrt"); then echo "$(date): Failed to acquire new auth token" && fatal_error fi echo "$token" > "$tokenfile" @@ -226,6 +230,7 @@ if [ $FIREWALL -eq 1 ]; then iptables -D OUTPUT -p udp --dport 53 -j ACCEPT iptables -D OUTPUT -p tcp --dport 443 -j ACCEPT iptables -D OUTPUT -p tcp --dport 1337 -j ACCEPT + [ $META_PORT -ne 443 ] && iptables -D OUTPUT -p tcp --dport "$META_PORT" -j ACCEPT # Allow docker network input/output for iface in /sys/class/net/*; do