#!/usr/bin/env bash set -eu echo "NexaVPN gateway helper starting" mkdir -p /var/lib/nexavpn IFACE="${NEXAVPN_GATEWAY_INTERFACE:-wg0}" UPLINK_IFACE="${NEXAVPN_UPLINK_INTERFACE:-eth0}" ENABLE_MASQUERADE="${NEXAVPN_ENABLE_MASQUERADE:-true}" GATEWAY_NAME="${NEXAVPN_GATEWAY_NAME:-primary-gateway}" BOOTSTRAP_URL="${NEXAVPN_GATEWAY_BOOTSTRAP_URL:-http://backend:8080/api/v1/gateway-agent/bootstrap}" SYNC_BASE_URL="${NEXAVPN_GATEWAY_SYNC_URL:-http://backend:8080/api/v1/gateway-agent}" GATEWAY_ID_FILE="/var/lib/nexavpn/gateway-id" BACKEND_HOST="${NEXAVPN_BACKEND_HOST:-backend}" if [ -z "${GATEWAY_BOOTSTRAP_TOKEN:-}" ]; then echo "GATEWAY_BOOTSTRAP_TOKEN is required." tail -f /dev/null exit 0 fi if [ -z "${NEXAVPN_GATEWAY_PRIVATE_KEY:-}" ]; then if [ -f /var/lib/nexavpn/gateway-private.key ]; then NEXAVPN_GATEWAY_PRIVATE_KEY="$(cat /var/lib/nexavpn/gateway-private.key)" else wg genkey | tee /var/lib/nexavpn/gateway-private.key >/tmp/nexavpn-gateway-private.key NEXAVPN_GATEWAY_PRIVATE_KEY="$(cat /tmp/nexavpn-gateway-private.key)" rm -f /tmp/nexavpn-gateway-private.key fi fi if [ -z "${NEXAVPN_GATEWAY_ID:-}" ] && [ -f "${GATEWAY_ID_FILE}" ]; then NEXAVPN_GATEWAY_ID="$(cat "${GATEWAY_ID_FILE}")" fi bootstrap_gateway() { GATEWAY_PUBLIC_KEY="$(printf '%s' "${NEXAVPN_GATEWAY_PRIVATE_KEY}" | wg pubkey)" echo "Bootstrapping gateway ${GATEWAY_NAME}" BOOTSTRAP_RESPONSE="$(curl -fsSL \ -H "Content-Type: application/json" \ -H "X-Gateway-Bootstrap-Token: ${GATEWAY_BOOTSTRAP_TOKEN}" \ -d "{\"name\":\"${GATEWAY_NAME}\",\"endpoint\":\"${DEFAULT_GATEWAY_ENDPOINT:-localhost:51820}\",\"public_key\":\"${GATEWAY_PUBLIC_KEY}\",\"listen_port\":51820,\"vpn_cidr\":\"${DEFAULT_VPN_CIDR:-100.96.0.0/24}\",\"dns_servers\":[\"10.20.0.53\"]}" \ "${BOOTSTRAP_URL}")" NEXAVPN_GATEWAY_ID="$(printf '%s' "${BOOTSTRAP_RESPONSE}" | jq -r '.id')" if [ -z "${NEXAVPN_GATEWAY_ID:-}" ] || [ "${NEXAVPN_GATEWAY_ID}" = "null" ]; then echo "Gateway bootstrap did not return an id." return 1 fi printf '%s' "${NEXAVPN_GATEWAY_ID}" > "${GATEWAY_ID_FILE}" } STATE_JSON="/var/lib/nexavpn/sync-bundle.json" WG_CONF="/etc/wireguard/${IFACE}.conf" WG_GENERATED="/var/lib/nexavpn/${IFACE}.generated.conf" NFT_CONF="/var/lib/nexavpn/nftables.generated.conf" mkdir -p /etc/wireguard apply_bundle() { if [ -z "${NEXAVPN_GATEWAY_ID:-}" ]; then bootstrap_gateway || return 1 fi if [ -z "${NEXAVPN_GATEWAY_ID:-}" ] || [ -z "${NEXAVPN_GATEWAY_PRIVATE_KEY:-}" ]; then echo "Gateway sync is not configured yet." return 1 fi SYNC_URL="${SYNC_BASE_URL}/${NEXAVPN_GATEWAY_ID}/sync" echo "Fetching bundle from ${SYNC_URL}" TMP_STATE_JSON="${STATE_JSON}.tmp" rm -f "${TMP_STATE_JSON}" curl -fsSL \ -H "X-Gateway-Bootstrap-Token: ${GATEWAY_BOOTSTRAP_TOKEN}" \ "${SYNC_URL}" \ -o "${TMP_STATE_JSON}" || return 1 mv "${TMP_STATE_JSON}" "${STATE_JSON}" INTERFACE_ADDRESS=$(jq -r '.interface.address' "${STATE_JSON}") NETWORK_CIDR=$(jq -r '.interface.network_cidr' "${STATE_JSON}") LISTEN_PORT=$(jq -r '.interface.listen_port' "${STATE_JSON}") cat > "${WG_GENERATED}" <> "${WG_GENERATED}" < "${NFT_CONF}" if [ -w /proc/sys/net/ipv4/ip_forward ]; then sysctl -w net.ipv4.ip_forward=1 >/dev/null || true fi nft -f "${NFT_CONF}" if ip link show "${IFACE}" >/dev/null 2>&1; then wg syncconf "${IFACE}" <(wg-quick strip "${WG_CONF}") ip link set "${IFACE}" up else wg-quick up "${WG_CONF}" fi echo "Applied WireGuard config from ${WG_CONF}" echo "Applied nftables config from ${NFT_CONF}" } while true; do apply_bundle || echo "Gateway apply failed; retrying in 15 seconds" sleep 15 done