Files
fn_registry/bash/functions/infra/wg_status.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

162 lines
5.1 KiB
Bash

#!/usr/bin/env bash
# wg_status — Parsea `wg show <iface> dump` a JSON estructurado con peers,
# handshake age, status (online/stale/never), bytes rx/tx.
# Resuelve device_id desde comentarios # DeviceID:<id> en wg0.conf.
#
# Usage:
# wg_status [interface_name] # default: wg0
#
# Env:
# WG_FAKE_DUMP=<path> # lee dump de archivo en vez de llamar wg show (para tests)
wg_status() {
local iface="${1:-wg0}"
local conf="${WG_FAKE_CONF:-/etc/wireguard/${iface}.conf}"
local now
now=$(date +%s)
# --- obtener dump (real o fake) ---
local dump
if [[ -n "${WG_FAKE_DUMP:-}" ]]; then
if [[ ! -f "$WG_FAKE_DUMP" ]]; then
printf '{"error":"WG_FAKE_DUMP file not found: %s"}\n' "$WG_FAKE_DUMP"
return 1
fi
dump=$(cat "$WG_FAKE_DUMP")
else
if ! command -v wg &>/dev/null; then
printf '{"error":"wg command not found"}\n'
return 1
fi
if ! dump=$(wg show "$iface" dump 2>&1); then
if echo "$dump" | grep -qi "no such device\|does not exist\|unable to access interface"; then
printf '{"error":"interface not found"}\n'
return 1
fi
printf '{"error":"%s"}\n' "$(echo "$dump" | head -n1 | sed 's/"/\\"/g')"
return 1
fi
fi
# --- primera linea: info de la propia interface ---
# formato: <private_key>\t<public_key>\t<listen_port>\t<fwmark>
local iface_line
iface_line=$(echo "$dump" | head -n1)
local iface_pubkey iface_port
iface_pubkey=$(echo "$iface_line" | awk -F'\t' '{print $2}')
iface_port=$(echo "$iface_line" | awk -F'\t' '{print $3}')
# --- leer DeviceID map desde wg0.conf ---
# Busca patron:
# # DeviceID:<id>
# [Peer]
# PublicKey = <pk>
# Producimos pares "pk\tdevice_id" en un archivo temporal para lookup via awk
local device_map
device_map=$(awk '
/^#[[:space:]]*DeviceID:/ {
split($0, a, "DeviceID:")
did = a[2]
gsub(/^[[:space:]]+|[[:space:]]+$/, "", did)
pending_did = did
}
/^\[Peer\]/ {
in_peer = 1
}
in_peer && /^PublicKey[[:space:]]*=/ {
pk = $0
sub(/^PublicKey[[:space:]]*=[[:space:]]*/, "", pk)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", pk)
if (pending_did != "") {
print pk "\t" pending_did
pending_did = ""
}
in_peer = 0
}
' "$conf" 2>/dev/null)
# --- parsear peers (lineas 2..N del dump) ---
# formato peer: <public_key>\t<preshared_key>\t<endpoint>\t<allowed_ips>\t<latest_handshake>\t<rx_bytes>\t<tx_bytes>\t<persistent_keepalive>
local peers_json
peers_json=$(echo "$dump" | tail -n +2 | awk -v now="$now" -v dmap="$device_map" '
BEGIN {
# construir lookup device_id
n = split(dmap, lines, "\n")
for (i = 1; i <= n; i++) {
if (lines[i] != "") {
split(lines[i], parts, "\t")
pk_to_did[parts[1]] = parts[2]
}
}
first = 1
printf "["
}
NF >= 7 {
pk = $1
endpoint = $3
allowed = $4
hs = $5 + 0
rx = $6 + 0
tx = $7 + 0
ka = $8
# device_id lookup
did = (pk in pk_to_did) ? pk_to_did[pk] : ""
# handshake age y status
if (hs == 0) {
ago = 0
status = "never"
} else {
ago = now - hs
if (ago < 180) status = "online"
else if (ago < 86400) status = "stale"
else status = "stale"
}
# persistent_keepalive
ka_val = (ka == "off" || ka == "") ? 0 : ka + 0
# endpoint null si "(none)"
ep_val = (endpoint == "(none)") ? "null" : "\"" endpoint "\""
# allowed_ips array
n_ips = split(allowed, ips_arr, ",")
ips_json = "["
for (j = 1; j <= n_ips; j++) {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", ips_arr[j])
ips_json = ips_json "\"" ips_arr[j] "\""
if (j < n_ips) ips_json = ips_json ","
}
ips_json = ips_json "]"
if (!first) printf ","
first = 0
printf "{"
printf "\"public_key\":\"%s\"", pk
printf ",\"device_id\":\"%s\"", did
printf ",\"endpoint\":%s", ep_val
printf ",\"allowed_ips\":%s", ips_json
printf ",\"latest_handshake_unix\":%d", hs
printf ",\"latest_handshake_ago_s\":%d",ago
printf ",\"rx_bytes\":%d", rx
printf ",\"tx_bytes\":%d", tx
printf ",\"persistent_keepalive\":%d", ka_val
printf ",\"status\":\"%s\"", status
printf "}"
}
END { printf "]" }
' FS='\t')
# --- output final ---
printf '{"interface":"%s","public_key":"%s","listen_port":%s,"peers":%s}\n' \
"$iface" "$iface_pubkey" "$iface_port" "$peers_json"
}
# Permitir invocacion directa: bash wg_status.sh [iface]
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
wg_status "$@"
fi