This commit is contained in:
2026-03-03 00:59:45 -05:00
parent 2955b980a5
commit e14ff24d8a
3 changed files with 48 additions and 15 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
ca.rsa.4096.crt
.ca.rsa.4096.crt

View File

@@ -1,5 +1,5 @@
# piawg # piawg
POSIX shell script for OPNsense that manages Private Internet Access next-gen Shell script for OPNsense that manages Private Internet Access next-gen
WireGuard tunnels using credentials and configuration stored in OpenBao. WireGuard tunnels using credentials and configuration stored in OpenBao.
Licensed under 0BSD. Licensed under 0BSD.

View File

@@ -2,6 +2,9 @@
# SPDX-License-Identifier: 0BSD # SPDX-License-Identifier: 0BSD
# SPDX-FileCopyrightText: 2026 Kris Lamoureux <kris@lamoureux.io> # SPDX-FileCopyrightText: 2026 Kris Lamoureux <kris@lamoureux.io>
# Allow local variable scoping, therefore not strictly POSIX
# shellcheck disable=SC3043
err() { err() {
printf '[ERROR]: %s\n' "$1" >&2 printf '[ERROR]: %s\n' "$1" >&2
exit 1 exit 1
@@ -14,6 +17,11 @@ check_http() {
esac esac
} }
# Check for plausible looking PIA token
check_token() {
printf '%s\n' "$1" | grep -q '^[0-9A-Fa-f]\{128\}$'
}
bao_curl() { bao_curl() {
curl -sS --connect-timeout 5 --max-time 20 --retry 5 --retry-delay 2 \ curl -sS --connect-timeout 5 --max-time 20 --retry 5 --retry-delay 2 \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
@@ -22,43 +30,51 @@ bao_curl() {
"$@" "$@"
} }
# Fetch latest token in OpenBao # Fetch latest PIA token in OpenBao
get_token() { get_token() {
local pia_token_reply
if ! pia_token_reply=$(bao_curl \ if ! pia_token_reply=$(bao_curl \
"$BAO_ADDR/v1/$BAO_KV_MOUNT/data/$BAO_PATH_TOKEN"); then "$BAO_ADDR/v1/$BAO_KV_MOUNT/data/$BAO_PATH_TOKEN"); then
err "Failed to fetch PIA token from '$BAO_ADDR'" err "Failed to fetch PIA token from '$BAO_ADDR'"
fi fi
printf '%s' "$pia_token_reply" printf '%s' "$pia_token_reply"
unset pia_token_reply
} }
# Get a new PIA token and store # Get a new PIA token and store
renew_token() { renew_token() {
login_response="$(bao_curl "$BAO_ADDR/v1/$BAO_KV_MOUNT/data/$BAO_PATH_LOGIN")" local login_response
local update_response
local token_response
local http_code
local pia_user
local pia_pass
local pia_token
login_response="$(bao_curl \
"$BAO_ADDR/v1/$BAO_KV_MOUNT/data/$BAO_PATH_LOGIN"
)"
http_code="$(printf '%s' "$login_response" | tail -1)" http_code="$(printf '%s' "$login_response" | tail -1)"
if ! check_http "$http_code"; then if ! check_http "$http_code"; then
err "Failed to get PIA login details (HTTP $http_code)" err "Failed to get PIA login details (HTTP $http_code)"
fi fi
unset http_code
login_response="$(printf '%s' "$login_response" | sed '$d')" login_response="$(printf '%s' "$login_response" | sed '$d')"
pia_user="$(printf '%s' "$login_response" | jq -r '.data.data.username')" pia_user="$(printf '%s' "$login_response" | jq -r '.data.data.username')"
pia_pass="$(printf '%s' "$login_response" | jq -r '.data.data.password')" pia_pass="$(printf '%s' "$login_response" | jq -r '.data.data.password')"
unset login_response unset login_response
if ! token_reply="$(curl -s -X POST "$PIA_API" \ if ! token_response="$(curl -s -X POST "$PIA_API" \
-F "username=$pia_user" \ -F "username=$pia_user" \
-F "password=$pia_pass")"; then -F "password=$pia_pass")"; then
err "Failed to get a new PIA token" err "Failed to get a new PIA token"
fi fi
unset pia_pass unset pia_pass
unset pia_user unset pia_user
pia_token="$(echo "$token_reply" | jq -r .token)" pia_token="$(echo "$token_response" | jq -r .token)"
unset token_reply unset token_response
if ! printf '%s' "$pia_token" | grep -Eq '^[0-9A-Fa-f]{128}$'; then check_token "$pia_token" || err "Invalid token found during renewal attempt"
err "Invalid token found during renewal attempt" if ! update_response="$(bao_curl -X POST -d \
fi "$(jq -n --arg t "$pia_token" '{data:{token:$t}}')" \
if ! update_response="$(bao_curl -X POST -d "$(jq -n --arg t "$pia_token" '{data:{token:$t}}')" \
"$BAO_ADDR/v1/$BAO_KV_MOUNT/data/$BAO_PATH_TOKEN")"; then "$BAO_ADDR/v1/$BAO_KV_MOUNT/data/$BAO_PATH_TOKEN")"; then
err "Failed to save PIA token to '$BAO_ADDR'" err "Failed to save PIA token to '$BAO_ADDR'"
fi fi
@@ -70,7 +86,7 @@ renew_token() {
} }
# Check for required external commands # Check for required external commands
for rbin in curl jq; do for rbin in curl jq openssl; do
command -v "$rbin" >/dev/null 2>&1 || command -v "$rbin" >/dev/null 2>&1 ||
err "Required binary '$rbin' not found" err "Required binary '$rbin' not found"
done done
@@ -100,6 +116,9 @@ fi
# Overridable defaults # Overridable defaults
: "${PIA_API:=https://www.privateinternetaccess.com/api/client/v2/token}" : "${PIA_API:=https://www.privateinternetaccess.com/api/client/v2/token}"
: "${PIA_CRT:=https://www.privateinternetaccess.com/openvpn/ca.rsa.4096.crt}"
: "${PIA_HASH:=1fd25658456eab3041fba77ccd398ab8\
124edcc1b8b2fc1d55fdf6b1bbfc9d70}"
: "${BAO_AUTH_PATH:=approle}" : "${BAO_AUTH_PATH:=approle}"
: "${BAO_KV_MOUNT:=kv}" : "${BAO_KV_MOUNT:=kv}"
: "${BAO_PATH_LOGIN:=piawg/creds/login}" : "${BAO_PATH_LOGIN:=piawg/creds/login}"
@@ -143,5 +162,17 @@ elif ! check_http "$http_code"; then
err "Failed to get PIA token from '$BAO_ADDR' (HTTP $http_code)" err "Failed to get PIA token from '$BAO_ADDR' (HTTP $http_code)"
fi fi
printf '%s\n' "$get_token_reply" pia_token="$(printf '%s' "$get_token_reply" | jq -r .data.data.token)"
exit 0 check_token "$pia_token" || err "Failed to get valid PIA token"
printf "[debug] %s: '%s'\n" "pia_token" "$pia_token"
# Download PIA RSA CA certificate
if [ ! -f ./ca.rsa.4096.crt ]; then
[ -f ./.ca.rsa.4096.crt ] && rm ./.ca.rsa.4096.crt
curl -sS --connect-timeout 5 --max-time 20 --retry 5 --retry-delay 2 \
-o ./.ca.rsa.4096.crt "$PIA_CRT"
pia_file_hash="$(openssl x509 -in ./.ca.rsa.4096.crt -outform DER |
openssl dgst -sha256 -r | awk '{print $1}')"
[ "$pia_file_hash" != "$PIA_HASH" ] && err "PIA CA fingerprint mismatch"
mv ./.ca.rsa.4096.crt ./ca.rsa.4096.crt
fi