#!/usr/bin/env bash # ============================================ # Lifecycle # ============================================ function config::on_load() { config::validate } # ============================================ # Server # ============================================ WG_INTERFACE="wg0" WG_CONFIG="$(ctx::wg)/${WG_INTERFACE}.conf" WG_SERVER_PUBLIC_KEY_FILE="$(ctx::wg)/server_public.key" WG_SERVER_PRIVATE_KEY_FILE="$(ctx::wg)/server_private.key" WG_ENDPOINT="wg.krilio.net:51820" WG_DNS="10.0.0.103" WG_LISTEN_PORT="51820" WG_SUBNET="10.1.0.0/16" WG_LAN="10.0.0.0/24" # ============================================ # Device Type → Subnet Mapping # ============================================ declare -gA DEVICE_SUBNETS=( [desktop]="10.1.1" [laptop]="10.1.2" [phone]="10.1.3" [tablet]="10.1.4" [guest]="10.1.100" ) # ============================================ # Tunnel Modes # ============================================ # split — route only VPN subnet + LAN through WireGuard # full — route all traffic through WireGuard WG_TUNNEL_SPLIT="${WG_SUBNET}, ${WG_LAN}" WG_TUNNEL_FULL="0.0.0.0/0, ::/0" # Default tunnel mode per device type declare -gA DEVICE_TUNNEL_MODE=( [desktop]="split" [laptop]="split" [phone]="split" [tablet]="split" [guest]="split" ) # ============================================ # Accessors # ============================================ function config::interface() { echo "$WG_INTERFACE"; } function config::config_file() { echo "$WG_CONFIG"; } function config::endpoint() { echo "$WG_ENDPOINT"; } function config::dns() { echo "$WG_DNS"; } function config::listen_port() { echo "$WG_LISTEN_PORT"; } function config::subnet() { echo "$WG_SUBNET"; } function config::lan() { echo "$WG_LAN"; } function config::tunnel_split() { echo "$WG_TUNNEL_SPLIT"; } function config::tunnel_full() { echo "$WG_TUNNEL_FULL"; } function config::server_public_key() { cat "$WG_SERVER_PUBLIC_KEY_FILE" } function config::device_types() { local types { set +u; types="${!DEVICE_SUBNETS[@]}"; set -u; } echo "$types" } function config::is_valid_type() { local type="$1" local subnet subnet=$(config::subnet_for "$type") [[ -n "$subnet" ]] } function config::subnet_for() { local type="$1" local result { set +u; result="${DEVICE_SUBNETS[$type]:-}"; set -u; } echo "$result" } function config::default_tunnel_for() { local type="$1" local result { set +u; result="${DEVICE_TUNNEL_MODE[$type]:-split}"; set -u; } echo "$result" } function config::allowed_ips_for() { local type="$1" local tunnel="${2:-}" # If tunnel mode not specified, use device default if [[ -z "$tunnel" ]]; then tunnel=$(config::default_tunnel_for "$type") fi case "$tunnel" in full) echo "$WG_TUNNEL_FULL" ;; split) echo "$WG_TUNNEL_SPLIT" ;; *) log::error "Unknown tunnel mode: ${tunnel} (use 'split' or 'full')" return 1 ;; esac } # ============================================ # Validation # ============================================ function config::validate() { if [[ ! -f "$WG_SERVER_PUBLIC_KEY_FILE" ]]; then log::error "Server public key not found: ${WG_SERVER_PUBLIC_KEY_FILE}" exit 1 fi if [[ ! -f "$WG_SERVER_PRIVATE_KEY_FILE" ]]; then log::error "Server private key not found: ${WG_SERVER_PRIVATE_KEY_FILE}" exit 1 fi if [[ ! -f "$WG_CONFIG" ]]; then log::error "WireGuard config not found: ${WG_CONFIG}" exit 1 fi }