Son aktivite 1 month ago

gistfile1.txt Ham
1#!/usr/bin/env bash
2
3FW_EVENTS_LOG="$(ctx::fw_events_log)"
4WG_EVENTS_LOG="$(ctx::events_log)"
5
6function cmd::logs::on_load() {
7 flag::register --name
8 flag::register --type
9 flag::register --since
10 flag::register --limit
11 flag::register --fw
12 flag::register --wg
13 flag::register --follow
14 flag::register --merged
15 flag::register --all
16 flag::register --before
17 flag::register --force
18 flag::register --days
19 flag::register --raw
20 flag::register --detailed
21}
22
23function cmd::logs::help() {
24 cat <<EOF
25Usage: wgctl logs [subcommand] [options]
26
27Show or manage WireGuard and firewall activity logs.
28
29Subcommands:
30 show (default) Show activity logs
31 remove, rm Remove log entries
32 rotate Remove entries older than N days
33
34Options for show:
35 --name <name> Filter by client name
36 --type <type> Filter by device type
37 --limit <n> Max results per source (default: 50)
38 --fw Show only firewall drops
39 --wg Show only WireGuard events
40 --merged Show all events chronologically interleaved
41 --follow, -f Follow logs in real time (alias: wgctl watch)
42 --raw Show raw IPs without service annotation
43
44Options for remove:
45 --name <name> Remove entries for specific peer
46 --all Remove all log entries
47 --fw Remove only firewall events
48 --wg Remove only WireGuard events
49 --before <days> Remove entries older than N days
50 --force Skip confirmation
51
52Options for rotate:
53 --days <n> Days to keep (default: 7)
54 --force Skip confirmation
55
56Examples:
57 wgctl logs
58 wgctl logs --name phone-nuno
59 wgctl logs --fw --limit 100
60 wgctl logs --merged
61 wgctl logs --follow
62 wgctl logs remove --name phone-nuno
63 wgctl logs rotate --days 30
64EOF
65}
66
67function cmd::logs::run() {
68 local subcmd="${1:-show}"
69 if [[ "$subcmd" == --* ]]; then
70 subcmd="show"
71 else
72 shift || true
73 fi
74
75 case "$subcmd" in
76 show) cmd::logs::show "$@" ;;
77 remove|rm|del) cmd::logs::remove "$@" ;;
78 rotate) cmd::logs::rotate "$@" ;;
79 help) cmd::logs::help ;;
80 *)
81 log::error "Unknown subcommand: '${subcmd}'"
82 cmd::logs::help
83 return 1
84 ;;
85 esac
86}
87
88function cmd::logs::show() {
89 local name="" type="" limit=50
90 local fw_only=false wg_only=false follow=false merged=false raw=false detailed=false
91
92
93 while [[ $# -gt 0 ]]; do
94 case "$1" in
95 --name) name="$2"; shift 2 ;;
96 --type) type="$2"; shift 2 ;;
97 --limit) limit="$2"; shift 2 ;;
98 --fw) fw_only=true; shift ;;
99 --wg) wg_only=true; shift ;;
100 --merged) merged=true; shift ;;
101 --follow|-f) follow=true; shift ;;
102 --raw) raw=true; shift ;;
103 --detailed) detailed=true shift ;;
104 --help) cmd::logs::help; return ;;
105 *)
106 log::error "Unknown flag: $1"
107 return 1
108 ;;
109 esac
110 done
111
112 local collapse=1
113 $detailed && collapse=0
114
115
116 if [[ -n "$name" && -n "$type" ]]; then
117 name=$(peers::resolve_and_require "$name" "$type") || return 1
118 fi
119
120 local filter_ip=""
121 if [[ -n "$name" ]]; then
122 filter_ip=$(peers::get_ip "$name")
123 [[ -z "$filter_ip" ]] && log::error "Could not find IP for: $name" && return 1
124 fi
125
126 if $follow; then
127 cmd::logs::follow "$filter_ip" "$name" "$type" "$fw_only" "$wg_only"
128 return
129 fi
130
131 local net_file=""
132 $raw || net_file="$(ctx::net)"
133
134 log::section "WireGuard Activity Log"
135 printf "\n"
136
137 if $merged; then
138 cmd::logs::show_merged "$filter_ip" "$name" "$type" "$limit" "$net_file"
139 return
140 fi
141
142 $wg_only || cmd::logs::show_fw_events "$filter_ip" "$name" "$type" "$limit" "$net_file" "$collapse"
143 $fw_only || cmd::logs::show_wg_events "$filter_ip" "$name" "$type" "$limit" "$collapse"
144}
145
146function cmd::logs::show_fw_events() {
147 local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \
148 limit="${4:-50}" net_file="${5:-}" collapse="${6:-1}"
149
150 [[ ! -f "$FW_EVENTS_LOG" ]] && return 0
151
152 local data
153 data=$(json::fw_events "$FW_EVENTS_LOG" "$filter_ip" "$filter_type" \
154 "$(ctx::clients)" "${net_file:-}" "$limit" "$collapse" 2>/dev/null)
155
156 [[ -z "$data" ]] && return 0
157
158 # Measure column widths
159 local w_client=16 w_dest=20
160 while IFS='|' read -r ts client dest_ip dest_port proto svc count; do
161 [[ -z "$ts" ]] && continue
162 (( ${#client} > w_client )) && w_client=${#client}
163 local dest_display
164 local host_name
165 host_name=$(hosts::resolve_ip "$dest_ip")
166 if [[ -n "$host_name" ]]; then
167 dest_display="$host_name"
168 elif [[ -n "$svc" ]]; then
169 [[ -n "$dest_port" ]] && dest_display="${svc}/${proto}" || dest_display="${svc} (${proto})"
170 else
171 [[ -n "$dest_port" ]] && dest_display="${dest_ip}:${dest_port}/${proto}" || dest_display="${dest_ip} (${proto})"
172 fi
173 (( ${#dest_display} > w_dest )) && w_dest=${#dest_display}
174 done <<< "$data"
175 (( w_client += 2 ))
176 (( w_dest += 2 ))
177
178 ui::logs::fw_section_header
179 while IFS='|' read -r ts client dest_ip dest_port proto svc count; do
180 [[ -z "$ts" ]] && continue
181 ui::logs::fw_row "$ts" "$client" "$dest_ip" "$dest_port" \
182 "$proto" "$svc" "$count" "$w_client" "$w_dest"
183 done <<< "$data"
184 printf "\n"
185}
186
187function cmd::logs::show_wg_events() {
188 local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \
189 limit="${4:-50}" collapse="${5:-1}"
190
191 [[ ! -f "$WG_EVENTS_LOG" ]] && return 0
192
193 local data
194 data=$(json::wg_events "$WG_EVENTS_LOG" "$filter_name" "$filter_type" "$limit" "$collapse" 2>/dev/null)
195
196 [[ -z "$data" ]] && return 0
197
198 # Resolve endpoints and measure column widths
199 local w_client=16 w_endpoint=16
200 local resolved_data=""
201 while IFS='|' read -r ts client endpoint event count; do
202 [[ -z "$ts" ]] && continue
203 local endpoint_display
204 endpoint_display=$(resolve::ip "$endpoint")
205 [[ -z "$endpoint_display" ]] && endpoint_display="$endpoint"
206 resolved_data+="${ts}|${client}|${endpoint_display}|${event}|${count}"$'\n'
207 (( ${#client} > w_client )) && w_client=${#client}
208 (( ${#endpoint_display} > w_endpoint )) && w_endpoint=${#endpoint_display}
209 done <<< "$data"
210 (( w_client += 2 ))
211 (( w_endpoint += 2 ))
212
213 ui::logs::wg_section_header
214 while IFS='|' read -r ts client endpoint event count; do
215 [[ -z "$ts" ]] && continue
216 ui::logs::wg_row "$ts" "$client" "$endpoint" "$event" \
217 "$count" "$w_client" "$w_endpoint"
218 done <<< "$resolved_data"
219 printf "\n"
220}
221
222
223function cmd::logs::show_merged() {
224 local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \
225 limit="${4:-50}" net_file="${5:-}"
226
227 local fw_data wg_data
228 fw_data=$(json::fw_events "$FW_EVENTS_LOG" "$filter_ip" "$filter_type" \
229 "$(ctx::clients)" "${net_file:-}" "$limit" 2>/dev/null)
230 wg_data=$(json::wg_events "$WG_EVENTS_LOG" "$filter_name" "$filter_type" \
231 "$limit" 2>/dev/null)
232
233 # Measure widths across both sources
234 local w_client=16 w_dest=20
235 while IFS='|' read -r ts client rest; do
236 [[ -z "$ts" ]] && continue
237 (( ${#client} > w_client )) && w_client=${#client}
238 done < <(echo "$fw_data"; echo "$wg_data")
239 (( w_client += 2 ))
240
241 # Tag and merge: prefix fw lines with "fw|", wg lines with "wg|"
242 local merged_data
243 merged_data=$(
244 while IFS='|' read -r ts client dest_ip dest_port proto svc count; do
245 [[ -z "$ts" ]] && continue
246 echo "fw|${ts}|${client}|${dest_ip}|${dest_port}|${proto}|${svc}|${count}"
247 done <<< "$fw_data"
248 while IFS='|' read -r ts client endpoint event count; do
249 [[ -z "$ts" ]] && continue
250 echo "wg|${ts}|${client}|${endpoint}|${event}|${count}"
251 done <<< "$wg_data"
252 )
253
254 # Sort by timestamp field 2
255 while IFS='|' read -r source ts rest; do
256 [[ -z "$source" ]] && continue
257 case "$source" in
258 fw)
259 IFS='|' read -r client dest_ip dest_port proto svc count <<< "$rest"
260 local dest_display
261 if [[ -n "$svc" ]]; then
262 [[ -n "$dest_port" ]] && dest_display="${svc}/${proto}" || dest_display="${svc} (${proto})"
263 else
264 [[ -n "$dest_port" ]] && dest_display="${dest_ip}:${dest_port}/${proto}" || dest_display="${dest_ip} (${proto})"
265 fi
266 (( ${#dest_display} > w_dest )) && w_dest=${#dest_display}
267 ;;
268 esac
269 done <<< "$merged_data"
270 (( w_dest += 2 ))
271
272 while IFS='|' read -r source ts rest; do
273 [[ -z "$source" ]] && continue
274 case "$source" in
275 fw)
276 IFS='|' read -r client dest_ip dest_port proto svc count <<< "$rest"
277 ui::watch::fw_row "$ts" "$client" \
278 "$(ui::logs::build_dest "$dest_ip" "$dest_port" "$proto" "$svc")" \
279 "$w_client" "$w_dest"
280 ;;
281 wg)
282 IFS='|' read -r client endpoint event count <<< "$rest"
283 ui::watch::wg_row "$ts" "$client" "$endpoint" "$event" \
284 "$w_client" "$w_dest"
285 ;;
286 esac
287 done < <(echo "$merged_data" | sort -t'|' -k2,2)
288
289 printf "\n"
290}
291
292function cmd::logs::follow() {
293 local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}"
294 local fw_only="${4:-false}" wg_only="${5:-false}"
295
296 log::section "WireGuard Live Log (Ctrl+C to stop)"
297 printf "\n"
298
299 # Delegate to watch command
300 local watch_args=()
301 [[ -n "$filter_name" ]] && watch_args+=(--name "$filter_name")
302 [[ -n "$filter_type" ]] && watch_args+=(--type "$filter_type")
303 $fw_only && watch_args+=(--restricted)
304 $wg_only && watch_args+=(--blocked)
305
306 cmd::watch::run "${watch_args[@]}"
307}
308
309function cmd::logs::remove() {
310 local name="" type="" before="" force=false
311 local fw_only=false wg_only=false all=false
312
313 while [[ $# -gt 0 ]]; do
314 case "$1" in
315 --name) name="$2"; shift 2 ;;
316 --type) type="$2"; shift 2 ;;
317 --before) before="$2"; shift 2 ;;
318 --fw) fw_only=true; shift ;;
319 --wg) wg_only=true; shift ;;
320 --all) all=true; shift ;;
321 --force) force=true; shift ;;
322 --help) cmd::logs::help; return ;;
323 *)
324 log::error "Unknown flag: $1"
325 return 1
326 ;;
327 esac
328 done
329
330 if ! $all && [[ -z "$name" && -z "$before" ]]; then
331 log::error "Specify --name, --before, or --all"
332 cmd::logs::help
333 return 1
334 fi
335
336 local filter_ip=""
337 if [[ -n "$name" ]]; then
338 name=$(peers::resolve_and_require "$name" "$type") || return 1
339 filter_ip=$(peers::get_ip "$name")
340 fi
341
342 local desc=""
343 $all && desc="all entries"
344 [[ -n "$name" ]] && desc="entries for '${name}'"
345 [[ -n "$before" ]] && desc="${desc:+$desc, }entries older than ${before} days"
346 $fw_only && desc="${desc} (fw only)"
347 $wg_only && desc="${desc} (wg only)"
348
349 if ! $force; then
350 read -r -p "Remove ${desc}? [y/N] " confirm
351 case "$confirm" in
352 [yY]*) ;;
353 *) log::info "Aborted"; return 0 ;;
354 esac
355 fi
356
357 local result
358 result=$(json::remove_events_filtered \
359 "$WG_EVENTS_LOG" "$FW_EVENTS_LOG" \
360 "${name:-}" "${filter_ip:-}" \
361 "$fw_only" "$wg_only" \
362 "${before:-}")
363
364 local removed_wg removed_fw
365 IFS="|" read -r removed_wg removed_fw <<< "$result"
366 local total=$(( removed_wg + removed_fw ))
367
368 if [[ "$total" -eq 0 ]]; then
369 log::wg_warning "No log entries found matching the criteria"
370 return 0
371 fi
372
373 log::wg_success "Removed ${total} log entries (wg: ${removed_wg}, fw: ${removed_fw})"
374}
375
376function cmd::logs::rotate() {
377 local days=7 force=false
378
379 while [[ $# -gt 0 ]]; do
380 case "$1" in
381 --days) days="$2"; shift 2 ;;
382 --force) force=true; shift ;;
383 --help) cmd::logs::help; return ;;
384 *) log::error "Unknown flag: $1"; return 1 ;;
385 esac
386 done
387
388 $force || {
389 read -r -p "Remove log entries older than ${days} days? [y/N] " confirm
390 case "$confirm" in
391 [yY]*) ;;
392 *) log::info "Aborted"; return 0 ;;
393 esac
394 }
395
396 local result
397 result=$(json::remove_events_filtered \
398 "$WG_EVENTS_LOG" "$FW_EVENTS_LOG" \
399 "" "" "false" "false" "$days")
400
401 local removed_wg removed_fw
402 IFS="|" read -r removed_wg removed_fw <<< "$result"
403 local total=$(( removed_wg + removed_fw ))
404
405 if [[ "$total" -eq 0 ]]; then
406 log::wg_warning "No log entries older than ${days} days"
407 return 0
408 fi
409
410 log::wg_success "Rotated ${total} entries older than ${days} days (wg: ${removed_wg}, fw: ${removed_fw})"
411}