feat(infra): auto-commit con 86 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user