wgctl/commands/inspect.command.sh

226 lines
No EOL
5.9 KiB
Bash

#!/usr/bin/env bash
function cmd::inspect::on_load() {
flag::register --name
flag::register --type
flag::register --config
flag::register --qr
}
function cmd::inspect::help() {
cat <<EOF
Usage: wgctl inspect --name <name> [--type <type>]
wgctl inspect <full-name>
Show detailed information for a single client.
Options:
--name <name> Client name
--type <type> Device type (optional, combines with --name)
--config Show raw client config
--qr Show QR code
Examples:
wgctl inspect --name phone-nuno
wgctl inspect --name nuno --type phone
wgctl inspect --name phone-nuno --config
wgctl inspect --name phone-nuno --qr
EOF
}
# ============================================
# Private helpers
# ============================================
function cmd::inspect::_section() {
local title="$1"
printf "\n \033[0;37m── %s ──────────────────────────────────\033[0m\n" "$title"
}
function cmd::inspect::_row() {
local label="$1" value="$2"
printf " %-20s %s\n" "${label}:" "$value"
}
function cmd::inspect::_peer_info() {
local name="$1"
local ip type rule status endpoint last_seen tunnel public_key
ip=$(peers::get_ip "$name")
type=$(peers::get_type "$name")
rule=$(peers::get_meta "$name" "rule")
public_key=$(keys::public "$name" 2>/dev/null)
# Status + endpoint + last seen — reuse list helpers
status=$(cmd::list::format_status "$name" "$public_key" "$ip")
last_seen=$(cmd::list::format_last_seen "$name" "$public_key" "$ip")
endpoint=$(monitor::get_cached_endpoint "$name")
# Tunnel mode from AllowedIPs in conf
local allowed_ips
allowed_ips=$(grep "^AllowedIPs" "$(ctx::clients)/${name}.conf" 2>/dev/null | cut -d'=' -f2- | xargs)
cmd::inspect::_section "Client"
cmd::inspect::_row "Name" "$name"
cmd::inspect::_row "IP" "$ip"
cmd::inspect::_row "Type" "$(cmd::list::display_type "$name" "$type")"
cmd::inspect::_row "Rule" "${rule:-}"
cmd::inspect::_row "Status" "$(echo -e "$status")"
cmd::inspect::_row "Endpoint" "${endpoint:-}"
cmd::inspect::_row "Last seen" "$last_seen"
cmd::inspect::_row "AllowedIPs" "$allowed_ips"
cmd::inspect::_row "Public key" "${public_key:-}"
}
function cmd::inspect::_rule_info() {
local name="$1"
local rule
rule=$(peers::get_meta "$name" "rule")
[[ -z "$rule" ]] && return 0
rule::exists "$rule" || return 0
local rule_file
rule_file="$(ctx::rule::path "${rule}.rule")"
ui::section "Rule: ${rule}"
local desc dns_redirect
desc=$(json::get "$rule_file" "desc")
dns_redirect=$(json::get "$rule_file" "dns_redirect")
ui::row "Description" "${desc:-}"
ui::row "DNS Redirect" "${dns_redirect:-false}"
local allow_ports allow_ips block_ips block_ports
allow_ports=$(json::get "$rule_file" "allow_ports")
allow_ips=$(json::get "$rule_file" "allow_ips")
block_ips=$(json::get "$rule_file" "block_ips")
block_ports=$(json::get "$rule_file" "block_ports")
if [[ -n "$allow_ports" || -n "$allow_ips" ]]; then
printf " %-20s\n" "Allows:"
ui::print_list "+" "$allow_ports"
ui::print_list "+" "$allow_ips"
else
ui::row "Allows" "—"
fi
if [[ -n "$block_ips" || -n "$block_ports" ]]; then
printf " %-20s\n" "Blocks:"
ui::print_list "-" "$block_ips"
ui::print_list "-" "$block_ports"
else
ui::row "Blocks" "— (full access)"
fi
}
function cmd::inspect::_group_info() {
local name="$1"
ui::section "Groups"
local groups=()
mapfile -t groups < <(json::peer_groups "$(ctx::groups)" "$name")
if [[ ${#groups[@]} -eq 0 ]] || [[ -z "${groups[0]:-}" ]]; then
printf " —\n"
return 0
fi
for g in "${groups[@]}"; do
[[ -z "$g" ]] && continue
local count
count=$(json::count "$(group::path "$g")" "peers")
printf " %-20s %s peers\n" "$g" "$count"
done
}
function cmd::inspect::_firewall_info() {
local name="$1" show_nflog="${2:-false}"
local ip
ip=$(peers::get_ip "$name")
ui::section "Firewall"
local count=0
while IFS=":" read -r pname pcount; do
[[ "$pname" == "$name" ]] && count="$pcount" && break
done < <(json::audit_fw_counts "$(ctx::clients)")
ui::row "Active rules" "$count"
if [[ "$count" -gt 0 ]]; then
printf "\n"
iptables -L FORWARD -n </dev/null | grep -F "$ip" | while IFS= read -r rule; do
[[ -z "$rule" ]] && continue
echo "$rule" | grep -q "NFLOG" && continue # skip NFLOG
ui::firewall_rule "$rule"
done
fi
}
function cmd::inspect::_config() {
local name="$1"
cmd::inspect::_section "Config"
printf "\n"
cat "$(ctx::clients)/${name}.conf"
printf "\n"
}
# ============================================
# Run
# ============================================
function cmd::inspect::run() {
local name="" type="" show_config=false show_qr=false
if [[ $# -gt 0 && "$1" != "--"* ]]; then
name="$1"
shift
fi
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--type) type="$2"; shift 2 ;;
--config) show_config=true; shift ;;
--qr) show_qr=true; shift ;;
--help) cmd::inspect::help; return ;;
*)
log::error "Unknown flag: $1"
cmd::inspect::help
return 1
;;
esac
done
if [[ -z "$name" ]]; then
log::error "Missing required flag: --name"
cmd::inspect::help
return 1
fi
name=$(peers::resolve_and_require "$name" "$type") || return 1
load_command list
log::section "Inspect: ${name}"
cmd::inspect::_peer_info "$name"
cmd::inspect::_rule_info "$name"
cmd::inspect::_group_info "$name"
cmd::inspect::_firewall_info "$name"
if $show_config; then
cmd::inspect::_config "$name"
fi
if $show_qr; then
cmd::inspect::_section "QR Code"
printf "\n"
load_command qr
cmd::qr::run --name "$name"
fi
printf "\n"
}