#!/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 < [--type ] wgctl inspect Show detailed information for a single client. Options: --name Client name --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" "—" 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