Skip to content

Instantly share code, notes, and snippets.

@trickapm
Last active May 17, 2022 20:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trickapm/deaeffea9a45e1a6e28468b52f726c42 to your computer and use it in GitHub Desktop.
Save trickapm/deaeffea9a45e1a6e28468b52f726c42 to your computer and use it in GitHub Desktop.
Wireguard helper script for SurfShark on OpenWRT
#!/bin/sh
# For more info see:
# https://forum.openwrt.org/t/create-surfshark-wireguard-connection-on-openwrt-easily/
#
config_file="config.json"
read_config() {
conf_json=$(cat "$config_file")
config_folder="$(echo "$conf_json" | jq -r '.config_folder')"
username="$(echo "$conf_json" | jq -r '.username')"
password="$(echo "$conf_json" | jq -r '.password')"
unset conf_json
baseurl="https://api.surfshark.com"
token_file="${config_folder}/token.json"
servers_file="${config_folder}/surfshark_servers.json"
wg_keys="${config_folder}/wg.json"
output_conf_folder="${config_folder}/conf"
}
do_login () {
rc=1
if [ -f "$token_file" ]; then
echo "Token file \"$token_file\" exists, skipping login"
rc=0
else
echo "Logging in..."
tmpfile=$(mktemp /tmp/wg-curl-res.XXXXXX)
url="$baseurl/v1/auth/login"
data="{\"username\":\"$username\", \"password\":\"$password\"}"
http_status=$(curl -o $tmpfile -s -w "%{http_code}" -d "$data" -H 'Content-Type: application/json' -X POST $url)
if [ $http_status -eq 200 ]; then
cp $tmpfile $token_file
echo " HTTP status OK"
rc=0
elif [ $http_status -eq 429 ]; then
echo " HTTP status $http_status (Blocked! Too many requests)"
else
echo " HTTP status $http_status (Failed!)"
fi
rm $tmpfile
fi
if [ "$rc" -eq 0 ]; then
token="$(jq -r '.token' "$token_file")"
renewToken="$(jq -r '.renewToken' "$token_file")"
fi
return $rc
}
get_servers() {
echo "Retrieving servers list..."
tmpfile=$(mktemp /tmp/surfshark-wg-servers.XXXXXX)
url="$baseurl/v4/server/clusters/generic?countryCode="
http_status=$(curl -o $tmpfile -s -w "%{http_code}" -H 'Content-Type: application/json' $url)
rc=1
if [ $http_status -eq 200 ]; then
echo " HTTP status OK ($(jq '. | length' "$tmpfile") servers downloaded)"
echo -n " Selecting suitable servers..."
tmpfile2=$(mktemp /tmp/surfshark-wg-servers.XXXXXX)
jq '.[] | select(.tags as $t | ["p2p", "physical"] | index($t))' "$tmpfile" | jq -s '.' > "$tmpfile2"
echo " ($(jq '. | length' "$tmpfile2") servers selected)"
if [ -f "$servers_file" ]; then
echo " Servers list \"$servers_file\" already exists"
changes=$(diff "$servers_file" $tmpfile2)
if [ -z "$changes" ]; then
echo " No changes"
rm $tmpfile2
else
echo " Servers changed! Updating servers file"
mv $tmpfile2 "$servers_file"
rc=0
fi
else
mv $tmpfile2 "$servers_file"
rc=0
fi
else
echo " HTTP status $http_status (Failed)"
fi
rm $tmpfile
return $rc
}
gen_keys() {
if [ -f "$wg_keys" ]; then
echo "WireGuard keys \"$wg_keys\" already exist"
wg_pub=$(cat $wg_keys | jq -r '.pub')
wg_prv=$(cat $wg_keys | jq -r '.prv')
else
echo "Generating WireGuard keys..."
wg_prv=$(wg genkey)
wg_pub=$(echo $wg_prv | wg pubkey)
echo "{\"pub\":\"$wg_pub\", \"prv\":\"$wg_prv\"}" > $wg_keys
fi
echo " Using public key: $wg_pub"
}
reg_pubkey() {
echo "Registering public key..."
url="$baseurl/v1/account/users/public-keys"
data="{\"pubKey\": \"$wg_pub\"}"
retry=$1
tmpfile="$(mktemp /tmp/wg-curl-res.XXXXXX)"
http_status="$(curl -o "$tmpfile" -s -w "%{http_code}" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "$data" -X POST $url)"
message="$(jq -r '.message' $tmpfile 2>/dev/null)"
if [ $http_status -eq 201 ]; then
echo " OK (expires: $(jq -r '.expiresAt' $tmpfile), id: $(jq -r '.id' $tmpfile))"
elif [ $http_status -eq 401 ]; then
echo " Access denied: $message"
if [ "$message" = "Expired JWT Token" ]; then
echo " Delete $token_file and try again!"
rm "$token_file"
if do_login; then
reg_pubkey 0
return
else
echo " Giving up..."
fi
elif [ "$message" = "JWT Token not found" ]; then
if [ $retry -eq 1 ]; then
echo " Have some coffee and try again!"
sleep 5
reg_pubkey 0
return
else
echo " Giving up..."
fi
fi
elif [ $http_status -eq 409 ]; then
echo " Already registered"
url="$baseurl/v1/account/users/public-keys/validate"
http_status="$(curl -o "$tmpfile" -s -w "%{http_code}" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "$data" -X POST $url)"
if [ $http_status -eq 200 ]; then
expire_date="$(jq -r '.expiresAt' $tmpfile)"
ed="$(date -u -d "$expire_date" -D "%Y-%m-%dT%T" +"%s")"
now="$(date -u +"%s")"
diff=$(($ed - $now))
if [ $diff -eq 604800 -o $((604800 - $diff)) -lt 10 ]; then
echo " Renewed! (expires: $expire_date)"
elif [ $diff > 0 ]; then
echo " Expires on $expire_date)"
else
echo " Warning: key is expired! ($expire_date)"
fi
else
echo " HTTP status $http_status, failed to check key: $(cat $tmpfile)"
fi
else
echo " Failed: HTTP $http_status, $(cat $tmpfile)"
fi
rm $tmpfile
}
gen_client_confs() {
postf=".surfshark.com"
mkdir -p "$output_conf_folder"
servers="$(cat "$servers_file" | jq -c '.[] | [.connectionName, .pubKey]')"
for s in $servers; do
name="$(echo $s | jq -r '.[0]')"
pubkey="$(echo $s | jq -r '.[1]')"
conf="${srv_conf_file_folder}/${name%$postf}.conf"
echo "Generating config for $name..."
cat << EOF > "$conf"
[Interface]
PrivateKey=$wg_priv
Address=1.2.3.4
MTU=1350
[Peer]
PublicKey=o07k/2dsaQkLLSR0dCI/FUd3FLik/F/HBBcOGUkNQGo=
AllowedIPs=172.16.0.36/32
Endpoint=wgs.prod.surfshark.com:51820
PersistentKeepalive=25
[Peer]
PublicKey=$pubkey
AllowedIPs=123.456.7.8
Endpoint=$name:51820
PersistentKeepalive=25
EOF
done
}
echo "Running at $(date)"
read_config
gen_keys
if do_login; then
reg_pubkey 1
else
echo "Not registering public key!"
fi
if [ "$1" == "-g" ]; then
if get_servers; then
gen_client_confs
else
echo "Not generating client configurations!"
fi
fi
echo "Done!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment