Última atividade 1 month ago

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 "split")}"
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 local type
122 type=$(peers::get_meta "$client_name" "type" 2>/dev/null)
123 [[ -z "$type" ]] && type=$(peers::get_type_from_ip "$ip")
124
125 printf " %-30s %-15s %-10s %s\n" \
126 "$client_name" "$ip" "$type" "$public_key"
127 done
128}
129
130function peers::list_by_type() {
131 local filter_type="$1"
132 local dir
133 dir="$(ctx::clients)"
134
135 for conf in "${dir}"/*.conf; do
136 local client_name
137 client_name=$(basename "$conf" .conf)
138
139 local ip
140 ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)
141
142 local type
143 type=$(peers::get_meta "$client_name" "type" 2>/dev/null)
144 [[ -z "$type" ]] && type=$(peers::get_type_from_ip "$ip")
145
146 [[ "$type" == "$filter_type" ]] && \
147 printf " %-30s %-15s\n" "$client_name" "$ip"
148 done
149}
150
151function peers::exists_in_server() {
152 local name="$1"
153 grep -q "^# ${name}$" "$(config::config_file)"
154}
155
156function peers::is_blocked() {
157 local name="${1:-}"
158 block::is_blocked "$name"
159}
160
161function peers::is_restricted() {
162 local name="${1:-}"
163 block::has_specific_rules "$name" 2>/dev/null
164}
165
166# ============================================
167# Default Rule
168# ============================================
169
170function peers::get_type() {
171 local name="$1"
172 local ip
173 ip=$(peers::get_ip "$name")
174 [[ -z "$ip" ]] && echo "unknown" && return 0
175 peers::get_type_from_ip "$ip"
176}
177
178function peers::default_rule() {
179 echo "user"
180}
181
182function peers::effective_rule() {
183 local name="$1"
184 local rule
185 rule=$(peers::get_meta "$name" "rule")
186 echo "${rule:---}"
187}
188
189# ============================================
190# Query
191# ============================================
192
193function peers::all() {
194 local dir
195 dir="$(ctx::clients)"
196 for conf in "${dir}"/*.conf; do
197 [[ -f "$conf" ]] || continue
198 basename "$conf" .conf
199 done
200}
201
202function peers::with_rule() {
203 local rule="$1"
204 while IFS= read -r name; do
205 local effective
206 effective=$(peers::effective_rule "$name")
207 [[ "$effective" == "$rule" ]] && echo "$name"
208 done < <(peers::all)
209}
210
211function peers::get_ip() {
212 local name="$1"
213 grep "^Address" "$(ctx::clients)/${name}.conf" 2>/dev/null \
214 | awk '{print $3}' | cut -d'/' -f1 || true
215}
216
217function peers::find_by_ip() {
218 local target_ip="$1"
219 while IFS= read -r name; do
220 local ip
221 ip=$(peers::get_ip "$name")
222 [[ "$ip" == "$target_ip" ]] && echo "$name" && return 0
223 done < <(peers::all)
224}
225
226function peers::is_connected() {
227 local handshake_ts="${1:-0}"
228 local now diff threshold
229 now=$(date +%s)
230 threshold=$(config::handshake_time_sec)
231 [[ "$handshake_ts" == "0" || -z "$handshake_ts" ]] && return 1
232 diff=$(( now - handshake_ts ))
233 (( diff < threshold ))
234}
235
236function peers::is_attempting() {
237 local last_ts="${1:-}"
238 [[ -z "$last_ts" ]] && return 1
239 local now attempt_ts diff threshold
240 now=$(date +%s)
241 threshold=$(config::handshake_time_sec)
242 attempt_ts=$(json::iso_to_ts "$last_ts")
243 [[ -z "$attempt_ts" || "$attempt_ts" == "0" ]] && return 1
244 diff=$(( now - attempt_ts ))
245 (( diff < threshold ))
246}
247
248function peers::is_online() {
249 local name="${1:-}" handshake_ts="${2:-0}" last_ts="${3:-}"
250 local is_blocked
251 peers::is_blocked "$name" && is_blocked="true" || is_blocked="false"
252 if [[ "$is_blocked" == "true" ]]; then
253 peers::is_attempting "$last_ts"
254 else
255 peers::is_connected "$handshake_ts"
256 fi
257}
258
259function peers::is_offline() {
260 local name="${1:-}" handshake_ts="${2:-0}" last_ts="${3:-}"
261 peers::is_online "$name" "$handshake_ts" "$last_ts" && return 1 || return 0
262}
263
264function peers::get_main_group() {
265 local name="${1:?}"
266 peers::get_meta "$name" "main_group"
267}
268
269function peers::set_main_group() {
270 local name="${1:?}" group="${2:?}"
271 peers::set_meta "$name" "main_group" "$group"
272}
273
274# ============================================
275# Name + Type Parsing
276# ============================================
277
278function peers::resolve_name() {
279 local name="$1"
280 local type="${2:-}"
281
282 if [[ -n "$type" ]]; then
283 if ! subnet::exists "$type"; then
284 log::error "Invalid device type: ${type}"
285 return 1
286 fi
287 echo "${type}-${name}"
288 else
289 echo "$name"
290 fi
291}
292
293function peers::require_exists() {
294 local name="$1"
295 if [[ ! -f "$(ctx::clients)/${name}.conf" ]]; then
296 log::error "Client not found: ${name}"
297 return 1
298 fi
299}
300
301function peers::resolve_and_require() {
302 local name="$1"
303 local type="${2:-}"
304
305 local resolved
306 resolved=$(peers::resolve_name "$name" "$type") || return 1
307 peers::require_exists "$resolved" || return 1
308 echo "$resolved"
309}
310
311function peers::rename_meta() {
312 local name="${1:-}" new_name="${2:-}"
313 local old_meta new_meta
314 old_meta=$(peers::meta_path "$name")
315 new_meta=$(peers::meta_path "$new_name")
316 [[ -f "$old_meta" ]] && mv "$old_meta" "$new_meta"
317 return 0
318}
319
320# ============================================
321# Cleanup
322# ============================================
323
324function peers::purge() {
325 local name="${1:-}" client_ip="${2:-}" was_blocked="${3:-false}"
326
327 [[ -n "$client_ip" ]] && fw::flush_peer "$client_ip"
328 peers::remove_from_server "$name" || return 1
329 peers::remove_client_config "$name" || return 1
330 keys::remove "$name" || return 1
331 group::remove_peer_from_all "$name" || return 1
332
333 if [[ -n "$client_ip" ]] && $was_blocked; then
334 fw::unblock_all "$client_ip"
335 fi
336
337 block::remove_file "$name" 2>/dev/null || true
338 peers::remove_meta "$name" 2>/dev/null || true
339 peers::reload || return 1
340}
341
342
343# ============================================
344# Display / Formatting
345# ============================================
346
347function peers::format_last_seen() {
348 local name="${1:-}" pubkey="${2:-}" is_blocked="${3:-false}"
349 local last_ts="${4:-}" last_evt="${5:-}" handshake_ts="${6:-0}"
350
351 local data
352 data=$(peers::last_seen_data "$is_blocked" "$last_ts" "$handshake_ts")
353
354 local ts type
355 IFS="|" read -r ts type <<< "$data"
356
357 case "$type" in
358 none) echo "—" ;;
359 dropped) echo "$(fmt::datetime_iso "$ts") (dropped)" ;;
360 handshake) echo "$(fmt::datetime "$ts") (handshake)" ;;
361 esac
362}
363
364function peers::format_status() {
365 local name="${1:-}" public_key="${2:-}" is_blocked="${3:-false}"
366 local is_restricted="${4:-false}" handshake_ts="${5:-0}" last_ts="${6:-}"
367
368 local state
369 state=$(peers::connection_state "$is_blocked" "$is_restricted" \
370 "$handshake_ts" "$last_ts")
371
372 local conn_str modifier color
373 IFS="|" read -r conn_str modifier color <<< "$state"
374
375 # Color based on state — modifier overrides base connection color
376 if [[ "$is_blocked" == "true" ]]; then
377 color="\033[1;31m" # red — blocked
378 elif [[ "$is_restricted" == "true" ]]; then
379 color="\033[1;33m" # yellow — restricted
380 elif [[ "$conn_str" == "online" ]]; then
381 color="\033[1;32m" # green — online
382 else
383 color="\033[0;37m" # gray — offline
384 fi
385
386 local conn_str_padded
387 conn_str_padded=$(printf "%-7s" "$conn_str")
388 echo -e "${color}${conn_str_padded}\033[0m"
389}
390
391# Inspect — verbose, color + descriptive text
392function peers::format_status_verbose() {
393 local name="${1:-}" public_key="${2:-}" is_blocked="${3:-false}"
394 local is_restricted="${4:-false}" handshake_ts="${5:-0}" last_ts="${6:-}"
395
396 local conn_str
397 local state
398 state=$(peers::connection_state "$is_blocked" "$is_restricted" \
399 "$handshake_ts" "$last_ts")
400 IFS="|" read -r conn_str _ _ <<< "$state"
401
402 local color suffix=""
403 if [[ "$is_blocked" == "true" ]]; then
404 color="\033[1;31m"
405 suffix=" (blocked)"
406 elif [[ "$is_restricted" == "true" ]]; then
407 color="\033[1;33m"
408 suffix=" (restricted)"
409 elif [[ "$conn_str" == "online" ]]; then
410 color="\033[1;32m"
411 else
412 color="\033[0;37m"
413 fi
414
415 echo -e "${color}${conn_str}${suffix}\033[0m"
416}
417
418function peers::display_type() {
419 local type="${1:-}" _subtype="${2:-}"
420 echo "${type:-unknown}"
421}
422
423# ============================================
424# Connection data
425# ============================================
426
427# Data functions — return raw values
428function peers::connection_state() {
429 # Returns: connected|modifier|color_code
430 local is_blocked="${1:-false}" is_restricted="${2:-false}"
431 local handshake_ts="${3:-0}" last_ts="${4:-}"
432 local threshold
433 threshold=$(config::handshake_time_sec)
434 local now
435 now=$(date +%s)
436 local connected=false modifier="" color
437
438 if [[ "$is_blocked" == "true" ]]; then
439 local attempt_ts diff
440 attempt_ts=$(json::iso_to_ts "${last_ts:-0}")
441 diff=$(( now - attempt_ts ))
442 (( diff < threshold )) && connected=true
443 modifier="blocked"
444 color="\033[1;31m"
445 elif [[ "$is_restricted" == "true" ]]; then
446 local diff=$(( now - handshake_ts ))
447 (( diff < threshold )) && connected=true
448 modifier="restricted"
449 color="\033[1;33m"
450 else
451 local diff=$(( now - handshake_ts ))
452 (( diff < threshold )) && connected=true
453 $connected && color="\033[1;32m" || color="\033[0;37m"
454 fi
455
456 $connected && echo "online|${modifier}|${color}" || echo "offline|${modifier}|${color}"
457}
458
459function peers::last_seen_data() {
460 # Returns: timestamp|type (dropped|handshake|none)
461 local is_blocked="${1:-false}" last_ts="${2:-}" handshake_ts="${3:-0}"
462
463 if [[ "$is_blocked" == "true" ]]; then
464 if [[ -n "$last_ts" && "$last_ts" != "0" && "$last_ts" != "null" ]]; then
465 echo "${last_ts}|dropped"
466 else
467 echo "|none"
468 fi
469 else
470 if [[ -z "$handshake_ts" || "$handshake_ts" == "0" ]]; then
471 echo "|none"
472 else
473 echo "${handshake_ts}|handshake"
474 fi
475 fi
476}
477
478function peers::get_type_from_ip() {
479 local ip="${1:-}"
480 [[ -z "$ip" ]] && echo "unknown" && return 0
481 subnet::type_from_ip "$ip"
482}
483
484
485# ============================================
486# Activity
487# ============================================
488
489# Returns: level|rx|tx
490function peers::activity_total() {
491 local pubkey="${1:-}"
492 json::peer_transfer "$(config::interface)" | grep "^${pubkey}" | head -1 | cut -d'|' -f2-
493}
494
495# Returns: level|rx_rate|tx_rate
496function peers::activity_current() {
497 local pubkey="${1:-}"
498 json::peer_transfer_delta "$(config::interface)" \
499 "$(ctx::daemon)/transfer_cache.json" \
500 | grep "^${pubkey}" | head -1 | cut -d'|' -f2-
501}
502
503function peers::format_activity_total() {
504 local pubkey="${1:-}"
505 local data
506 data=$(peers::activity_total "$pubkey")
507 [[ -z "$data" ]] && echo "—" && return 0
508 local level rx tx rx_hr tx_hr
509 IFS="|" read -r rx tx level <<< "$data"
510 rx_hr=$(numfmt --to=iec "${rx:-0}" 2>/dev/null || echo "0B")
511 tx_hr=$(numfmt --to=iec "${tx:-0}" 2>/dev/null || echo "0B")
512 echo "${level:-none} (↓${rx_hr} ↑${tx_hr})"
513}
514
515function peers::format_activity_current() {
516 local pubkey="${1:-}"
517 local data
518 data=$(peers::activity_current "$pubkey")
519 [[ -z "$data" ]] && echo "—" && return 0
520 local level rx_rate tx_rate rx_hr tx_hr
521 IFS="|" read -r rx_rate tx_rate level <<< "$data"
522 [[ "$level" == "unknown" ]] && echo "sampling..." && return 0
523 local rx_hr tx_hr
524 rx_hr=$(numfmt --to=iec "${rx_rate:-0}" 2>/dev/null || echo "${rx_rate:-0}")
525 tx_hr=$(numfmt --to=iec "${tx_rate:-0}" 2>/dev/null || echo "${tx_rate:-0}")
526 echo "${level} (↓${rx_hr}B/s ↑${tx_hr}B/s)"
527}
528
529# ============================================
530# Helpers - Meta File
531# ============================================
532
533function peers::meta_path() {
534 local name="$1"
535 echo "$(ctx::meta)/${name}.meta"
536}
537
538function peers::get_meta() {
539 local name="$1" key="$2"
540 json::get "$(peers::meta_path "$name")" "$key"
541}
542
543function peers::set_meta() {
544 local name="$1" key="$2" value="$3"
545 json::set "$(peers::meta_path "$name")" "$key" "$value"
546}
547
548function peers::remove_meta() {
549 local name="$1"
550 rm -f "$(peers::meta_path "$name")"
551}
552
553# ============================================
554# Live Reload
555# ============================================
556
557function peers::reload() {
558 wg syncconf "$(config::interface)" <(wg-quick strip "$(config::interface)")
559 log::debug "WireGuard config reloaded"
560}
561