Verify RSA-SHA256 signatures on all PIA API responses
All checks were successful
Check Flake / check-flake (push) Successful in 3m18s
All checks were successful
Check Flake / check-flake (push) Successful in 3m18s
Every PIA API response includes a trailing RSA-SHA256 signature (line 1 = JSON, lines 3+ = base64-encoded signature) which was previously ignored entirely. Add verifyPIAResponse() that checks each response against PIA's public signing key before trusting the data. On verification failure the service aborts and systemd restarts it. Also bump RestartSec to 5m to avoid hammering PIA servers on repeated failures.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
let
|
||||
caPath = ./ca.rsa.4096.crt;
|
||||
pubKeyPath = ./pubkey.pem;
|
||||
in
|
||||
|
||||
# Bash function library for PIA VPN WireGuard operations.
|
||||
@@ -21,14 +22,33 @@ in
|
||||
fi
|
||||
}
|
||||
|
||||
# Verify a raw PIA API response (line 1 = JSON, lines 3+ = base64 RSA-SHA256 signature).
|
||||
verifyPIAResponse() {
|
||||
local raw=$1 label=$2
|
||||
local sig_file
|
||||
sig_file=$(mktemp)
|
||||
echo "$raw" | tail -n +3 | base64 -d > "$sig_file"
|
||||
if ! echo -n "$(echo "$raw" | head -n 1 | tr -d '\n')" | \
|
||||
openssl dgst -sha256 -verify "${pubKeyPath}" \
|
||||
-signature "$sig_file"; then
|
||||
echo "ERROR: $label signature verification failed" >&2
|
||||
rm -f "$sig_file"
|
||||
return 1
|
||||
fi
|
||||
echo "$label signature verified"
|
||||
rm -f "$sig_file"
|
||||
}
|
||||
|
||||
fetchPIAToken() {
|
||||
local PIA_USER PIA_PASS resp
|
||||
local PIA_USER PIA_PASS raw resp
|
||||
echo "Reading PIA credentials..."
|
||||
PIA_USER=$(sed '1q;d' /run/agenix/pia-login.conf)
|
||||
PIA_PASS=$(sed '2q;d' /run/agenix/pia-login.conf)
|
||||
echo "Requesting PIA authentication token..."
|
||||
resp=$(curl -s $(proxy_args) -u "$PIA_USER:$PIA_PASS" \
|
||||
raw=$(curl -s $(proxy_args) -u "$PIA_USER:$PIA_PASS" \
|
||||
"https://www.privateinternetaccess.com/gtoken/generateToken")
|
||||
verifyPIAResponse "$raw" "generateToken"
|
||||
resp=$(echo "$raw" | head -n 1)
|
||||
PIA_TOKEN=$(echo "$resp" | jq -r '.token')
|
||||
if [[ -z "$PIA_TOKEN" || "$PIA_TOKEN" == "null" ]]; then
|
||||
echo "ERROR: Failed to fetch PIA token: $resp" >&2
|
||||
@@ -39,20 +59,20 @@ in
|
||||
|
||||
choosePIAServer() {
|
||||
local serverLocation=$1
|
||||
local servers servers_json totalservers serverindex
|
||||
servers=$(mktemp)
|
||||
local raw servers_json totalservers serverindex
|
||||
servers_json=$(mktemp)
|
||||
echo "Fetching PIA server list..."
|
||||
curl -s $(proxy_args) \
|
||||
"https://serverlist.piaservers.net/vpninfo/servers/v6" > "$servers"
|
||||
head -n 1 "$servers" | tr -d '\n' > "$servers_json"
|
||||
raw=$(curl -s $(proxy_args) \
|
||||
"https://serverlist.piaservers.net/vpninfo/servers/v6")
|
||||
verifyPIAResponse "$raw" "serverList"
|
||||
echo "$raw" | head -n 1 | tr -d '\n' > "$servers_json"
|
||||
|
||||
totalservers=$(jq -r \
|
||||
'.regions | .[] | select(.id=="'"$serverLocation"'") | .servers.wg | length' \
|
||||
"$servers_json")
|
||||
if ! [[ "$totalservers" =~ ^[0-9]+$ ]] || [ "$totalservers" -eq 0 ] 2>/dev/null; then
|
||||
echo "ERROR: Location \"$serverLocation\" not found." >&2
|
||||
rm -f "$servers_json" "$servers"
|
||||
rm -f "$servers_json"
|
||||
return 1
|
||||
fi
|
||||
echo "Found $totalservers WireGuard servers in region '$serverLocation'"
|
||||
@@ -66,7 +86,7 @@ in
|
||||
"$servers_json")
|
||||
WG_SERVER_PORT=$(jq -r '.groups.wg | .[0] | .ports | .[0]' "$servers_json")
|
||||
|
||||
rm -f "$servers_json" "$servers"
|
||||
rm -f "$servers_json"
|
||||
echo "Selected server $serverindex/$totalservers: $WG_HOSTNAME ($WG_SERVER_IP:$WG_SERVER_PORT)"
|
||||
}
|
||||
|
||||
@@ -77,14 +97,16 @@ in
|
||||
}
|
||||
|
||||
authorizeKeyWithPIAServer() {
|
||||
local addKeyResponse
|
||||
local raw addKeyResponse
|
||||
echo "Sending addKey request to $WG_HOSTNAME ($WG_SERVER_IP:$WG_SERVER_PORT)..."
|
||||
addKeyResponse=$(curl -s -G $(proxy_args) \
|
||||
raw=$(curl -s -G $(proxy_args) \
|
||||
--connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" \
|
||||
--cacert "${caPath}" \
|
||||
--data-urlencode "pt=$PIA_TOKEN" \
|
||||
--data-urlencode "pubkey=$PUBLIC_KEY" \
|
||||
"https://$WG_HOSTNAME:$WG_SERVER_PORT/addKey")
|
||||
verifyPIAResponse "$raw" "addKey"
|
||||
addKeyResponse=$(echo "$raw" | head -n 1)
|
||||
local status
|
||||
status=$(echo "$addKeyResponse" | jq -r '.status')
|
||||
if [[ "$status" != "OK" ]]; then
|
||||
@@ -158,13 +180,15 @@ in
|
||||
}
|
||||
|
||||
reservePortForward() {
|
||||
local payload_and_signature
|
||||
local raw payload_and_signature
|
||||
echo "Requesting port forward signature from $WG_HOSTNAME..."
|
||||
payload_and_signature=$(curl -s -m 5 \
|
||||
raw=$(curl -s -m 5 \
|
||||
--connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" \
|
||||
--cacert "${caPath}" \
|
||||
-G --data-urlencode "token=$PIA_TOKEN" \
|
||||
"https://$WG_HOSTNAME:19999/getSignature")
|
||||
verifyPIAResponse "$raw" "getSignature"
|
||||
payload_and_signature=$(echo "$raw" | head -n 1)
|
||||
local status
|
||||
status=$(echo "$payload_and_signature" | jq -r '.status')
|
||||
if [[ "$status" != "OK" ]]; then
|
||||
@@ -195,14 +219,16 @@ in
|
||||
}
|
||||
|
||||
refreshPIAPort() {
|
||||
local bindPortResponse
|
||||
local raw bindPortResponse
|
||||
echo "Refreshing port forward binding with $WG_HOSTNAME..."
|
||||
bindPortResponse=$(curl -Gs -m 5 \
|
||||
raw=$(curl -Gs -m 5 \
|
||||
--connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" \
|
||||
--cacert "${caPath}" \
|
||||
--data-urlencode "payload=$PORT_PAYLOAD" \
|
||||
--data-urlencode "signature=$PORT_SIGNATURE" \
|
||||
"https://$WG_HOSTNAME:19999/bindPort")
|
||||
verifyPIAResponse "$raw" "bindPort"
|
||||
bindPortResponse=$(echo "$raw" | head -n 1)
|
||||
echo "bindPort response: $bindPortResponse"
|
||||
}
|
||||
'';
|
||||
|
||||
Reference in New Issue
Block a user