Files
fn_registry/bash/functions/infra/wg_client_install.sh
T
egutierrez 621e8895c9 feat(infra): auto-commit con 86 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 19:38:15 +02:00

117 lines
5.9 KiB
Bash

#!/usr/bin/env bash
# wg_client_install — Device-side: instala wg0.conf en /etc/wireguard/, habilita
# systemd wg-quick@<iface>, verifica handshake con hub. Idempotente.
# Acepta config por path o stdin ("-").
# Exit 0 = éxito (installed o already-configured), 1 = error fatal.
wg_client_install() {
local config_src="${1:--}"
local iface="${2:-wg0}"
local conf_dest="/etc/wireguard/${iface}.conf"
local config_content="" hub_endpoint="" handshake_seen="false"
_wg_ci_log() { echo "[wg_client_install] $*" >&2; }
# ── Prereq: wg debe estar instalado ──────────────────────────────────────
if ! command -v wg &>/dev/null; then
_wg_ci_log "ERROR: 'wg' no encontrado. Ejecuta wg_install primero."
return 1
fi
# ── Leer contenido del .conf ──────────────────────────────────────────────
if [[ "${config_src}" == "-" ]]; then
_wg_ci_log "Leyendo config desde stdin"
config_content=$(cat) || { _wg_ci_log "ERROR: fallo al leer stdin"; return 1; }
elif [[ -f "${config_src}" ]]; then
_wg_ci_log "Leyendo config desde ${config_src}"
config_content=$(cat "${config_src}") || { _wg_ci_log "ERROR: fallo al leer ${config_src}"; return 1; }
else
_wg_ci_log "ERROR: '${config_src}' no es un path existente ni '-' (stdin)"
return 1
fi
if [[ -z "${config_content}" ]]; then
_wg_ci_log "ERROR: contenido de config vacío"
return 1
fi
# ── Extraer endpoint del hub para incluirlo en el JSON de salida ──────────
hub_endpoint=$(printf '%s\n' "${config_content}" | grep -m1 '^Endpoint\s*=' | sed 's/.*=\s*//' | tr -d '[:space:]' || true)
# ── Idempotencia: comparar con conf existente ─────────────────────────────
if [[ -f "${conf_dest}" ]]; then
local existing_content
existing_content=$(sudo cat "${conf_dest}" 2>/dev/null || cat "${conf_dest}" 2>/dev/null || true)
if [[ "${existing_content}" == "${config_content}" ]]; then
_wg_ci_log "Configuración idéntica ya presente en ${conf_dest}; nada que hacer"
printf '{"status":"already-configured","interface":"%s","hub_endpoint":"%s","handshake_seen":false}\n' \
"${iface}" "${hub_endpoint}"
return 0
fi
# Contenido difiere → backup + rewrite
local backup="${conf_dest}.bak.$(date +%Y%m%d%H%M%S)"
_wg_ci_log "Configuración existente difiere; backup → ${backup}"
sudo cp "${conf_dest}" "${backup}" \
|| { _wg_ci_log "ERROR: no se pudo hacer backup de ${conf_dest}"; return 1; }
fi
# ── Crear directorio y escribir conf ─────────────────────────────────────
sudo mkdir -p "/etc/wireguard" \
|| { _wg_ci_log "ERROR: no se pudo crear /etc/wireguard"; return 1; }
printf '%s\n' "${config_content}" | sudo tee "${conf_dest}" >/dev/null \
|| { _wg_ci_log "ERROR: no se pudo escribir ${conf_dest}"; return 1; }
sudo chmod 600 "${conf_dest}" \
|| { _wg_ci_log "WARN: no se pudo chmod 600 ${conf_dest}"; }
_wg_ci_log "Config escrita en ${conf_dest} (chmod 600)"
# ── Habilitar + arrancar systemd unit ─────────────────────────────────────
if ! command -v systemctl &>/dev/null; then
_wg_ci_log "WARN: systemctl no disponible."
_wg_ci_log " En WSL2 sin systemd: ejecuta 'sudo wg-quick up ${iface}' manualmente."
_wg_ci_log " Para autostart en WSL2: añade 'sudo wg-quick up ${iface}' a ~/.bashrc o usa WSL2 con systemd habilitado."
printf '{"status":"installed-no-systemd","interface":"%s","hub_endpoint":"%s","handshake_seen":false}\n' \
"${iface}" "${hub_endpoint}"
return 0
fi
_wg_ci_log "Habilitando y arrancando wg-quick@${iface}"
if ! sudo systemctl enable --now "wg-quick@${iface}" 2>&1 | tee /dev/stderr >&2; then
_wg_ci_log "ERROR: systemctl enable --now wg-quick@${iface} falló."
_wg_ci_log " En WSL2: asegúrate de tener kernel >= 5.6 y systemd habilitado (/etc/wsl.conf: [boot] systemd=true)."
_wg_ci_log " Si NetworkManager gestiona ${iface}: añade 'unmanaged-devices=interface-name:${iface}' a /etc/NetworkManager/conf.d/99-wg.conf"
return 1
fi
_wg_ci_log "wg-quick@${iface} habilitado y activo"
# ── Esperar handshake (hasta 10 s) ────────────────────────────────────────
local deadline=$(( $(date +%s) + 10 ))
_wg_ci_log "Esperando handshake en ${iface} (timeout 10s)..."
while [[ $(date +%s) -lt ${deadline} ]]; do
local hs_output
hs_output=$(sudo wg show "${iface}" latest-handshakes 2>/dev/null || true)
# latest-handshakes devuelve "<pubkey> <unix_ts>"; ts > 0 = handshake visto
if printf '%s\n' "${hs_output}" | awk '{print $2}' | grep -qE '^[1-9][0-9]+$'; then
handshake_seen="true"
_wg_ci_log "Handshake confirmado en ${iface}"
break
fi
sleep 1
done
if [[ "${handshake_seen}" == "false" ]]; then
_wg_ci_log "WARN: timeout esperando handshake en ${iface}. La interfaz está activa pero el hub no ha respondido aún."
_wg_ci_log " Verifica: endpoint accesible, hub corriendo, claves correctas."
printf '{"status":"installed-no-handshake","interface":"%s","hub_endpoint":"%s","handshake_seen":false}\n' \
"${iface}" "${hub_endpoint}"
return 0
fi
printf '{"status":"installed","interface":"%s","hub_endpoint":"%s","handshake_seen":true}\n' \
"${iface}" "${hub_endpoint}"
return 0
}