Última atividade 1 month ago

Revisão 7f0637adf5ef3386665381692030e0b36762e06f

gistfile1.txt Bruto
1#!/usr/bin/env bash
2
3# ============================================
4# Client Config
5# ============================================
6
7function peers::create_client_config() {
8 local name="$1"
9 local type="$2"
10 local ip="$3"
11 local allowed_ips="${4:-$(config::allowed_ips_for "$type" "$(config::default_tunnel_for "$type")")}"
12
13 local conf
14 conf="$(ctx::clients)/${name}.conf"
15
16 if [[ -f "$conf" ]]; then
17 log::wg_warning "Client config already exists: ${name}"
18 return 1
19 fi
20
21 local private_key
22 private_key=$(keys::private "$name")
23
24 local server_public_key
25 server_public_key=$(config::server_public_key)
26
27 cat > "$conf" <<EOF
28[Interface]
29PrivateKey = ${private_key}
30Address = ${ip}/32
31DNS = $(config::dns)
32
33[Peer]
34PublicKey = ${server_public_key}
35Endpoint = $(config::endpoint)
36AllowedIPs = ${allowed_ips}
37PersistentKeepalive = 25
38EOF
39
40 chmod 600 "$conf"
41 log::debug "Created client config: ${name} (${ip})"
42}
43
44function peers::remove_client_config() {
45 local name="$1"
46 local conf
47 conf="$(ctx::clients)/${name}.conf"
48
49 if [[ ! -f "$conf" ]]; then
50 log::wg_warning "Client config not found: ${name}"
51 return 1
52 fi
53
54 rm -f "$conf"
55 log::debug "Removed client config: ${name}"
56}
57
58# ============================================
59# Server Config (wg0.conf) Peer Management
60# ============================================
61
62function peers::cleanup_config() {
63 json::cleanup_config "$(config::config_file)"
64}
65
66
67function peers::add_to_server() {
68 local name="${1:?name required}"
69 local public_key="${2:?public_key required}"
70 local ip="${3:?ip required}"
71
72 local config
73 config=$(config::config_file)
74
75 cat >> "$config" <<EOF
76
77[Peer]
78# ${name}
79PublicKey = ${public_key}
80AllowedIPs = ${ip}/32
81EOF
82
83 log::debug "Added peer to server config: ${name}"
84}
85
86function peers::remove_block() {
87 local name="${1:?name required}"
88 json::remove_peer_block "$(config::config_file)" "$name"
89}
90
91function peers::remove_from_server() {
92 local name="${1:?name required}"
93 peers::remove_block "$name"
94 peers::cleanup_config
95 log::debug "Removed peer from server config: ${name}"
96}
97
98# ============================================
99# Listing
100# ============================================
101
102function peers::list() {
103 local dir
104 dir="$(ctx::clients)"
105
106 if [[ -z "$(ls -A "$dir"/*.conf 2>/dev/null)" ]]; then
107 log::wg_list "No clients configured"
108 return 0
109 fi
110
111 for conf in "${dir}"/*.conf; do
112 local client_name
113 client_name=$(basename "$conf" .conf)
114
115 local ip
116 ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)
117
118 local public_key
119 public_key=$(keys::public "$client_name" 2>/dev/null || echo "unknown")
120
121 # Determine type from IP
122 local type="unknown"
123 for t in $(config::device_types); do
124 local subnet
125 subnet=$(config::subnet_for "$t")
126 if string::starts_with "$ip" "$subnet"; then
127 type="$t"
128 break
129 fi
130 done
131
132 printf " %-30s %-15s %-10s %s\n" \
133 "$client_name" "$ip" "$type" "$public_key"
134 done
135}
136
137function peers::list_by_type() {
138 local filter_type="$1"
139 local dir
140 dir="$(ctx::clients)"
141
142 for conf in "${dir}"/*.conf; do
143 local client_name
144 client_name=$(basename "$conf" .conf)
145
146 local ip
147 ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)
148
149 local subnet
150 subnet=$(config::subnet_for "$filter_type")
151
152 if string::starts_with "$ip" "$subnet"; then
153 printf " %-30s %-15s\n" "$client_name" "$ip"
154 fi
155 done
156}
157
158function peers::exists_in_server() {
159 local name="$1"
160 grep -q "^# ${name}$" "$(config::config_file)"
161}
162
163# function peers::is_blocked() {
164# local name="${1:-}"
165# peers::exists_in_server "$name" && return 1 || return 0
166# }
167
168function peers::is_blocked() {
169 local name="${1:-}"
170 block::is_blocked "$name"
171}
172
173function peers::is_restricted() {
174 local name="${1:-}"
175 block::has_specific_rules "$name" 2>/dev/null
176}
177
178# ============================================
179# Default Rule
180# ============================================
181
182function peers::get_type() {
183 local name="$1"
184 local ip
185 ip=$(peers::get_ip "$name")
186 [[ -z "$ip" ]] && echo "unknown" && return 0
187 peers::get_type_from_ip "$ip"
188}
189
190function peers::default_rule() {
191 echo "user"
192}
193
194function peers::effective_rule() {
195 local name="$1"
196 local rule
197 rule=$(peers::get_meta "$name" "rule")
198 echo "${rule:---}"
199}
200
201# ============================================
202# Query
203# ============================================
204
205function peers::all() {
206 local dir
207 dir="$(ctx::clients)"
208 for conf in "${dir}"/*.conf; do
209 [[ -f "$conf" ]] || continue
210 basename "$conf" .conf
211 done
212}
213
214function peers::with_rule() {
215 local rule="$1"
216 while IFS= read -r name; do
217 local effective
218 effective=$(peers::effective_rule "$name")
219 [[ "$effective" == "$rule" ]] && echo "$name"
220 done < <(peers::all)
221}
222
223function peers::get_ip() {
224 local name="$1"
225 grep "^Address" "$(ctx::clients)/${name}.conf" 2>/dev/null \
226 | awk '{print $3}' | cut -d'/' -f1 || true
227}
228
229function peers::find_by_ip() {
230 local target_ip="$1"
231 while IFS= read -r name; do
232 local ip
233 ip=$(peers::get_ip "$name")
234 [[ "$ip" == "$target_ip" ]] && echo "$name" && return 0
235 done < <(peers::all)
236}
237
238function peers::is_connected() {
239 local handshake_ts="${1:-0}"
240 local now diff threshold
241 now=$(date +%s)
242 threshold=$(config::handshake_time_sec)
243 [[ "$handshake_ts" == "0" || -z "$handshake_ts" ]] && return 1
244 diff=$(( now - handshake_ts ))
245 (( diff < threshold ))
246}
247
248function peers::is_attempting() {
249 local last_ts="${1:-}"
250 [[ -z "$last_ts" ]] && return 1
251 local now attempt_ts diff threshold
252 now=$(date +%s)
253 threshold=$(config::handshake_time_sec)
254 attempt_ts=$(json::iso_to_ts "$last_ts")
255 [[ -z "$attempt_ts" || "$attempt_ts" == "0" ]] && return 1
256 diff=$(( now - attempt_ts ))
257 (( diff < threshold ))
258}
259
260function peers::is_online() {
261 local name="${1:-}" handshake_ts="${2:-0}" last_ts="${3:-}"
262 local is_blocked
263 peers::is_blocked "$name" && is_blocked="true" || is_blocked="false"
264 if [[ "$is_blocked" == "true" ]]; then
265 peers::is_attempting "$last_ts"
266 else
267 peers::is_connected "$handshake_ts"
268 fi
269}
270
271function peers::is_offline() {
272 local name="${1:-}" handshake_ts="${2:-0}" last_ts="${3:-}"
273 peers::is_online "$name" "$handshake_ts" "$last_ts" && return 1 || return 0
274}
275
276function peers::get_main_group() {
277 local name="${1:?}"
278 peers::get_meta "$name" "main_group"
279}
280
281function peers::set_main_group() {
282 local name="${1:?}" group="${2:?}"
283 peers::set_meta "$name" "main_group" "$group"
284}
285
286# ============================================
287# Name + Type Parsing
288# ============================================
289
290function peers::resolve_name() {
291 local name="$1"
292 local type="${2:-}"
293
294 if [[ -n "$type" ]]; then
295 if ! config::is_valid_type "$type"; then
296 log::error "Invalid device type: ${type}"
297 return 1
298 fi
299 echo "${type}-${name}"
300 else
301 echo "$name"
302 fi
303}
304
305function peers::require_exists() {
306 local name="$1"
307 if [[ ! -f "$(ctx::clients)/${name}.conf" ]]; then
308 log::error "Client not found: ${name}"
309 return 1
310 fi
311}
312
313function peers::resolve_and_require() {
314 local name="$1"
315 local type="${2:-}"
316
317 local resolved
318 resolved=$(peers::resolve_name "$name" "$type") || return 1
319 peers::require_exists "$resolved" || return 1
320 echo "$resolved"
321}
322
323# ============================================
324# Cleanup
325# ============================================
326
327function peers::purge() {
328 local name="${1:-}" client_ip="${2:-}" was_blocked="${3:-false}"
329
330 [[ -n "$client_ip" ]] && fw::flush_peer "$client_ip"
331 peers::remove_from_server "$name" || return 1
332 peers::remove_client_config "$name" || return 1
333 keys::remove "$name" || return 1
334 group::remove_peer_from_all "$name" || return 1
335
336 if [[ -n "$client_ip" ]] && $was_blocked; then
337 fw::unblock_all "$client_ip"
338 fi
339
340 block::remove_file "$name" 2>/dev/null || true
341 peers::remove_meta "$name" 2>/dev/null || true
342 peers::reload || return 1
343}
344
345
346# ============================================
347# Display / Formatting
348# ============================================
349
350function peers::format_last_seen() {
351 local name="${1:-}" pubkey="${2:-}" is_blocked="${3:-false}"
352 local last_ts="${4:-}" last_evt="${5:-}" handshake_ts="${6:-0}"
353
354 local data
355 data=$(peers::last_seen_data "$is_blocked" "$last_ts" "$handshake_ts")
356
357 local ts type
358 IFS="|" read -r ts type <<< "$data"
359
360 case "$type" in
361 none) echo "—" ;;
362 dropped) echo "$(fmt::datetime_iso "$ts") (dropped)" ;;
363 handshake) echo "$(fmt::datetime "$ts") (handshake)" ;;
364 esac
365}
366
367# function peers::format_status() {
368# local name="${1:-}" public_key="${2:-}" is_blocked="${3:-false}"
369# local is_restricted="${4:-false}" handshake_ts="${5:-0}" last_ts="${6:-}"
370
371# local state
372# state=$(peers::connection_state "$is_blocked" "$is_restricted" \
373# "$handshake_ts" "$last_ts")
374
375# local conn_str modifier color
376# IFS="|" read -r conn_str modifier color <<< "$state"
377
378# local display="$conn_str"
379# [[ -n "$modifier" ]] && display="${conn_str} (${modifier})"
380# echo -e "${color}${display}\033[0m"
381# }
382
383function peers::format_status() {
384 local name="${1:-}" public_key="${2:-}" is_blocked="${3:-false}"
385 local is_restricted="${4:-false}" handshake_ts="${5:-0}" last_ts="${6:-}"
386
387 local state
388 state=$(peers::connection_state "$is_blocked" "$is_restricted" \
389 "$handshake_ts" "$last_ts")
390
391 local conn_str modifier color
392 IFS="|" read -r conn_str modifier color <<< "$state"
393
394 # Color based on state — modifier overrides base connection color
395 if [[ "$is_blocked" == "true" ]]; then
396 color="\033[1;31m" # red — blocked
397 elif [[ "$is_restricted" == "true" ]]; then
398 color="\033[1;33m" # yellow — restricted
399 elif [[ "$conn_str" == "online" ]]; then
400 color="\033[1;32m" # green — online
401 else
402 color="\033[0;37m" # gray — offline
403 fi
404
405 local conn_str_padded
406 conn_str_padded=$(printf "%-7s" "$conn_str")
407 echo -e "${color}${conn_str_padded}\033[0m"
408}
409
410# Inspect — verbose, color + descriptive text
411function peers::format_status_verbose() {
412 local name="${1:-}" public_key="${2:-}" is_blocked="${3:-false}"
413 local is_restricted="${4:-false}" handshake_ts="${5:-0}" last_ts="${6:-}"
414
415 local conn_str
416 local state
417 state=$(peers::connection_state "$is_blocked" "$is_restricted" \
418 "$handshake_ts" "$last_ts")
419 IFS="|" read -r conn_str _ _ <<< "$state"
420
421 local color suffix=""
422 if [[ "$is_blocked" == "true" ]]; then
423 color="\033[1;31m"
424 suffix=" (blocked)"
425 elif [[ "$is_restricted" == "true" ]]; then
426 color="\033[1;33m"
427 suffix=" (restricted)"
428 elif [[ "$conn_str" == "online" ]]; then
429 color="\033[1;32m"
430 else
431 color="\033[0;37m"
432 fi
433
434 echo -e "${color}${conn_str}${suffix}\033[0m"
435}
436
437function peers::display_type() {
438 local type="${1:-}" _subtype="${2:-}"
439 echo "${type:-unknown}"
440}
441
442# ============================================
443# Connection data
444# ============================================
445
446# Data functions — return raw values
447function peers::connection_state() {
448 # Returns: connected|modifier|color_code
449 local is_blocked="${1:-false}" is_restricted="${2:-false}"
450 local handshake_ts="${3:-0}" last_ts="${4:-}"
451 local threshold
452 threshold=$(config::handshake_time_sec)
453 local now
454 now=$(date +%s)
455 local connected=false modifier="" color
456
457 if [[ "$is_blocked" == "true" ]]; then
458 local attempt_ts diff
459 attempt_ts=$(json::iso_to_ts "${last_ts:-0}")
460 diff=$(( now - attempt_ts ))
461 (( diff < threshold )) && connected=true
462 modifier="blocked"
463 color="\033[1;31m"
464 elif [[ "$is_restricted" == "true" ]]; then
465 local diff=$(( now - handshake_ts ))
466 (( diff < threshold )) && connected=true
467 modifier="restricted"
468 color="\033[1;33m"
469 else
470 local diff=$(( now - handshake_ts ))
471 (( diff < threshold )) && connected=true
472 $connected && color="\033[1;32m" || color="\033[0;37m"
473 fi
474
475 $connected && echo "online|${modifier}|${color}" || echo "offline|${modifier}|${color}"
476}
477
478function peers::last_seen_data() {
479 # Returns: timestamp|type (dropped|handshake|none)
480 local is_blocked="${1:-false}" last_ts="${2:-}" handshake_ts="${3:-0}"
481
482 if [[ "$is_blocked" == "true" ]]; then
483 if [[ -n "$last_ts" && "$last_ts" != "0" && "$last_ts" != "null" ]]; then
484 echo "${last_ts}|dropped"
485 else
486 echo "|none"
487 fi
488 else
489 if [[ -z "$handshake_ts" || "$handshake_ts" == "0" ]]; then
490 echo "|none"
491 else
492 echo "${handshake_ts}|handshake"
493 fi
494 fi
495}
496
497function peers::get_type_from_ip() {
498 local ip="${1:-}"
499 [[ -z "$ip" ]] && echo "unknown" && return 0
500 subnet::type_from_ip "$ip"
501}
502
503
504# ============================================
505# Activity
506# ============================================
507
508# Returns: level|rx|tx
509function peers::activity_total() {
510 local pubkey="${1:-}"
511 json::peer_transfer "$(config::interface)" | grep "^${pubkey}" | head -1 | cut -d'|' -f2-
512}
513
514# Returns: level|rx_rate|tx_rate
515function peers::activity_current() {
516 local pubkey="${1:-}"
517 json::peer_transfer_delta "$(config::interface)" \
518 "$(ctx::daemon)/transfer_cache.json" \
519 | grep "^${pubkey}" | head -1 | cut -d'|' -f2-
520}
521
522function peers::format_activity_total() {
523 local pubkey="${1:-}"
524 local data
525 data=$(peers::activity_total "$pubkey")
526 [[ -z "$data" ]] && echo "—" && return 0
527 local level rx tx rx_hr tx_hr
528 IFS="|" read -r rx tx level <<< "$data"
529 rx_hr=$(numfmt --to=iec "${rx:-0}" 2>/dev/null || echo "0B")
530 tx_hr=$(numfmt --to=iec "${tx:-0}" 2>/dev/null || echo "0B")
531 echo "${level:-none} (↓${rx_hr} ↑${tx_hr})"
532}
533
534function peers::format_activity_current() {
535 local pubkey="${1:-}"
536 local data
537 data=$(peers::activity_current "$pubkey")
538 [[ -z "$data" ]] && echo "—" && return 0
539 local level rx_rate tx_rate rx_hr tx_hr
540 IFS="|" read -r rx_rate tx_rate level <<< "$data"
541 [[ "$level" == "unknown" ]] && echo "sampling..." && return 0
542 local rx_hr tx_hr
543 rx_hr=$(numfmt --to=iec "${rx_rate:-0}" 2>/dev/null || echo "${rx_rate:-0}")
544 tx_hr=$(numfmt --to=iec "${tx_rate:-0}" 2>/dev/null || echo "${tx_rate:-0}")
545 echo "${level} (↓${rx_hr}B/s ↑${tx_hr}B/s)"
546}
547
548# ============================================
549# Helpers - Meta File
550# ============================================
551
552function peers::meta_path() {
553 local name="$1"
554 echo "$(ctx::meta)/${name}.meta"
555}
556
557function peers::get_meta() {
558 local name="$1" key="$2"
559 json::get "$(peers::meta_path "$name")" "$key"
560}
561
562function peers::set_meta() {
563 local name="$1" key="$2" value="$3"
564 json::set "$(peers::meta_path "$name")" "$key" "$value"
565}
566
567function peers::remove_meta() {
568 local name="$1"
569 rm -f "$(peers::meta_path "$name")"
570}
571
572# ============================================
573# Live Reload
574# ============================================
575
576function peers::reload() {
577 wg syncconf "$(config::interface)" <(wg-quick strip "$(config::interface)")
578 log::debug "WireGuard config reloaded"
579}
580