From ce9bda8a0b74c73acfb657662864b66c472295d9 Mon Sep 17 00:00:00 2001 From: Zuckerberg Date: Thu, 26 Feb 2026 22:32:23 -0800 Subject: [PATCH] Verify RSA-SHA256 signature on PIA server list response The server list endpoint returns JSON on line 1 with a base64-encoded RSA-SHA256 signature on lines 3+. This was previously ignored. Add verifyServerList() that checks the signature against PIA's public signing key before trusting the data. On failure the service aborts and systemd restarts it. Also bump RestartSec to 5m to avoid hammering PIA servers on repeated failures, and add openssl to container dependencies. --- common/network/pia-vpn/pubkey.pem | 9 +++++++ common/network/pia-vpn/scripts.nix | 32 ++++++++++++++++++------ common/network/pia-vpn/vpn-container.nix | 4 +-- 3 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 common/network/pia-vpn/pubkey.pem diff --git a/common/network/pia-vpn/pubkey.pem b/common/network/pia-vpn/pubkey.pem new file mode 100644 index 0000000..9848559 --- /dev/null +++ b/common/network/pia-vpn/pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzLYHwX5Ug/oUObZ5eH5P +rEwmfj4E/YEfSKLgFSsyRGGsVmmjiXBmSbX2s3xbj/ofuvYtkMkP/VPFHy9E/8ox +Y+cRjPzydxz46LPY7jpEw1NHZjOyTeUero5e1nkLhiQqO/cMVYmUnuVcuFfZyZvc +8Apx5fBrIp2oWpF/G9tpUZfUUJaaHiXDtuYP8o8VhYtyjuUu3h7rkQFoMxvuoOFH +6nkc0VQmBsHvCfq4T9v8gyiBtQRy543leapTBMT34mxVIQ4ReGLPVit/6sNLoGLb +gSnGe9Bk/a5V/5vlqeemWF0hgoRtUxMtU1hFbe7e8tSq1j+mu0SHMyKHiHd+OsmU +IQIDAQAB +-----END PUBLIC KEY----- diff --git a/common/network/pia-vpn/scripts.nix b/common/network/pia-vpn/scripts.nix index e5fabb1..0bbc2bd 100644 --- a/common/network/pia-vpn/scripts.nix +++ b/common/network/pia-vpn/scripts.nix @@ -1,5 +1,6 @@ let caPath = ./ca.rsa.4096.crt; + pubKeyPath = ./pubkey.pem; in # Bash function library for PIA VPN WireGuard operations. @@ -21,6 +22,23 @@ in fi } + # Verify the server list RSA-SHA256 signature (line 1 = JSON, lines 3+ = base64 signature). + verifyServerList() { + local raw=$1 + 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: Server list signature verification failed" >&2 + rm -f "$sig_file" + return 1 + fi + echo "Server list signature verified" + rm -f "$sig_file" + } + fetchPIAToken() { local PIA_USER PIA_PASS resp echo "Reading PIA credentials..." @@ -39,20 +57,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") + verifyServerList "$raw" + 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 +84,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)" } diff --git a/common/network/pia-vpn/vpn-container.nix b/common/network/pia-vpn/vpn-container.nix index ed6b081..4631681 100644 --- a/common/network/pia-vpn/vpn-container.nix +++ b/common/network/pia-vpn/vpn-container.nix @@ -73,7 +73,7 @@ in config = { config, pkgs, lib, ... }: let - scriptPkgs = with pkgs; [ wireguard-tools iproute2 curl jq iptables coreutils ]; + scriptPkgs = with pkgs; [ wireguard-tools iproute2 curl jq iptables coreutils openssl ]; in { imports = allModules; @@ -133,7 +133,7 @@ in serviceConfig = { Type = "simple"; Restart = "always"; - RestartSec = "10s"; + RestartSec = "5m"; RuntimeMaxSec = "30d"; };