gistfile1.txt
· 5.3 KiB · Text
Brut
function cmd::watch::_poll_handshakes() {
local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
local w_client="${4:-20}" w_dest="${5:-18}"
while IFS= read -r line; do
local public_key ts
public_key=$(echo "$line" | awk '{print $1}')
ts=$(echo "$line" | awk '{print $2}')
[[ -z "$ts" || "$ts" == "0" ]] && continue
local client_name=""
for conf in "$(ctx::clients)"/*.conf; do
[[ -f "$conf" ]] || continue
local cname
cname=$(basename "$conf" .conf)
local key
key=$(keys::public "$cname" 2>/dev/null || echo "")
if [[ "$key" == "$public_key" ]]; then
client_name="$cname"
break
fi
done
[[ -z "$client_name" ]] && continue
[[ -n "$filter_name" && "$client_name" != "$filter_name" ]] && continue
local safe_key
safe_key=$(echo "$public_key" | md5sum | cut -d' ' -f1)
local prev_ts_file="/tmp/wgctl_hs_${safe_key}"
local prev_ts="0"
[[ -f "$prev_ts_file" ]] && prev_ts=$(cat "$prev_ts_file")
[[ "$ts" == "$prev_ts" ]] && continue
local gap=$(( ts - ${prev_ts:-0} ))
echo "$ts" > "$prev_ts_file"
(( gap < ${WG_HANDSHAKE_CHECK_TIME_SEC:-300} )) && continue
local ts_fmt
ts_fmt=$(fmt::datetime_short "$ts")
local endpoint
endpoint=$(monitor::endpoint_for_key "$public_key")
# Resolve endpoint
local endpoint_display
endpoint_display=$(resolve::ip "${endpoint:-}")
[[ -z "$endpoint_display" ]] && endpoint_display="${endpoint:-—}"
ui::watch::wg_row "$ts_fmt" "$client_name" "$endpoint_display" "handshake" \
"$w_client" "$w_dest"
done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null)
}
function cmd::watch::_tail_events() {
local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}"
local w_client="${7:-20}" w_dest="${8:-18}"
# Build ip->name map
declare -A ip_to_name=()
while IFS= read -r conf; do
local cname
cname=$(basename "$conf" .conf)
local ip
ip=$(grep "^Address" "$conf" 2>/dev/null | awk '{print $3}' | cut -d'/' -f1)
[[ -n "$ip" && -n "$cname" ]] && ip_to_name["$ip"]="$cname"
done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
declare -A _WATCH_LAST_FW=()
declare -A _WATCH_LAST_WG=()
local source_file
source_file=$(mktemp)
echo "wg" > "$source_file"
trap "rm -f '$source_file'" EXIT
tail -f "$(ctx::events_log)" "$(ctx::fw_events_log)" 2>/dev/null \
| while IFS= read -r line; do
[[ -z "$line" ]] && continue
if [[ "$line" == "==> "* ]]; then
[[ "$line" == *"fw_events"* ]] && echo "fw" > "$source_file" || echo "wg" > "$source_file"
continue
fi
local source
source=$(cat "$source_file")
if [[ "$source" == "fw" ]]; then
$allowed_only && continue
local fw_data
fw_data=$(python3 "$(ctx::json_helper)" parse_fw_event "$line" 2>/dev/null) || continue
[[ -z "$fw_data" ]] && continue
local ts src_ip dest_ip dest_port proto
IFS='|' read -r ts src_ip dest_ip dest_port proto <<< "$fw_data"
[[ -z "$src_ip" ]] && continue
local client="${ip_to_name[$src_ip]:-$src_ip}"
[[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
local fw_key="${src_ip}:${dest_ip}:${dest_port}:${proto}"
local now; now=$(date +%s)
local window=30
[[ "$proto" == "17" || "$proto" == "udp" ]] && window=10
[[ "$proto" == "1" || "$proto" == "icmp" ]] && window=5
local last="${_WATCH_LAST_FW[$fw_key]:-0}"
(( now - last < window )) && continue
_WATCH_LAST_FW["$fw_key"]="$now"
local ts_fmt
ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
local dest_display
dest_display=$(resolve::dest "$dest_ip" "$dest_port" "$proto")
ui::watch::fw_row "$ts_fmt" "$client" "$dest_display" "$w_client" "$w_dest"
else
$restricted_only && continue
local ev_data
ev_data=$(python3 "$(ctx::json_helper)" parse_event "$line" 2>/dev/null) || continue
[[ -z "$ev_data" ]] && continue
local ts client endpoint event
IFS='|' read -r ts client endpoint event <<< "$ev_data"
[[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
$blocked_only && [[ "$event" != "attempt" ]] && continue
$allowed_only && [[ "$event" != "handshake" ]] && continue
local wg_key="${client}:${endpoint}:${event}"
local now; now=$(date +%s)
local last="${_WATCH_LAST_WG[$wg_key]:-0}"
# Handshakes — only show if gap > 5min (new session)
# Attempts — shorter window (30s) since each attempt is meaningful
local window=30
[[ "$event" == "handshake" ]] && window="${WG_HANDSHAKE_CHECK_TIME_SEC:-300}"
(( now - last < window )) && continue
_WATCH_LAST_WG["$wg_key"]="$now"
local ts_fmt
ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
# Resolve endpoint
local endpoint_display
endpoint_display=$(resolve::ip "${endpoint:-}")
[[ -z "$endpoint_display" ]] && endpoint_display="${endpoint:-—}"
ui::watch::wg_row "$ts_fmt" "$client" "$endpoint_display" "$event" \
"$w_client" "$w_dest"
fi
done
rm -f "$source_file"
}
| 1 | function cmd::watch::_poll_handshakes() { |
| 2 | local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}" |
| 3 | local w_client="${4:-20}" w_dest="${5:-18}" |
| 4 | |
| 5 | while IFS= read -r line; do |
| 6 | local public_key ts |
| 7 | public_key=$(echo "$line" | awk '{print $1}') |
| 8 | ts=$(echo "$line" | awk '{print $2}') |
| 9 | [[ -z "$ts" || "$ts" == "0" ]] && continue |
| 10 | |
| 11 | local client_name="" |
| 12 | for conf in "$(ctx::clients)"/*.conf; do |
| 13 | [[ -f "$conf" ]] || continue |
| 14 | local cname |
| 15 | cname=$(basename "$conf" .conf) |
| 16 | local key |
| 17 | key=$(keys::public "$cname" 2>/dev/null || echo "") |
| 18 | if [[ "$key" == "$public_key" ]]; then |
| 19 | client_name="$cname" |
| 20 | break |
| 21 | fi |
| 22 | done |
| 23 | [[ -z "$client_name" ]] && continue |
| 24 | [[ -n "$filter_name" && "$client_name" != "$filter_name" ]] && continue |
| 25 | |
| 26 | local safe_key |
| 27 | safe_key=$(echo "$public_key" | md5sum | cut -d' ' -f1) |
| 28 | local prev_ts_file="/tmp/wgctl_hs_${safe_key}" |
| 29 | local prev_ts="0" |
| 30 | [[ -f "$prev_ts_file" ]] && prev_ts=$(cat "$prev_ts_file") |
| 31 | [[ "$ts" == "$prev_ts" ]] && continue |
| 32 | |
| 33 | local gap=$(( ts - ${prev_ts:-0} )) |
| 34 | echo "$ts" > "$prev_ts_file" |
| 35 | (( gap < ${WG_HANDSHAKE_CHECK_TIME_SEC:-300} )) && continue |
| 36 | |
| 37 | local ts_fmt |
| 38 | ts_fmt=$(fmt::datetime_short "$ts") |
| 39 | local endpoint |
| 40 | endpoint=$(monitor::endpoint_for_key "$public_key") |
| 41 | |
| 42 | # Resolve endpoint |
| 43 | local endpoint_display |
| 44 | endpoint_display=$(resolve::ip "${endpoint:-}") |
| 45 | [[ -z "$endpoint_display" ]] && endpoint_display="${endpoint:-—}" |
| 46 | |
| 47 | ui::watch::wg_row "$ts_fmt" "$client_name" "$endpoint_display" "handshake" \ |
| 48 | "$w_client" "$w_dest" |
| 49 | |
| 50 | done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null) |
| 51 | } |
| 52 | function cmd::watch::_tail_events() { |
| 53 | local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}" |
| 54 | local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}" |
| 55 | local w_client="${7:-20}" w_dest="${8:-18}" |
| 56 | |
| 57 | # Build ip->name map |
| 58 | declare -A ip_to_name=() |
| 59 | while IFS= read -r conf; do |
| 60 | local cname |
| 61 | cname=$(basename "$conf" .conf) |
| 62 | local ip |
| 63 | ip=$(grep "^Address" "$conf" 2>/dev/null | awk '{print $3}' | cut -d'/' -f1) |
| 64 | [[ -n "$ip" && -n "$cname" ]] && ip_to_name["$ip"]="$cname" |
| 65 | done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null) |
| 66 | |
| 67 | declare -A _WATCH_LAST_FW=() |
| 68 | declare -A _WATCH_LAST_WG=() |
| 69 | |
| 70 | local source_file |
| 71 | source_file=$(mktemp) |
| 72 | echo "wg" > "$source_file" |
| 73 | trap "rm -f '$source_file'" EXIT |
| 74 | |
| 75 | tail -f "$(ctx::events_log)" "$(ctx::fw_events_log)" 2>/dev/null \ |
| 76 | | while IFS= read -r line; do |
| 77 | [[ -z "$line" ]] && continue |
| 78 | |
| 79 | if [[ "$line" == "==> "* ]]; then |
| 80 | [[ "$line" == *"fw_events"* ]] && echo "fw" > "$source_file" || echo "wg" > "$source_file" |
| 81 | continue |
| 82 | fi |
| 83 | |
| 84 | local source |
| 85 | source=$(cat "$source_file") |
| 86 | |
| 87 | if [[ "$source" == "fw" ]]; then |
| 88 | $allowed_only && continue |
| 89 | |
| 90 | local fw_data |
| 91 | fw_data=$(python3 "$(ctx::json_helper)" parse_fw_event "$line" 2>/dev/null) || continue |
| 92 | [[ -z "$fw_data" ]] && continue |
| 93 | |
| 94 | local ts src_ip dest_ip dest_port proto |
| 95 | IFS='|' read -r ts src_ip dest_ip dest_port proto <<< "$fw_data" |
| 96 | [[ -z "$src_ip" ]] && continue |
| 97 | |
| 98 | local client="${ip_to_name[$src_ip]:-$src_ip}" |
| 99 | [[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue |
| 100 | |
| 101 | local fw_key="${src_ip}:${dest_ip}:${dest_port}:${proto}" |
| 102 | local now; now=$(date +%s) |
| 103 | local window=30 |
| 104 | [[ "$proto" == "17" || "$proto" == "udp" ]] && window=10 |
| 105 | [[ "$proto" == "1" || "$proto" == "icmp" ]] && window=5 |
| 106 | local last="${_WATCH_LAST_FW[$fw_key]:-0}" |
| 107 | (( now - last < window )) && continue |
| 108 | _WATCH_LAST_FW["$fw_key"]="$now" |
| 109 | |
| 110 | local ts_fmt |
| 111 | ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)") |
| 112 | |
| 113 | local dest_display |
| 114 | dest_display=$(resolve::dest "$dest_ip" "$dest_port" "$proto") |
| 115 | |
| 116 | ui::watch::fw_row "$ts_fmt" "$client" "$dest_display" "$w_client" "$w_dest" |
| 117 | |
| 118 | else |
| 119 | $restricted_only && continue |
| 120 | |
| 121 | local ev_data |
| 122 | ev_data=$(python3 "$(ctx::json_helper)" parse_event "$line" 2>/dev/null) || continue |
| 123 | [[ -z "$ev_data" ]] && continue |
| 124 | |
| 125 | local ts client endpoint event |
| 126 | IFS='|' read -r ts client endpoint event <<< "$ev_data" |
| 127 | [[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue |
| 128 | $blocked_only && [[ "$event" != "attempt" ]] && continue |
| 129 | $allowed_only && [[ "$event" != "handshake" ]] && continue |
| 130 | |
| 131 | local wg_key="${client}:${endpoint}:${event}" |
| 132 | local now; now=$(date +%s) |
| 133 | local last="${_WATCH_LAST_WG[$wg_key]:-0}" |
| 134 | |
| 135 | # Handshakes — only show if gap > 5min (new session) |
| 136 | # Attempts — shorter window (30s) since each attempt is meaningful |
| 137 | local window=30 |
| 138 | [[ "$event" == "handshake" ]] && window="${WG_HANDSHAKE_CHECK_TIME_SEC:-300}" |
| 139 | |
| 140 | (( now - last < window )) && continue |
| 141 | _WATCH_LAST_WG["$wg_key"]="$now" |
| 142 | |
| 143 | local ts_fmt |
| 144 | ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)") |
| 145 | |
| 146 | # Resolve endpoint |
| 147 | local endpoint_display |
| 148 | endpoint_display=$(resolve::ip "${endpoint:-}") |
| 149 | [[ -z "$endpoint_display" ]] && endpoint_display="${endpoint:-—}" |
| 150 | |
| 151 | ui::watch::wg_row "$ts_fmt" "$client" "$endpoint_display" "$event" \ |
| 152 | "$w_client" "$w_dest" |
| 153 | fi |
| 154 | done |
| 155 | |
| 156 | rm -f "$source_file" |
| 157 | } |