Dernière activité 2 months ago

Révision c2d5ecef13a917e5db7d0cd2f90e2e0f8e13e2bf

gistfile1.txt Brut
1#!/usr/bin/env bash
2
3# ============================================
4# Config
5# ============================================
6
7MONITOR_DIR="$(ctx::root)/daemon"
8WATCHLIST_FILE="${MONITOR_DIR}/watchlist.json"
9EVENTS_LOG="${MONITOR_DIR}/events.log"
10MONITOR_SERVICE="wgctl-monitor"
11
12# ============================================
13# Lifecycle
14# ============================================
15
16function monitor::on_load() {
17 if [[ ! -f "$WATCHLIST_FILE" ]]; then
18 echo '{}' > "$WATCHLIST_FILE"
19 fi
20 if [[ ! -f "$EVENTS_LOG" ]]; then
21 touch "$EVENTS_LOG"
22 fi
23}
24
25# ============================================
26# Watchlist
27# ============================================
28
29function monitor::watch() {
30 local ip="$1" client="$2"
31 json::set "$WATCHLIST_FILE" "$ip" "$client"
32 log::wg "Watching: ${client} (${ip})"
33}
34
35function monitor::unwatch() {
36 local ip="$1"
37 json::delete "$WATCHLIST_FILE" "$ip"
38}
39
40function monitor::is_watched() {
41 local ip="$1"
42 python3 -c "
43import json
44with open('${WATCHLIST_FILE}') as f:
45 wl = json.load(f)
46exit(0 if '${ip}' in wl else 1)
47"
48}
49
50function monitor::unwatch_client() {
51 local name="$1"
52 python3 -c "
53import json
54with open('${WATCHLIST_FILE}') as f:
55 wl = json.load(f)
56wl = {k: v for k, v in wl.items() if v != '${name}'}
57with open('${WATCHLIST_FILE}', 'w') as f:
58 json.dump(wl, f, indent=2)
59"
60 log::wg "Unwatched client: ${name}"
61}
62
63# ============================================
64# Events
65# ============================================
66
67function monitor::last_attempt() {
68 local client="$1"
69 python3 -c "
70import json
71
72events = []
73try:
74 with open('${EVENTS_LOG}') as f:
75 for line in f:
76 try:
77 e = json.loads(line.strip())
78 if e.get('client') == '${client}':
79 events.append(e)
80 except:
81 pass
82except:
83 pass
84
85if events:
86 print(events[-1].get('timestamp', ''))
87"
88}
89
90function monitor::last_endpoint() {
91 local client="$1"
92 python3 -c "
93import json
94
95events = []
96try:
97 with open('${EVENTS_LOG}') as f:
98 for line in f:
99 try:
100 e = json.loads(line.strip())
101 if e.get('client') == '${client}' and e.get('endpoint'):
102 events.append(e)
103 except:
104 pass
105except:
106 pass
107
108if events:
109 print(events[-1].get('endpoint', ''))
110"
111}
112
113function monitor::events_for() {
114 local ip="$1"
115 local limit="${2:-50}"
116 python3 -c "
117import json
118from datetime import datetime, timezone
119
120events = []
121try:
122 with open('${EVENTS_LOG}') as f:
123 for line in f:
124 try:
125 e = json.loads(line.strip())
126 if e.get('ip') == '${ip}':
127 events.append(e)
128 except:
129 pass
130except:
131 pass
132
133for e in events[-${limit}:]:
134 ts = e.get('timestamp', '')
135 try:
136 dt = datetime.fromisoformat(ts)
137 ts = dt.strftime('%Y-%m-%d %H:%M:%S')
138 except:
139 pass
140 endpoint = e.get('endpoint', '—')
141 client = e.get('client', '—')
142 event = e.get('event', '—')
143 print(f' {ts} {client:<20} {endpoint:<20} {event}')
144"
145}
146
147# ============================================
148# Endpoint Cache (for blocked clients)
149# ============================================
150
151ENDPOINT_CACHE="${WGCTL_DIR}/daemon/endpoint_cache.json"
152
153function monitor::cache_endpoint() {
154 local client="$1" ip="$2"
155 json::set "$ENDPOINT_CACHE" "$client" "$ip"
156}
157
158function monitor::get_cached_endpoint() {
159 local client="$1"
160 json::get "$ENDPOINT_CACHE" "$client"
161}
162
163function monitor::update_endpoint_cache() {
164 while IFS=$'\t' read -r key endpoint; do
165 [[ "$endpoint" == "(none)" ]] && continue
166 local ip
167 ip=$(echo "$endpoint" | cut -d':' -f1)
168 local client
169 client=$(keys::find_by_public "$key") || continue
170 monitor::cache_endpoint "$client" "$ip"
171 done < <(wg show "$(config::interface)" endpoints 2>/dev/null)
172}
173
174# ============================================
175# Endpoint (from wg show, for active clients)
176# ============================================
177
178function monitor::endpoint_for_key() {
179 local public_key="$1"
180 wg show "$(config::interface)" endpoints 2>/dev/null \
181 | grep "^${public_key}" \
182 | awk '{print $2}' \
183 | cut -d':' -f1
184}
185
186# ============================================
187# Service
188# ============================================
189
190function monitor::start() {
191 systemctl start "$MONITOR_SERVICE"
192 log::wg_start "Monitor daemon started"
193}
194
195function monitor::stop() {
196 systemctl stop "$MONITOR_SERVICE"
197 log::wg_stop "Monitor daemon stopped"
198}
199
200function monitor::restart() {
201 systemctl restart "$MONITOR_SERVICE"
202 log::wg_start "Monitor daemon restarted"
203}
204
205function monitor::is_running() {
206 systemctl is-active --quiet "$MONITOR_SERVICE"
207}
208