Skip to content

Instantly share code, notes, and snippets.

@triffid
Last active February 15, 2024 06:48
Show Gist options
  • Save triffid/da48f3c99f1ff334571ae49be80d591b to your computer and use it in GitHub Desktop.
Save triffid/da48f3c99f1ff334571ae49be80d591b to your computer and use it in GitHub Desktop.
shell script for accessing PIA wireguard
#!/sbin/openrc-run
command="/root/bin/pia-wg.sh"
CONFIGDIR="${CONFIGDIR:-/var/cache/pia-wg}"
CONFIG="${CONFIG:-/etc/pia-wg/pia-wg.conf}"
extra_started_commands="reload"
depend() {
need net sysfs
after modules ip-rules
use logger
}
start_pre() {
if ! [ -e "$CONFIG" ]
then
echo "Please generate a config with pia-wg.sh and copy it to $CONFIG"
return 1
fi
if ! [ -w "$CONFIGDIR" ] || ! [ -d "$CONFIGDIR" ]
then
echo "$CONFIGDIR is not a writable directory"
return 1
fi
return 0
}
start() {
(
export CONFIGDIR="$CONFIGDIR"
export CONFIG="$CONFIG"
while "$command" "$@" 2>&1 | tee /var/log/pia-wg.log && [ ${PIPESTATUS[0]} -ne 0 ]
do
ewarn "Failed, retrying"
sleep 1
done
einfo "OK"
);
return 0
}
reload() {
start -n
}
restart() {
stop
start
}
stop() {
source "$CONFIG"
ip link del "${PIA_INTERFACE:-pia}"
}
#!/bin/bash
PIA_CONFIG="$(dirname "$(realpath "$(which "$0")")")/pia-config.sh"
if ! [ -r "$PIA_CONFIG" ]
then
echo "Can't find pia-config.sh at $PIA_CONFIG - if you've symlinked pia-wg.sh, please also symlink that file"
EXIT=1
fi
[ -n "$EXIT" ] && exit 1
source "$PIA_CONFIG"
SERVER_VIP="$(jq -r .server_vip "$REMOTEINFO")"
ping -I$PIA_INTERFACE -n -w5 -W0.5 -c5 "$SERVER_VIP"
#!/bin/bash
if [ -t 1 ]
then
BOLD=$'\e[1m'
NORMAL=$'\e[0m'
fi
TAB=$'\t'
if [ -z "$CONFIGDIR" ]
then
if [ $EUID -eq 0 ]
then
CONFIGDIR="/var/cache/pia-wg"
else
CONFIGDIR="$HOME/.config/pia-wg"
fi
mkdir -p "$CONFIGDIR"
fi
if [ -z "$CONFIG" ]
then
if [ $EUID -eq 0 ]
then
CONFIG="/etc/pia-wg/pia-wg.conf"
else
CONFIG="$CONFIGDIR/pia-wg.conf"
fi
fi
if [ -r "$CONFIG" ]
then
source "$CONFIG"
fi
if [ -z "$CLIENT_PRIVATE_KEY" ]
then
echo "Generating new private key"
CLIENT_PRIVATE_KEY="$(wg genkey)"
fi
if [ -z "$CLIENT_PUBLIC_KEY" ]
then
CLIENT_PUBLIC_KEY=$(wg pubkey <<< "$CLIENT_PRIVATE_KEY")
fi
if [ -z "$CLIENT_PUBLIC_KEY" ]
then
echo "Failed to generate client public key, check your config!"
exit 1
fi
if [ -z "$LOC" ]
then
echo "Setting default location: ${BOLD}any${NORMAL}"
LOC="."
fi
if [ -z "$PIA_INTERFACE" ]
then
echo "Setting default wireguard interface name: ${BOLD}pia${NORMAL}"
PIA_INTERFACE="pia"
fi
if [ -z "$WGCONF" ]
then
WGCONF="$CONFIGDIR/${PIA_INTERFACE}.conf"
fi
if [ -z "$PIA_CERT" ]
then
PIA_CERT="$CONFIGDIR/rsa_4096.crt"
fi
if [ -z "$TOKENFILE" ]
then
TOKENFILE="$CONFIGDIR/token"
fi
if [ -z "$TOK" ] && [ -r "$TOKENFILE" ]
then
TOK=$(< "$TOKENFILE")
fi
if [ -z "$DATAFILE" ]
then
DATAFILE="$CONFIGDIR/data.json"
fi
if [ -z "$DATAFILE_NEW" ]
then
DATAFILE_NEW="$CONFIGDIR/data_new.json"
fi
if [ -z "$REMOTEINFO" ]
then
REMOTEINFO="$CONFIGDIR/remote.info"
fi
if [ -z "$CONNCACHE" ]
then
CONNCACHE="$CONFIGDIR/cache.json"
fi
if [ -z "$HARDWARE_ROUTE_TABLE" ]
then
# 0xca6c
HARDWARE_ROUTE_TABLE=51820
fi
if [ -z "$VPNONLY_ROUTE_TABLE" ]
then
# 0xca6d
VPNONLY_ROUTE_TABLE=51821
fi
if [ -z "$PF_SIGFILE" ]
then
PF_SIGFILE="$CONFIGDIR/pf-sig"
fi
if [ -z "$PF_BINDFILE" ]
then
PF_BINDFILE="$CONFIGDIR/pf-bind"
fi
#!/bin/bash
PIA_CONFIG="$(dirname "$(realpath "$(which "$0")")")/pia-config.sh"
if ! [ -r "$PIA_CONFIG" ]
then
echo "Can't find pia-config.sh at $PIA_CONFIG - if you've symlinked pia-wg.sh, please also symlink that file"
EXIT=1
fi
[ -n "$EXIT" ] && exit 1
source "$PIA_CONFIG"
SERVER_IP="$(jq -r .server_ip "$REMOTEINFO")"
if [ -r "$CONNCACHE" ]
then
jq . "$CONNCACHE"
elif [ -z "$(jq '.regions | .[] | select(.servers.wg[0].ip == "'"$SERVER_IP"'")' "$DATAFILE_NEW")" ]
then
SERVER_IP_S="$(cut -d. -f1-3 <<< $SERVER_IP)"
jq '.regions | .[] | select(.servers.wg[0].ip | test("^'"$SERVER_IP_S"'"))' "$DATAFILE_NEW"
echo "Note: Inexact match for $SERVER_IP_S.* ($SERVER_IP not found)" >/dev/stderr
else
jq '.regions | .[] | select(.servers.wg[0].ip == "'"$SERVER_IP"'")' "$DATAFILE_NEW"
fi
#!/bin/bash
PIA_CONFIG="$(dirname "$(realpath "$(which "$0")")")/pia-config.sh"
if ! [ -r "$PIA_CONFIG" ]
then
echo "Can't find pia-config.sh at $PIA_CONFIG - if you've symlinked pia-wg.sh, please also symlink that file"
EXIT=1
fi
[ -n "$EXIT" ] && exit 1
source "$PIA_CONFIG"
if [ -r "$CONNCACHE" ]
then
WG_INFO="$(jq -r . "$CONNCACHE")"
fi
SERVER_IP="$(jq -r .server_ip "$REMOTEINFO")"
if [ -z "$WG_INFO" ]
then
WG_INFO="$(jq '.regions | .[] | select(.servers.wg[0].ip == "'"$SERVER_IP"'")' "$DATAFILE_NEW")"
fi
if [ -z "$WG_INFO" ]
then
SERVER_IP_S="$(cut -d. -f1-3 <<< $SERVER_IP)"
WG_INFO="$(jq '.regions | .[] | select(.servers.wg[0].ip | test("^'"$SERVER_IP_S"'"))' "$DATAFILE_NEW")"
fi
if [ -z "$WG_INFO" ]
then
echo "Couldn't determine server information even with fuzzy search, is your $DATAFILE_NEW ok?" >/dev/stderr
exit 1
fi
if [ "$(jq -r .port_forward <<< "$WG_INFO")" != true ]
then
echo "Current server doesn't support port forwarding:"
jq . <<< "$WG_INFO"
exit 1
fi
WG_NAME="$(jq -r .name <<< "$WG_INFO")"
WG_DNS="$(jq -r .dns <<< "$WG_INFO")"
WG_HOST="$(jq -r '.servers.wg[0].ip' <<< "$WG_INFO")"
WG_CN="$(jq -r '.servers.wg[0].cn' <<< "$WG_INFO")"
# sections of the below adapted from Threarah's work at
# https://github.com/thrnz/docker-wireguard-pia/blob/003f79f3b6ba24387e10d7de63ec62e98e6518a5/run#L233-L270 with permission
# Also see https://www.reddit.com/r/PrivateInternetAccess/comments/h9y4da/is_there_any_way_to_generate_wireguard_config/fxhkpjt/
if [ -r "$PF_SIGFILE" ]
then
PF_SIG="$(< "$PF_SIGFILE")"
PF_PAYLOAD_RAW=$(jq -r .payload <<< "$PF_SIG")
PF_PAYLOAD=$(base64 -d <<< "$PF_PAYLOAD_RAW")
PF_TOKEN_EXPIRY_RAW=$(jq -r .expires_at <<< "$PF_PAYLOAD")
PF_TOKEN_EXPIRY=$(date --date="$PF_TOKEN_EXPIRY_RAW" +%s)
fi
if [ $(( "$PF_TOKEN_EXPIRY" - $(date -u +%s) )) -le 900 ]
then
echo "Signature stale, refetching"
# Very strange - must connect via 10.0/8 private VPN link to the server's public IP - why?
# I tried SERVER_VIP (10.0/8 private IP) instead of SERVER_IP (public IP) but it won't connect
# It also won't connect if you try to connect from the internet, hence needing --interface "$PIA_INTERFACE"
PF_SIG="$(curl --interface "$PIA_INTERFACE" --CAcert "$PIA_CERT" --get --silent --show-error --retry 5 --retry-delay 5 --max-time 15 --data-urlencode token@/dev/fd/3 --resolve "$WG_CN:19999:$SERVER_IP" "https://$WG_CN:19999/getSignature" 3< <(echo -n "$TOK") | tee "$PF_SIGFILE")"
PF_STATUS="$(jq -r .status <<< "$PF_SIG")"
if [ "$PF_STATUS" != "OK" ]
then
echo "Signature retrieval failed: $PF_STATUS"
jq . <<< "$PF_SIG"
exit 1
fi
PF_PAYLOAD_RAW=$(jq -r .payload <<< "$PF_SIG")
PF_PAYLOAD=$(base64 -d <<< "$PF_PAYLOAD_RAW")
PF_TOKEN_EXPIRY_RAW=$(jq -r .expires_at <<< "$PF_PAYLOAD")
PF_TOKEN_EXPIRY=$(date +%Y-%m-%dT%H:%M:%S --date="$PF_TOKEN_EXPIRY_RAW" +%s)
fi
PF_GETSIGNATURE=$(jq -r .signature <<< "$PF_SIG")
PF_PORT=$(jq -r .port <<< "$PF_PAYLOAD")
PF_BIND="$(curl --interface "$PIA_INTERFACE" --CAcert "$PIA_CERT" --get --silent --show-error --retry 5 --retry-delay 5 --max-time 15 --data-urlencode payload@/dev/fd/3 --data-urlencode signature@/dev/fd/4 --resolve "$WG_CN:19999:$SERVER_IP" "https://$WG_CN:19999/bindPort" 3< <(echo -n "$PF_PAYLOAD_RAW") 4< <(echo -n "$PF_GETSIGNATURE") )"
PF_STATUS="$(jq -r .status <<< "$PF_BIND")"
if [ "$PF_STATUS" != "OK" ]
then
echo "Bind failed: $PF_STATUS"
jq . <<< "$PF_BIND"
exit 1
fi
( echo -n "PIA Server->Bind: "; jq -r .message <<< "$PF_BIND"; ) > /dev/stderr
echo > /dev/stderr
echo -n "Bound port: " > /dev/stderr
echo "$PF_PORT"
echo > /dev/stderr
###############################################################################
# #
# TODO: make this more flexible for others' systems #
# #
###############################################################################
echo "To test if your port has successfully been forwarded, execute:"
echo "transmission-remote -p "$PF_PORT" -pt"
###############################################################################
# #
# #
# #
###############################################################################
exit 0
#!/bin/bash
# original script posted by tpretz at https://www.reddit.com/r/PrivateInternetAccess/comments/g08ojr/is_wireguard_available_yet/fnvs20c/
# and at https://gist.github.com/tpretz/5ea1226517d95361f063f621e45de0a6
#
# significantly modified by Triffid_Hunter
#
# Improved with suggestions from Threarah at https://www.reddit.com/r/PrivateInternetAccess/comments/h9y4da/is_there_any_way_to_generate_wireguard_config/fv3cgi9/
#
# After the first run to fetch various data files and an auth token, this script does not require the ability to DNS resolve privateinternetaccess.com
while [ -n "$1" ]
do
case "$1" in
"-r")
shift
OPT_RECONNECT=1
;;
"-c")
shift
OPT_CONFIGONLY=1
;;
"-h")
shift
OPT_SHOWHELP=1
;;
*)
echo "Unrecognized option: $1"
shift
OPT_SHOWHELP=1
;;
esac
done
if [ -n "$OPT_SHOWHELP" ]
then
echo
echo "USAGE: $(basename "$0") [-r] [-c]"
echo
echo " -r Force reconnection even if a cached link is available"
echo
echo " -c Config only - generate a WireGuard config but do not apply it to this system"
echo
exit 1
fi
if ! which curl &>/dev/null
then
echo "The 'curl' utility is required"
echo " Most package managers should have a 'curl' package available"
EXIT=1
fi
if ! which jq &>/dev/null
then
echo "The 'jq' utility is required"
echo " Most package managers should have a 'jq' package available"
EXIT=1
fi
if [ -z "$OPT_CONFIGONLY" ]
then
if ! which ip &>/dev/null
then
echo "The 'ip' utility from iproute2 is needed to apply configs to this machine"
echo " Most package managers should have a 'iproute2' package available"
EXIT2=1
fi
if ! which wg &>/dev/null
then
echo "The 'wg' utility from wireguard-tools is needed to apply configs to this machine"
echo " Most package managers should have a 'wireguard-tools' package available"
EXIT2=1
fi
if [ -n "$EXIT2" ]
then
echo
echo "You can use the -c option if you wish to only generate a config"
fi
EXIT="${EXIT}${EXIT2}"
fi
PIA_CONFIG="$(dirname "$(realpath "$(which "$0")")")/pia-config.sh"
if ! [ -r "$PIA_CONFIG" ]
then
echo "Can't find pia-config.sh at $PIA_CONFIG - if you've symlinked pia-wg.sh, please also symlink that file"
EXIT=1
fi
[ -n "$EXIT" ] && exit 1
source "$PIA_CONFIG"
# if [ -z "$TOK" ] && ([ -z "$PIA_USERNAME" ] || [ -z "$PASS" ])
if ! [ -r "$CONFIG" ]
then
echo "Cannot read '$CONFIG', generating a default one"
if [ -z "$PIA_USERNAME" ]
then
read -p "Please enter your privateinternetaccess.com username: " PIA_USERNAME
fi
cat <<ENDCONFIG > "$CONFIG"
# your privateinternetaccess.com username (not needed if you already have an auth token)
PIA_USERNAME="$PIA_USERNAME"
# your desired endpoint location
LOC="$LOC"
# the name of the network interface (default: pia)
# PIA_INTERFACE="$PIA_INTERFACE"
# wireguard client-side private key (new key generated every invocation if not specified)
CLIENT_PRIVATE_KEY="$CLIENT_PRIVATE_KEY"
# if PORTFORWARD is set, pia-wg will only connect to port-forward capable servers, and will invoke pia-portforward.sh after connection
# PORTFORWARD="literally anything"
# If you have an existing routing table that only contains routes for hardware interfaces, specify it here
# this will allow pia-wg to hop endpoints without requiring you to disconnect first
# HARDWARE_ROUTE_TABLE="hardlinks"
# If you have daemons that you want to force to only use the VPN and already have a routing table for this purpose, specify it here
# pia-wg will add a default route via the PIA VPN link to that table for you
# VPNONLY_ROUTE_TABLE="vpnonly"
ENDCONFIG
echo "Config saved"
fi
# fetch data-new.json if missing
if ! [ -r "$DATAFILE_NEW" ]
then
echo "Fetching new generation server list from PIA"
curl --max-time 15 'https://serverlist.piaservers.net/vpninfo/servers/new' -o "$DATAFILE_NEW.temp" || exit 1
if [ "$(jq '.regions | map_values(select(.servers.wg)) | keys' "$DATAFILE_NEW.temp" 2>/dev/null | wc -l)" -le 30 ]
then
echo "Bad serverlist retrieved to $DATAFILE_NEW.temp, exiting"
echo "You can try again if there was a transient error"
exit 1
else
jq -cM '.' "$DATAFILE_NEW.temp" > "$DATAFILE_NEW" 2>/dev/null
fi
fi
if ! [ -r "$PIA_CERT" ]
then
echo "Fetching PIA self-signed cert from github"
curl --max-time 15 'https://raw.githubusercontent.com/pia-foss/desktop/master/daemon/res/ca/rsa_4096.crt' > "$PIA_CERT" || exit 1
fi
if [ -n "$OPT_RECONNECT" ]
then
rm "$CONNCACHE" "$REMOTEINFO"
fi
if [ -r "$CONNCACHE" ]
then
WG_NAME="$(jq -r ".name" "$CONNCACHE")"
WG_DNS="$(jq -r ".dns" "$CONNCACHE")"
WG_HOST="$(jq -r ".servers.wg[0].ip" "$CONNCACHE")"
WG_CN="$(jq -r ".servers.wg[0].cn" "$CONNCACHE")"
WG_PORT="$(jq -r '.groups.wg[0].ports[]' "$DATAFILE_NEW" | sort -r | head -n1)"
WG_SN="$(cut -d. -f1 <<< "$WG_DNS")"
fi
if [ -z "$WG_HOST" ] || [ -z "$WG_CN" ] || [ -z "$WG_PORT" ]
then
if [ "$(jq -r ".regions | .[] | select(.id == \"$LOC\")" "$DATAFILE_NEW")" == "" ]
then
LOC=$(jq -r '.regions | .[] | select(.id | test("^'"$LOC"'")) '${PORTFORWARD:+'| select(.port_forward) '}'| .id' "$DATAFILE_NEW" | shuf -n 1)
fi
if [ "$(jq -r ".regions | .[] | select(.id == \"$LOC\")" "$DATAFILE_NEW")" == "" ]
then
echo "Location $LOC not found!"
echo "Options are:"
# jq '.regions | .[] | .id' "$DATAFILE_NEW" | sort | sed -e 's/^/ * /'
(
echo "${BOLD}Location${TAB}Region${TAB}Port Forward${TAB}Geolocated${NORMAL}"
echo "----------------${TAB}------------------${TAB}------------${TAB}----------"
jq -r '.regions | .[] | '${PORTFORWARD:+'| select(.port_forward)'}' [.id, .name, .port_forward, .geo] | "'$'\e''[1m\(.[0])'$'\e''[0m\t\(.[1])\t\(.[2])\t\(.[3])"' "$DATAFILE_NEW"
) | column -t -s "${TAB}"
echo "${PORTFORWARD:+'Note: only port-forwarding regions displayed'}"
echo "Please edit $CONFIG and change your desired location, then try again"
exit 1
fi
jq -r ".regions | .[] | select(.id == \"$LOC\")" "$DATAFILE_NEW" > "$CONNCACHE"
WG_NAME="$(jq -r ".name" "$CONNCACHE")"
WG_DNS="$(jq -r ".dns" "$CONNCACHE")"
WG_HOST="$(jq -r ".servers.wg[0].ip" "$CONNCACHE")"
WG_CN="$(jq -r ".servers.wg[0].cn" "$CONNCACHE")"
WG_PORT="$(jq -r '.groups.wg[0].ports[]' "$DATAFILE_NEW" | sort -r | head -n1)"
WG_SN="$(cut -d. -f1 <<< "$WG_DNS")"
fi
if [ -z "$WG_HOST$WG_PORT" ]; then
echo "no wg region, exiting"
exit 1
fi
if ! [ -r "$REMOTEINFO" ]
then
if [ -z "$TOK" ]
then
if [ -z "$PIA_UESRNAME" ] || [ -z "$PASS" ]
then
echo "A new auth token is required."
fi
if [ -z "$PIA_USERNAME" ]
then
read -p "Please enter your privateinternetaccess.com username: " PIA_USERNAME
[ -z "$PIA_USERNAME" ] && exit 1
fi
if [ -z "$PASS" ]
then
echo "Your password will NOT be saved."
read -p "Please enter your privateinternetaccess.com password for $PIA_USERNAME: " -s PASS
[ -z "$PASS" ] && exit 1
fi
TOK=$(curl -X POST \
-H "Content-Type: application/json" \
-d "{\"username\":\"$PIA_USERNAME\",\"password\":\"$PASS\"}" \
"https://www.privateinternetaccess.com/api/client/v2/token" | jq -r '.token')
# echo "got token: $TOK"
if [ -z "$TOK" ]; then
echo "Failed to authenticate with privateinternetaccess"
echo "Check your user/pass and try again"
exit 1
fi
touch "$TOKENFILE"
chmod 600 "$TOKENFILE"
echo "$TOK" > "$TOKENFILE"
echo "Functional DNS is no longer required."
echo "If you're setting up in a region with heavy internet restrictions, you can disable your alternate VPN or connection method now"
fi
echo "Registering public key with ${BOLD}$WG_NAME $WG_HOST${NORMAL}"
[ "$EUID" -eq 0 ] && [ -z "$OPT_CONFIGONLY" ] && ip rule add to "$WG_HOST" lookup china pref 10
if ! curl -GsS \
--max-time 5 \
--data-urlencode "pubkey=$CLIENT_PUBLIC_KEY" \
--data-urlencode "pt=$TOK" \
--cacert "$PIA_CERT" \
--resolve "$WG_CN:$WG_PORT:$WG_HOST" \
"https://$WG_CN:$WG_PORT/addKey" > "$REMOTEINFO.temp"
then
echo "Registering with $WG_CN failed, trying $WG_DNS"
# fall back to trying DNS certificate if CN fails
# /u/dean_oz reported that this works better for them at https://www.reddit.com/r/PrivateInternetAccess/comments/h9y4da/is_there_any_way_to_generate_wireguard_config/fyfqjf7/
# in testing I find that sometimes one works, sometimes the other works
if ! curl -GsS \
--max-time 5 \
--data-urlencode "pubkey=$CLIENT_PUBLIC_KEY" \
--data-urlencode "pt=$TOK" \
--cacert "$PIA_CERT" \
--resolve "$WG_DNS:$WG_PORT:$WG_HOST" \
"https://$WG_DNS:$WG_PORT/addKey" > "$REMOTEINFO.temp"
then
echo "Failed to register key with $WG_SN ($WG_HOST)"
if ! [ -e "/sys/class/net/$PIA_INTERFACE" ]
then
echo "If you're trying to change hosts because your link has stopped working,"
echo " you may need to ${BOLD}ip link del dev $PIA_INTERFACE${NORMAL} and try this script again"
fi
exit 1
fi
fi
if [ "$(jq -r .status "$REMOTEINFO.temp")" != "OK" ]
then
echo "WG key registration failed - bad token?"
echo "If you see an auth error, consider deleting $TOKENFILE and getting a new token"
exit 1
fi
mv "$REMOTEINFO.temp" \
"$REMOTEINFO"
fi
PEER_IP="$(jq -r .peer_ip "$REMOTEINFO")"
SERVER_PUBLIC_KEY="$(jq -r .server_key "$REMOTEINFO")"
SERVER_IP="$(jq -r .server_ip "$REMOTEINFO")"
SERVER_PORT="$(jq -r .server_port "$REMOTEINFO")"
SERVER_VIP="$(jq -r .server_vip "$REMOTEINFO")"
if [ -n "$OPT_CONFIGONLY" ]
then
cat > "$WGCONF" <<ENDWG
[Interface]
PrivateKey = $CLIENT_PRIVATE_KEY
Address = $PEER_IP
DNS = $(jq -r '.dns_servers[0:2]' "$REMOTEINFO" | grep ^\ | cut -d\" -f2 | xargs echo | sed -e 's/ /,/g')
[Peer]
PublicKey = $SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = $SERVER_IP:$SERVER_PORT
ENDWG
echo
echo "$WGCONF generated"
echo
if which qrencode &>/dev/null
then
qrencode -t ansiutf8 < "$WGCONF"
fi
echo
cat "$WGCONF"
exit 0
fi
if ! ip route show table $HARDWARE_ROUTE_TABLE 2>/dev/null | grep -q .
then
ROUTES_ADD=$(
for IF in $(ip link show | grep -B1 'link/ether' | grep '^[0-9]' | cut -d: -f2)
do
ip route show | grep "dev $IF" | sed -e 's/linkdown//' | sed -e "s/^/ip route add table $HARDWARE_ROUTE_TABLE /"
done
)
if [ "$EUID" -eq 0 ]
then
echo "Build a routing table with only hardware links to stop wireguard packets going back through the VPN:"
echo sudo sh '<<<' "$ROUTES_ADD"
sudo sh <<< "$ROUTES_ADD"
else
sh <<< "$ROUTES_ADD"
fi
echo "Table $HARDWARE_ROUTE_TABLE (hardware network links) now contains:"
ip route show table $HARDWARE_ROUTE_TABLE | sed -e "s/^/${TAB}/"
echo
echo "${BOLD}*** PLEASE NOTE: if this table isn't updated by your network post-connect hooks, your connection cannot remain up if your network links change${NORMAL}"
fi
# echo "Bringing up wireguard interface $PIA_INTERFACE... "
if [ "$EUID" -eq 0 ]
then
# scratch current config if any
# put new settings into existing interface instead of teardown/re-up to prevent leaks
if ip link list "$PIA_INTERFACE" > /dev/null
then
echo "Updating existing interface '$PIA_INTERFACE'"
OLD_PEER_IP="$(ip -j addr show dev pia | jq -r '.[].addr_info[].local')"
OLD_KEY="$(echo $(wg showconf "$PIA_INTERFACE" | grep ^PublicKey | cut -d= -f2-))"
OLD_ENDPOINT="$(wg show "$PIA_INTERFACE" endpoints | grep "$OLD_KEY" | cut "-d${TAB}" -f2 | cut -d: -f1)"
# Note: unnecessary if Table != off above, but doesn't hurt.
# ensure we don't get a packet storm loop
ip rule add fwmark 51820 lookup $HARDWARE_ROUTE_TABLE pref 10
if [ "$OLD_KEY" != "$SERVER_PUBLIC_KEY" ]
then
echo " [Change Peer from $OLD_KEY to $SERVER_PUBLIC_KEY]"
wg set "$PIA_INTERFACE" fwmark 51820 private-key <(echo "$CLIENT_PRIVATE_KEY") peer "$SERVER_PUBLIC_KEY" endpoint "$SERVER_IP:$SERVER_PORT" allowed-ips "0.0.0.0/0,::/0" || exit 1
# remove old key
wg set "$PIA_INTERFACE" peer "$OLD_KEY" remove
fi
if [ "$PEER_IP" != "$OLD_PEER_IP/32" ]
then
echo " [Change $PIA_INTERFACE ipaddr from $OLD_PEER_IP to $PEER_IP]"
# update link ip address in case
ip addr replace "$PEER_IP" dev "$PIA_INTERFACE"
ip addr del "$OLD_PEER_IP/32" dev "$PIA_INTERFACE"
# remove old route
ip rule del to "$OLD_PEER_IP" lookup $HARDWARE_ROUTE_TABLE 2>/dev/null
fi
# Note: only if Table = off in wireguard config file above
ip route add default dev "$PIA_INTERFACE"
# Specific to my setup
ip route add default table $VPNONLY_ROUTE_TABLE dev "$PIA_INTERFACE"
else
echo "Bringing up interface '$PIA_INTERFACE'"
# Note: unnecessary if Table != off above, but doesn't hurt.
ip rule add fwmark 51820 lookup $HARDWARE_ROUTE_TABLE pref 10
# bring up wireguard interface
ip link add "$PIA_INTERFACE" type wireguard || exit 1
ip link set dev "$PIA_INTERFACE" up || exit 1
wg set "$PIA_INTERFACE" fwmark 51820 private-key <(echo "$CLIENT_PRIVATE_KEY") peer "$SERVER_PUBLIC_KEY" endpoint "$SERVER_IP:$SERVER_PORT" allowed-ips "0.0.0.0/0,::/0" || exit 1
ip addr replace "$PEER_IP" dev "$PIA_INTERFACE" || exit 1
# Note: only if Table = off in wireguard config file above
ip route add default dev "$PIA_INTERFACE"
# Specific to my setup
ip route add default table $VPNONLY_ROUTE_TABLE dev "$PIA_INTERFACE"
fi
else
echo ip rule add fwmark 51820 lookup $HARDWARE_ROUTE_TABLE pref 10
sudo ip rule add fwmark 51820 lookup $HARDWARE_ROUTE_TABLE pref 10
if ! ip link list "$PIA_INTERFACE" > /dev/null
then
echo ip link add "$PIA_INTERFACE" type wireguard
sudo ip link add "$PIA_INTERFACE" type wireguard
fi
echo wg set "$PIA_INTERFACE" fwmark 51820 private-key "$CLIENT_PRIVATE_KEY" peer "$SERVER_PUBLIC_KEY" endpoint "$SERVER_IP:$SERVER_PORT" allowed-ips "0.0.0.0/0,::/0"
sudo wg set "$PIA_INTERFACE" fwmark 51820 private-key <(echo "$CLIENT_PRIVATE_KEY") peer "$SERVER_PUBLIC_KEY" endpoint "$SERVER_IP:$SERVER_PORT" allowed-ips "0.0.0.0/0,::/0"
echo ip addr replace "$PEER_IP" dev "$PIA_INTERFACE"
sudo ip addr replace "$PEER_IP" dev "$PIA_INTERFACE"
if ip link list $PIA_INTERFACE > /dev/null
then
OLD_PEER_IP="$(ip -j addr show dev pia | jq '.[].addr_info[].local')"
OLD_KEY="$(echo $(wg showconf "$PIA_INTERFACE" | grep ^PublicKey | cut -d= -f2))"
OLD_ENDPOINT="$(wg show "$PIA_INTERFACE" endpoints | grep "$OLD_KEY" | cut "-d${TAB}" -f2 | cut -d: -f1)"
echo wg set "$PIA_INTERFACE" peer "$OLD_KEY" remove
sudo wg set "$PIA_INTERFACE" peer "$OLD_KEY" remove
fi
echo ip route add default dev "$PIA_INTERFACE"
sudo ip route add default dev "$PIA_INTERFACE"
fi
echo "PIA Wireguard '$PIA_INTERFACE' configured successfully"
TRIES=0
echo -n "Waiting for connection to stabilise..."
while ! ping -n -c1 -w 5 -s 1280 -I "$PIA_INTERFACE" "$SERVER_VIP" &>/dev/null
do
echo -n "."
TRIES=$(( $TRIES + 1 ))
if [[ $TRIES -ge 20 ]]
then
echo "Connection failed to stabilise, try again"
exit 1
fi
sleep 0.5 # so we can catch ctrl+c
done
echo " OK"
if find "$DATAFILE_NEW" -mtime -3 -exec false {} +
then
echo "PIA endpoint list is stale, Fetching new generation wireguard server list"
echo curl --max-time 15 --interface "$PIA_INTERFACE" --CAcert "$PIA_CERT" --resolve "$WG_CN:443:10.0.0.1" "https://$WG_CN:443/vpninfo/servers/v4"
# curl 'https://serverlist.piaservers.net/vpninfo/servers/new' > "$DATAFILE_NEW.temp" || exit 1
curl --max-time 15 --interface "$PIA_INTERFACE" --CAcert "$PIA_CERT" --resolve "$WG_CN:443:10.0.0.1" "https://$WG_CN:443/vpninfo/servers/v4" > "$DATAFILE_NEW.temp" ||
curl --max-time 15 'https://serverlist.piaservers.net/vpninfo/servers/new' > "$DATAFILE_NEW.temp" ||
exit 0
if [ "$(jq '.regions | map_values(select(.servers.wg)) | keys' "$DATAFILE_NEW.temp" 2>/dev/null | wc -l)" -le 30 ]
then
echo "Bad serverlist retrieved to $DATAFILE_NEW.temp, exiting"
echo "You can try again if there was a transient error"
exit 1
else
jq -cM '.' "$DATAFILE_NEW.temp" > "$DATAFILE_NEW" 2>/dev/null
fi
fi
if [ -n "$PORTFORWARD" ]
then
echo "Requesting forwarded port..."
if which pia-portforward.sh &>/dev/null
then
pia-portforward.sh
else
if [ -r "${0%/*}/pia-portforward.sh" ]
then
"${0%/*}/pia-portforward.sh"
else
echo "pia-portforward.sh couldn't be found!"
exit 1
fi
fi
echo "Note: pia-portforward.sh should be called every ~5 minutes to maintain your forward."
echo "You could try:"
echo " while sleep 5m; do pia-portforward.sh; done"
echo "or alternately add a cronjob with crontab -e"
fi
exit 0
@tomangert
Copy link

pia-wg.sh

'ip -j' is a recent enhancement. Ubuntu 16.04 LTS for example does not have it.

If you replace:

OLD_PEER_IP="$(ip -j addr show dev pia | jq -r '.[].addr_info[].local')"

with something like

OLD_PEER_IP="$(ifconfig pia | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}')"

in 2 places - then the script works.

Thanks!

@triffid
Copy link
Author

triffid commented Sep 4, 2020

Thanks for your comment @tomangert!

Your information helps everyone who's still using Ubuntu 16 :)

I'm sorry to say that I'm not going to alter a currently experimental and unsupported script to support OSes that are older than my aged computer - especially when it's still full of stuff that's only applicable to my own personal setup.

As much as I'd like PIA to at least suggest this as an option, I'm happy to very slowly work my way there.

Feel free to post an update any time I break future support for Ubuntu 16, so other users can reap the benefits of your careful attention!

@qdm12
Copy link

qdm12 commented Sep 12, 2020

Thanks for those scripts. I'm the author of a Docker container to tunnel to PIA and people want port forwarding with the next gen (v4) PIA servers. As it's written in Go, I would need to dig in your script and adapt it in Go so I thought I'd ask you a quick question before digging 🕳️ 🐰 For now it's still using openvpn (not wireguard yet), do you think a port forwarded could still be obtained? Thanks!! 👍

@triffid
Copy link
Author

triffid commented Sep 13, 2020

@qdm12 I got the port forward method from Threarah's PIA Docker image - I think it works with OpenVPN on next-gen, but haven't tried since OpenVPN doesn't work for me.

@svanhoutte
Copy link

Thanks for the script, i m trying to make it work and found the $USER is actually being used by the system. Then the variable is not null and there is no ask for the login. Should it be changed to something else like $USER_PIA ? or i m missing something ?
Thanks !!

@triffid
Copy link
Author

triffid commented Sep 14, 2020

@svanhoutte hmm that didn't come up in my testing and it's overwritten if you put it in your config.. I guess I can change it for first run though

@svanhoutte
Copy link

@triffid, thanks i found that also the password is not protected if special characters like $ is being used. Then the pass is not past in full in the config file.

@triffid
Copy link
Author

triffid commented Sep 14, 2020

@svanhoutte I just pushed a change that should hopefully retain the pass properly when it's saved in your config, just by wrapping it in single quotes rather than double.

If that doesn't protect it well enough, feel free to suggest a better method :)

@svanhoutte
Copy link

@triffid, thanks for the changes, i faced other issues and made few changes :

  • the token is coded as "token":"token line" when the registration of the gateway happen it failed, i had to isolate the token with TOK=$(echo $TOK | awk -F '"' '{print $4}').
  • I add to create the china routing table, maybe a test at the initiation of the script to check if the table exist and create the table.

@FrAllard
Copy link

Hi thanks for the script! I'm trying to make it work and I'm wondering what is this "china" routing table thing you refer to in the script. It throw an error and I'm not sure how to "create the china routing table" as svanhoutte did.

@triffid
Copy link
Author

triffid commented Sep 19, 2020

It's a table that only contains routes for my physical interfaces - ie ethernet and wifi - but not virtual links like wireguard, etc.

You can probably just remove the ip rules that refer to it as it's specific to my setup, and there's a separate ip rule matching fwmark to prevent a packet storm loop.

The only problem with removing it is that if your endpoint stops working for some reason, you can't re-run the script without deleting the default route over wireguard first, as it'll try to post to the new endpoint over the existing connection.

I probably should move those to a separate hook script at some point, would make things easier for others trying it out

@JeffreyShran
Copy link

This might help as an alternative: https://github.com/pia-foss/manual-connections

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment