621e8895c9
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
117 lines
5.9 KiB
Bash
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
|
|
}
|