134 lines
3.5 KiB
Go
134 lines
3.5 KiB
Go
package collector
|
|
|
|
import (
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type WireGuardTunnel struct {
|
|
Interface string `json:"interface"`
|
|
PublicKey string `json:"public_key"`
|
|
ListenPort int `json:"listen_port"`
|
|
Peers []WireGuardPeer `json:"peers"`
|
|
}
|
|
|
|
type WireGuardPeer struct {
|
|
PublicKey string `json:"public_key"`
|
|
Endpoint string `json:"endpoint"`
|
|
AllowedIPs []string `json:"allowed_ips"`
|
|
LatestHandshake int64 `json:"latest_handshake_epoch"`
|
|
HandshakeAge string `json:"handshake_age"`
|
|
TransferRx int64 `json:"transfer_rx_bytes"`
|
|
TransferTx int64 `json:"transfer_tx_bytes"`
|
|
Keepalive int `json:"persistent_keepalive"`
|
|
Status string `json:"status"` // "active", "inactive", "unknown"
|
|
}
|
|
|
|
func CollectWireGuard() []WireGuardTunnel {
|
|
// wg show all dump: maschinenlesbares Format
|
|
// Voller Pfad, da daemon(8) evtl. reduzierten PATH hat
|
|
wgPath := "/usr/bin/wg"
|
|
if _, err := exec.LookPath("wg"); err == nil {
|
|
wgPath = "wg"
|
|
}
|
|
out, err := exec.Command(wgPath, "show", "all", "dump").Output()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
tunnels := make(map[string]*WireGuardTunnel)
|
|
var order []string
|
|
|
|
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
fields := strings.Split(line, "\t")
|
|
if len(fields) < 5 {
|
|
continue
|
|
}
|
|
|
|
iface := fields[0]
|
|
|
|
// Interface-Zeile: iface privkey pubkey listenport fwmark
|
|
if _, exists := tunnels[iface]; !exists {
|
|
port := 0
|
|
if len(fields) >= 4 {
|
|
port, _ = strconv.Atoi(fields[3])
|
|
}
|
|
pubkey := ""
|
|
if len(fields) >= 3 {
|
|
pubkey = fields[2]
|
|
}
|
|
tunnels[iface] = &WireGuardTunnel{
|
|
Interface: iface,
|
|
PublicKey: pubkey,
|
|
ListenPort: port,
|
|
}
|
|
order = append(order, iface)
|
|
continue
|
|
}
|
|
|
|
// Peer-Zeile: iface pubkey presharedkey endpoint allowedips latest-handshake transfer-rx transfer-tx keepalive
|
|
if len(fields) < 9 {
|
|
continue
|
|
}
|
|
|
|
peer := WireGuardPeer{
|
|
PublicKey: fields[1],
|
|
Endpoint: fields[3],
|
|
}
|
|
|
|
// Allowed IPs
|
|
if fields[4] != "(none)" {
|
|
peer.AllowedIPs = strings.Split(fields[4], ",")
|
|
}
|
|
|
|
// Latest handshake (epoch)
|
|
if ts, err := strconv.ParseInt(fields[5], 10, 64); err == nil && ts > 0 {
|
|
peer.LatestHandshake = ts
|
|
age := time.Since(time.Unix(ts, 0))
|
|
if age < 3*time.Minute {
|
|
peer.Status = "active"
|
|
} else if age < 10*time.Minute {
|
|
peer.Status = "inactive"
|
|
} else {
|
|
peer.Status = "inactive"
|
|
}
|
|
// Menschenlesbare Altersangabe
|
|
if age.Hours() >= 24 {
|
|
peer.HandshakeAge = strconv.Itoa(int(age.Hours()/24)) + "d " + strconv.Itoa(int(age.Hours())%24) + "h ago"
|
|
} else if age.Hours() >= 1 {
|
|
peer.HandshakeAge = strconv.Itoa(int(age.Hours())) + "h " + strconv.Itoa(int(age.Minutes())%60) + "m ago"
|
|
} else if age.Minutes() >= 1 {
|
|
peer.HandshakeAge = strconv.Itoa(int(age.Minutes())) + "m " + strconv.Itoa(int(age.Seconds())%60) + "s ago"
|
|
} else {
|
|
peer.HandshakeAge = strconv.Itoa(int(age.Seconds())) + "s ago"
|
|
}
|
|
} else {
|
|
peer.Status = "unknown"
|
|
peer.HandshakeAge = "never"
|
|
}
|
|
|
|
// Transfer RX/TX
|
|
peer.TransferRx, _ = strconv.ParseInt(fields[6], 10, 64)
|
|
peer.TransferTx, _ = strconv.ParseInt(fields[7], 10, 64)
|
|
|
|
// Keepalive
|
|
if fields[8] != "off" {
|
|
peer.Keepalive, _ = strconv.Atoi(fields[8])
|
|
}
|
|
|
|
tunnels[iface].Peers = append(tunnels[iface].Peers, peer)
|
|
}
|
|
|
|
// Geordnet zurueckgeben
|
|
var result []WireGuardTunnel
|
|
for _, name := range order {
|
|
result = append(result, *tunnels[name])
|
|
}
|
|
return result
|
|
}
|