#!/usr/bin/env bash set -euo pipefail # Vigilar — VAPID key generator for Web Push notifications # Uses py-vapid (from the vigilar venv) or falls back to openssl. VENV_DIR="/opt/vigilar/venv" SECRETS_DIR="/etc/vigilar/secrets" PRIVATE_KEY_FILE="${SECRETS_DIR}/vapid_private.pem" PUBLIC_KEY_FILE="${SECRETS_DIR}/vapid_public.txt" VIGILAR_GROUP="vigilar" info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; } ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; } fail() { printf '\033[1;31m[FAIL]\033[0m %s\n' "$*" >&2; exit 1; } generate_with_py_vapid() { info "Generating VAPID keys with py-vapid" local python="${VENV_DIR}/bin/python" "$python" -c " from py_vapid import Vapid import base64 v = Vapid() v.generate_keys() v.save_key('${PRIVATE_KEY_FILE}') raw = v.public_key.public_bytes( encoding=__import__('cryptography.hazmat.primitives.serialization', fromlist=['Encoding']).Encoding.X962, format=__import__('cryptography.hazmat.primitives.serialization', fromlist=['PublicFormat']).PublicFormat.UncompressedPoint, ) pub_b64 = base64.urlsafe_b64encode(raw).rstrip(b'=').decode() print(pub_b64) " | tee "$PUBLIC_KEY_FILE" } generate_with_openssl() { info "Generating VAPID keys with openssl" # Generate ECDSA P-256 private key in PEM format openssl ecparam -name prime256v1 -genkey -noout -out "$PRIVATE_KEY_FILE" 2>/dev/null # Extract the public key in uncompressed point format, base64url-encode it local pub_b64 pub_b64="$(openssl ec -in "$PRIVATE_KEY_FILE" -pubout -outform DER 2>/dev/null \ | tail -c 65 \ | base64 -w 0 \ | tr '+/' '-_' \ | tr -d '=')" echo "$pub_b64" | tee "$PUBLIC_KEY_FILE" } main() { info "=== Vigilar VAPID Key Generator ===" sudo mkdir -p "$SECRETS_DIR" if [[ -f "$PRIVATE_KEY_FILE" ]]; then warn "VAPID private key already exists at ${PRIVATE_KEY_FILE}" read -rp "Overwrite? [y/N] " answer if [[ ! "$answer" =~ ^[Yy]$ ]]; then info "Keeping existing key" if [[ -f "$PUBLIC_KEY_FILE" ]]; then info "Public key (base64url):" cat "$PUBLIC_KEY_FILE" fi exit 0 fi fi local public_key if [[ -x "${VENV_DIR}/bin/python" ]] && "${VENV_DIR}/bin/python" -c "import py_vapid" 2>/dev/null; then public_key="$(generate_with_py_vapid)" elif command -v openssl &>/dev/null; then public_key="$(generate_with_openssl)" else fail "Neither py-vapid nor openssl found." fi # Secure the private key sudo chown root:root "$PRIVATE_KEY_FILE" sudo chmod 0600 "$PRIVATE_KEY_FILE" # Public key file is not sensitive sudo chown root:"${VIGILAR_GROUP}" "$PUBLIC_KEY_FILE" sudo chmod 0644 "$PUBLIC_KEY_FILE" echo ok "VAPID keys generated" info " Private key: ${PRIVATE_KEY_FILE}" info " Public key (base64url):" echo " ${public_key}" echo info "Use the public key above in your web app's push subscription config." } main "$@"