Ultima attività 1 month ago

nuno ha revisionato questo gist 1 month ago. Vai alla revisione

1 file changed, 277 insertions

gistfile1.txt(file creato)

@@ -0,0 +1,277 @@
1 + #!/usr/bin/env bash
2 +
3 + # ============================================
4 + # Lifecycle
5 + # ============================================
6 +
7 + function cmd::watch::on_load() {
8 + flag::register --type
9 + flag::register --name
10 + flag::register --peers
11 + flag::register --blocked
12 + flag::register --restricted
13 + flag::register --allowed
14 + flag::register --raw
15 + }
16 +
17 + # ============================================
18 + # Help
19 + # ============================================
20 +
21 + function cmd::watch::help() {
22 + cat <<EOF
23 + Usage: wgctl watch [options]
24 +
25 + Live monitor of WireGuard activity.
26 + - Handshakes from connected peers (green)
27 + - Connection attempts from blocked peers (red)
28 + - Firewall drops from restricted peers (red)
29 +
30 + Options:
31 + --name <name> Filter by client name
32 + --type <type> Filter by device type
33 + --blocked Show only blocked peer attempts
34 + --allowed Show only handshakes
35 + --restricted Show only firewall drop events
36 + --raw Show raw IPs without service annotation
37 +
38 + Examples:
39 + wgctl watch
40 + wgctl watch --name phone-nuno
41 + wgctl watch --blocked
42 + wgctl watch --type phone
43 + EOF
44 + }
45 +
46 + # ============================================
47 + # Run
48 + # ============================================
49 +
50 + function cmd::watch::run() {
51 + local filter_name="" filter_type="" filter_peers=""
52 + local blocked_only=false allowed_only=false restricted_only=false
53 + local raw=false
54 +
55 + rm -f /tmp/wgctl_hs_* /tmp/wgctl_attempt_* 2>/dev/null || true
56 +
57 + while [[ $# -gt 0 ]]; do
58 + case "$1" in
59 + --name) filter_name="$2"; shift 2 ;;
60 + --type) filter_type="$2"; shift 2 ;;
61 + --peers) filter_peers="$2"; shift 2 ;;
62 + --blocked) blocked_only=true; shift ;;
63 + --allowed) allowed_only=true; shift ;;
64 + --restricted) restricted_only=true; shift ;;
65 + --raw) raw=true; shift ;;
66 + --help) cmd::watch::help; return ;;
67 + *)
68 + log::error "Unknown flag: $1"
69 + cmd::watch::help
70 + return 1
71 + ;;
72 + esac
73 + done
74 +
75 + local net_file=""
76 + $raw || net_file="$(ctx::net)"
77 +
78 + log::section "wgctl — Live Monitor (Ctrl+C to stop)"
79 + printf "\n"
80 +
81 + # Fixed display widths for watch (dynamic measurement not possible in stream)
82 + local w_client=20 w_dest=18
83 +
84 + # Handshake poller (background)
85 + if ! $blocked_only && ! $restricted_only; then
86 + (
87 + while true; do
88 + cmd::watch::_poll_handshakes \
89 + "$filter_name" "$filter_type" "$filter_peers" "$w_client" "$w_dest"
90 + sleep 5
91 + done
92 + ) &
93 + local poller_pid=$!
94 + fi
95 +
96 + # Event tailer (background)
97 + cmd::watch::_tail_events \
98 + "$filter_name" "$filter_type" "$filter_peers" \
99 + "$blocked_only" "$restricted_only" "$allowed_only" \
100 + "$net_file" "$w_client" "$w_dest" &
101 + local tailer_pid=$!
102 +
103 + trap "kill $tailer_pid ${poller_pid:-} 2>/dev/null; \
104 + rm -f /tmp/wgctl_hs_* /tmp/wgctl_attempt_*; printf '\n'; exit 0" INT TERM
105 +
106 + wait
107 + }
108 +
109 + # ============================================
110 + # Handshake Poller
111 + # ============================================
112 +
113 + function cmd::watch::_poll_handshakes() {
114 + local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
115 + local w_client="${4:-20}" w_dest="${5:-30}"
116 +
117 + local peer_set=()
118 + [[ -n "$filter_peers" ]] && IFS=',' read -ra peer_set <<< "$filter_peers"
119 +
120 + while IFS= read -r line; do
121 + local public_key ts
122 + public_key=$(echo "$line" | awk '{print $1}')
123 + ts=$(echo "$line" | awk '{print $2}')
124 + [[ -z "$ts" || "$ts" == "0" ]] && continue
125 +
126 + # Find client by public key
127 + local client_name=""
128 + for conf in "$(ctx::clients)"/*.conf; do
129 + [[ -f "$conf" ]] || continue
130 + local cname
131 + cname=$(basename "$conf" .conf)
132 + local key
133 + key=$(keys::public "$cname" 2>/dev/null || echo "")
134 + if [[ "$key" == "$public_key" ]]; then
135 + client_name="$cname"
136 + break
137 + fi
138 + done
139 + [[ -z "$client_name" ]] && continue
140 + [[ -n "$filter_name" && "$client_name" != "$filter_name" ]] && continue
141 +
142 + # Dedup — only emit if handshake is new
143 + local safe_key
144 + safe_key=$(echo "$public_key" | md5sum | cut -d' ' -f1)
145 + local prev_ts_file="/tmp/wgctl_hs_${safe_key}"
146 + local prev_ts="0"
147 + [[ -f "$prev_ts_file" ]] && prev_ts=$(cat "$prev_ts_file")
148 + [[ "$ts" == "$prev_ts" ]] && continue
149 + echo "$ts" > "$prev_ts_file"
150 +
151 + local ts_fmt
152 + ts_fmt=$(fmt::datetime_short "$ts")
153 + local endpoint
154 + endpoint=$(monitor::endpoint_for_key "$public_key")
155 +
156 + ui::watch::wg_row "$ts_fmt" "$client_name" "${endpoint:-—}" "handshake" \
157 + "$w_client" "$w_dest"
158 +
159 + done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null)
160 + }
161 +
162 + # ============================================
163 + # Event Tailer
164 + # ============================================
165 +
166 + function cmd::watch::_tail_events() {
167 + local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
168 + local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}"
169 + local net_file="${7:-}" w_client="${8:-20}" w_dest="${9:-30}"
170 +
171 + local peer_set=()
172 + [[ -n "$filter_peers" ]] && IFS=',' read -ra peer_set <<< "$filter_peers"
173 +
174 + # Build ip->name map
175 + declare -A ip_to_name=()
176 + while IFS= read -r conf; do
177 + local cname
178 + cname=$(basename "$conf" .conf)
179 + local ip
180 + ip=$(grep "^Address" "$conf" 2>/dev/null | awk '{print $3}' | cut -d'/' -f1)
181 + [[ -n "$ip" && -n "$cname" ]] && ip_to_name["$ip"]="$cname"
182 + done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
183 +
184 + # Load net services if not --raw
185 + declare -A _svc_cache=()
186 + function _resolve_dest() {
187 + local dest_ip="${1:-}" dest_port="${2:-}" proto="${3:-}"
188 + [[ -z "$net_file" || ! -f "$net_file" ]] && echo "" && return
189 + local key="${dest_ip}:${dest_port}:${proto}"
190 + if [[ -z "${_svc_cache[$key]+x}" ]]; then
191 + _svc_cache[$key]=$(net::reverse_lookup "$dest_ip" "$dest_port" "$proto" 2>/dev/null || true)
192 + fi
193 + echo "${_svc_cache[$key]:-}"
194 + }
195 +
196 + declare -A _WATCH_LAST_FW=()
197 + declare -A _WATCH_LAST_WG=()
198 +
199 + local source_file
200 + source_file=$(mktemp)
201 + echo "wg" > "$source_file"
202 + trap "rm -f '$source_file'" EXIT
203 +
204 + tail -f "$(ctx::events_log)" "$(ctx::fw_events_log)" 2>/dev/null \
205 + | while IFS= read -r line; do
206 + [[ -z "$line" ]] && continue
207 +
208 + if [[ "$line" == "==> "* ]]; then
209 + [[ "$line" == *"fw_events"* ]] && echo "fw" > "$source_file" || echo "wg" > "$source_file"
210 + continue
211 + fi
212 +
213 + local source
214 + source=$(cat "$source_file")
215 +
216 + if [[ "$source" == "fw" ]]; then
217 + $allowed_only && continue
218 +
219 + local fw_data
220 + fw_data=$(python3 "$(ctx::json_helper)" parse_fw_event "$line" 2>/dev/null) || continue
221 + [[ -z "$fw_data" ]] && continue
222 +
223 + local ts src_ip dest_ip dest_port proto
224 + IFS='|' read -r ts src_ip dest_ip dest_port proto <<< "$fw_data"
225 + [[ -z "$src_ip" ]] && continue
226 +
227 + local client="${ip_to_name[$src_ip]:-$src_ip}"
228 + [[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
229 +
230 + # Dedup
231 + local fw_key="${src_ip}:${dest_ip}:${dest_port}:${proto}"
232 + local now; now=$(date +%s)
233 + local window=30
234 + [[ "$proto" == "17" || "$proto" == "udp" ]] && window=10
235 + [[ "$proto" == "1" || "$proto" == "icmp" ]] && window=5
236 + local last="${_WATCH_LAST_FW[$fw_key]:-0}"
237 + (( now - last < window )) && continue
238 + _WATCH_LAST_FW["$fw_key"]="$now"
239 +
240 + local ts_fmt
241 + ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
242 +
243 + local dest_display
244 + dest_display=$(resolve::dest "$dest_ip" "$dest_port" "$proto")
245 +
246 + ui::watch::fw_row "$ts_fmt" "$client" "$dest_display" "$w_client" "$w_dest"
247 +
248 + else
249 + $restricted_only && continue
250 +
251 + local ev_data
252 + ev_data=$(python3 "$(ctx::json_helper)" parse_event "$line" 2>/dev/null) || continue
253 + [[ -z "$ev_data" ]] && continue
254 +
255 + local ts client endpoint event
256 + IFS='|' read -r ts client endpoint event <<< "$ev_data"
257 + [[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
258 + $blocked_only && [[ "$event" != "attempt" ]] && continue
259 + $allowed_only && [[ "$event" != "handshake" ]] && continue
260 +
261 + # Dedup
262 + local wg_key="${client}:${endpoint}:${event}"
263 + local now; now=$(date +%s)
264 + local last="${_WATCH_LAST_WG[$wg_key]:-0}"
265 + (( now - last < 30 )) && continue
266 + _WATCH_LAST_WG["$wg_key"]="$now"
267 +
268 + local ts_fmt
269 + ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
270 +
271 + ui::watch::wg_row "$ts_fmt" "$client" "${endpoint:-—}" "$event" \
272 + "$w_client" "$w_dest"
273 + fi
274 + done
275 +
276 + rm -f "$source_file"
277 + }
Più nuovi Più vecchi