#!/usr/bin/env bash # wg_hub_setup — Configura el host como hub WireGuard (servidor central). # Crea /etc/wireguard/wg0.conf con [Interface] block, abre UDP en firewall, # habilita ip_forward persistente, arranca y verifica wg-quick@wg0. # Idempotente: si el conf existe con la misma PrivateKey -> no-op. # Emite JSON a stdout. Logs a stderr con prefijo [wg_hub_setup]. # Exit 0 = éxito, 1 = fallo. wg_hub_setup() { local private_key="${1:-}" local subnet_cidr="${2:-10.42.0.1/24}" local listen_port="${3:-51820}" _wg_hub_log() { echo "[wg_hub_setup] $*" >&2; } # ── Validación de entradas ────────────────────────────────────────────── # private_key: base64 estándar de 44 caracteres (32 bytes) if [[ -z "${private_key}" ]]; then _wg_hub_log "ERROR: private_key requerida (base64 44 chars, generada por wg genkey)" return 1 fi if ! [[ "${private_key}" =~ ^[A-Za-z0-9+/]{43}=$ ]]; then _wg_hub_log "ERROR: private_key no parece base64 válida (se esperan 44 chars terminando en '=')" return 1 fi # subnet_cidr: 10.x.x.x/nn if ! [[ "${subnet_cidr}" =~ ^10\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]]; then _wg_hub_log "ERROR: subnet_cidr debe ser 10.x.x.x/nn, recibido: '${subnet_cidr}'" return 1 fi # listen_port: 1024-65535 if ! [[ "${listen_port}" =~ ^[0-9]+$ ]] || (( listen_port < 1024 || listen_port > 65535 )); then _wg_hub_log "ERROR: listen_port debe ser un entero entre 1024 y 65535, recibido: '${listen_port}'" return 1 fi # ── Verificar que wireguard-tools esté instalado ──────────────────────── if ! command -v wg &>/dev/null; then _wg_hub_log "ERROR: 'wg' no encontrado. Ejecuta wg_install primero." return 1 fi if ! command -v wg-quick &>/dev/null; then _wg_hub_log "ERROR: 'wg-quick' no encontrado. Instala wireguard-tools." return 1 fi # ── Extraer hub_ip (parte sin CIDR prefix) y determinar config_path ──── local hub_ip="${subnet_cidr%%/*}" local config_path="/etc/wireguard/wg0.conf" local interface="wg0" local action_status="" # ── Idempotencia: comparar PrivateKey existente ───────────────────────── if [[ -f "${config_path}" ]]; then local existing_key existing_key=$(sudo grep -E '^\s*PrivateKey\s*=' "${config_path}" 2>/dev/null \ | head -n1 | sed 's/.*=\s*//') if [[ "${existing_key}" == "${private_key}" ]]; then _wg_hub_log "Config existente con misma PrivateKey — no-op (status=already-configured)" printf '{"status":"already-configured","config_path":"%s","interface":"%s","hub_ip":"%s"}\n' \ "${config_path}" "${interface}" "${hub_ip}" return 0 else _wg_hub_log "Config existente con PrivateKey DIFERENTE — haciendo backup y reescribiendo" local backup_path="${config_path}.bak.$(date +%Y%m%d%H%M%S)" sudo cp "${config_path}" "${backup_path}" \ || { _wg_hub_log "ERROR: no se pudo hacer backup en ${backup_path}"; return 1; } _wg_hub_log "Backup guardado en ${backup_path}" action_status="reconfigured" fi else action_status="configured" fi # ── Asegurar que /etc/wireguard existe con permisos correctos ─────────── if [[ ! -d /etc/wireguard ]]; then sudo mkdir -p /etc/wireguard \ || { _wg_hub_log "ERROR: no se pudo crear /etc/wireguard"; return 1; } sudo chmod 700 /etc/wireguard _wg_hub_log "Directorio /etc/wireguard creado" fi # ── Escribir /etc/wireguard/wg0.conf ──────────────────────────────────── _wg_hub_log "Escribiendo ${config_path} (Address=${subnet_cidr}, ListenPort=${listen_port})" sudo tee "${config_path}" > /dev/null </dev/null; then _wg_hub_log "Habilitando ip_forward en ${sysctl_file}" echo "net.ipv4.ip_forward = 1" | sudo tee "${sysctl_file}" > /dev/null \ || { _wg_hub_log "ERROR: no se pudo escribir ${sysctl_file}"; return 1; } fi sudo sysctl -p "${sysctl_file}" >&2 \ || _wg_hub_log "WARN: sysctl -p falló (puede ignorarse si el kernel ya tiene ip_forward=1)" # ── Abrir puerto en firewall ───────────────────────────────────────────── if command -v ufw &>/dev/null && sudo ufw status 2>/dev/null | grep -q "Status: active"; then _wg_hub_log "ufw activo — abriendo UDP/${listen_port}" sudo ufw allow "${listen_port}/udp" >&2 \ || _wg_hub_log "WARN: ufw allow ${listen_port}/udp falló (verificar manualmente)" elif command -v iptables &>/dev/null; then _wg_hub_log "ufw inactivo — usando iptables para abrir UDP/${listen_port}" sudo iptables -C INPUT -p udp --dport "${listen_port}" -j ACCEPT 2>/dev/null \ || sudo iptables -A INPUT -p udp --dport "${listen_port}" -j ACCEPT >&2 \ || _wg_hub_log "WARN: iptables INPUT rule falló (verificar manualmente)" else _wg_hub_log "WARN: ni ufw ni iptables disponibles — abre el puerto ${listen_port}/udp manualmente" fi # ── Detener interfaz si estaba corriendo (para aplicar nueva config) ──── if sudo wg show "${interface}" &>/dev/null 2>&1; then _wg_hub_log "Interfaz ${interface} activa — deteniendo antes de reconfigurar" sudo systemctl stop "wg-quick@${interface}" 2>/dev/null \ || sudo wg-quick down "${interface}" 2>/dev/null \ || _wg_hub_log "WARN: no se pudo detener ${interface} (puede que no estuviera activa)" fi # ── Habilitar y arrancar wg-quick@wg0 ──────────────────────────────────── _wg_hub_log "Habilitando systemd unit wg-quick@${interface}" sudo systemctl enable "wg-quick@${interface}" >&2 \ || { _wg_hub_log "ERROR: systemctl enable wg-quick@${interface} falló"; return 1; } _wg_hub_log "Arrancando wg-quick@${interface}" sudo systemctl start "wg-quick@${interface}" >&2 \ || { _wg_hub_log "ERROR: systemctl start wg-quick@${interface} falló"; return 1; } # ── Verificar que la interfaz está UP ──────────────────────────────────── local retries=5 local up=0 for (( i=0; i/dev/null 2>&1; then up=1 break fi sleep 1 done if [[ "${up}" -eq 0 ]]; then _wg_hub_log "ERROR: 'wg show ${interface}' falló tras ${retries}s — la interfaz no arrancó" return 1 fi _wg_hub_log "Interfaz ${interface} UP (status=${action_status})" printf '{"status":"%s","config_path":"%s","interface":"%s","hub_ip":"%s"}\n' \ "${action_status}" "${config_path}" "${interface}" "${hub_ip}" return 0 }