gistfile1.txt
· 6.2 KiB · Text
Sin formato
#!/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::_peer_info() {
local name="${1:-}"
local ip type rule public_key allowed_ips
ip=$(peers::get_ip "$name")
type=$(peers::get_type "$name")
rule=$(peers::get_meta "$name" "rule")
public_key=$(keys::public "$name" 2>/dev/null || echo "")
allowed_ips=$(grep "^AllowedIPs" "$(ctx::clients)/${name}.conf" \
2>/dev/null | cut -d'=' -f2- | xargs)
# Status
local handshake_ts is_blocked last_ts
handshake_ts=$(monitor::get_handshake_ts "$public_key")
peers::is_blocked "$name" && is_blocked="true" || is_blocked="false"
last_ts=$(monitor::last_attempt "$name")
local status last_seen endpoint
status=$(peers::format_status "$name" "$public_key" \
"$is_blocked" "false" "$handshake_ts" "$last_ts")
last_seen=$(peers::format_last_seen "$name" "$public_key" \
"$is_blocked" "$last_ts" "" "$handshake_ts")
endpoint=$(monitor::get_cached_endpoint "$name")
local activity_total
activity_total=$(peers::format_activity_total "$public_key")
local activity_current
activity_current=$(peers::format_activity_current "$public_key")
local subtype
subtype=$(peers::get_meta "$name" "subtype")
cmd::inspect::_section "Client"
ui::row "Name" "$name"
ui::row "IP" "$ip"
ui::row "Type" "$(peers::display_type "$type" "$subtype")"
ui::row "Rule" "${rule:-—}"
ui::row "Status" "$(echo -e "$status")"
ui::row "Endpoint" "${endpoint:-—}"
ui::row "Last seen" "$last_seen"
ui::row "AllowedIPs" "$allowed_ips"
ui::row "Public key" "${public_key:-—}"
ui::row "Activity" "Total: $activity_total | Current: $activity_current"
}
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 </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"
}
| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | function cmd::inspect::on_load() { |
| 4 | flag::register --name |
| 5 | flag::register --type |
| 6 | flag::register --config |
| 7 | flag::register --qr |
| 8 | } |
| 9 | |
| 10 | function cmd::inspect::help() { |
| 11 | cat <<EOF |
| 12 | Usage: wgctl inspect --name <name> [--type <type>] |
| 13 | wgctl inspect <full-name> |
| 14 | |
| 15 | Show detailed information for a single client. |
| 16 | |
| 17 | Options: |
| 18 | --name <name> Client name |
| 19 | --type <type> Device type (optional, combines with --name) |
| 20 | --config Show raw client config |
| 21 | --qr Show QR code |
| 22 | |
| 23 | Examples: |
| 24 | wgctl inspect --name phone-nuno |
| 25 | wgctl inspect --name nuno --type phone |
| 26 | wgctl inspect --name phone-nuno --config |
| 27 | wgctl inspect --name phone-nuno --qr |
| 28 | EOF |
| 29 | } |
| 30 | |
| 31 | # ============================================ |
| 32 | # Private helpers |
| 33 | # ============================================ |
| 34 | |
| 35 | function cmd::inspect::_section() { |
| 36 | local title="$1" |
| 37 | printf "\n \033[0;37m── %s ──────────────────────────────────\033[0m\n" "$title" |
| 38 | } |
| 39 | |
| 40 | function cmd::inspect::_peer_info() { |
| 41 | local name="${1:-}" |
| 42 | |
| 43 | local ip type rule public_key allowed_ips |
| 44 | ip=$(peers::get_ip "$name") |
| 45 | type=$(peers::get_type "$name") |
| 46 | rule=$(peers::get_meta "$name" "rule") |
| 47 | public_key=$(keys::public "$name" 2>/dev/null || echo "") |
| 48 | allowed_ips=$(grep "^AllowedIPs" "$(ctx::clients)/${name}.conf" \ |
| 49 | 2>/dev/null | cut -d'=' -f2- | xargs) |
| 50 | |
| 51 | # Status |
| 52 | local handshake_ts is_blocked last_ts |
| 53 | handshake_ts=$(monitor::get_handshake_ts "$public_key") |
| 54 | peers::is_blocked "$name" && is_blocked="true" || is_blocked="false" |
| 55 | last_ts=$(monitor::last_attempt "$name") |
| 56 | |
| 57 | local status last_seen endpoint |
| 58 | status=$(peers::format_status "$name" "$public_key" \ |
| 59 | "$is_blocked" "false" "$handshake_ts" "$last_ts") |
| 60 | last_seen=$(peers::format_last_seen "$name" "$public_key" \ |
| 61 | "$is_blocked" "$last_ts" "" "$handshake_ts") |
| 62 | endpoint=$(monitor::get_cached_endpoint "$name") |
| 63 | |
| 64 | local activity_total |
| 65 | activity_total=$(peers::format_activity_total "$public_key") |
| 66 | |
| 67 | local activity_current |
| 68 | activity_current=$(peers::format_activity_current "$public_key") |
| 69 | |
| 70 | local subtype |
| 71 | subtype=$(peers::get_meta "$name" "subtype") |
| 72 | |
| 73 | cmd::inspect::_section "Client" |
| 74 | ui::row "Name" "$name" |
| 75 | ui::row "IP" "$ip" |
| 76 | ui::row "Type" "$(peers::display_type "$type" "$subtype")" |
| 77 | ui::row "Rule" "${rule:-—}" |
| 78 | ui::row "Status" "$(echo -e "$status")" |
| 79 | ui::row "Endpoint" "${endpoint:-—}" |
| 80 | ui::row "Last seen" "$last_seen" |
| 81 | ui::row "AllowedIPs" "$allowed_ips" |
| 82 | ui::row "Public key" "${public_key:-—}" |
| 83 | ui::row "Activity" "Total: $activity_total | Current: $activity_current" |
| 84 | } |
| 85 | |
| 86 | function cmd::inspect::_rule_info() { |
| 87 | local name="$1" |
| 88 | local rule |
| 89 | rule=$(peers::get_meta "$name" "rule") |
| 90 | [[ -z "$rule" ]] && return 0 |
| 91 | |
| 92 | rule::exists "$rule" || return 0 |
| 93 | |
| 94 | local rule_file |
| 95 | rule_file="$(ctx::rule::path "${rule}.rule")" |
| 96 | |
| 97 | ui::section "Rule: ${rule}" |
| 98 | |
| 99 | local desc dns_redirect |
| 100 | desc=$(json::get "$rule_file" "desc") |
| 101 | dns_redirect=$(json::get "$rule_file" "dns_redirect") |
| 102 | ui::row "Description" "${desc:-—}" |
| 103 | ui::row "DNS Redirect" "${dns_redirect:-false}" |
| 104 | |
| 105 | local allow_ports allow_ips block_ips block_ports |
| 106 | allow_ports=$(json::get "$rule_file" "allow_ports") |
| 107 | allow_ips=$(json::get "$rule_file" "allow_ips") |
| 108 | block_ips=$(json::get "$rule_file" "block_ips") |
| 109 | block_ports=$(json::get "$rule_file" "block_ports") |
| 110 | |
| 111 | if [[ -n "$allow_ports" || -n "$allow_ips" ]]; then |
| 112 | printf " %-20s\n" "Allows:" |
| 113 | ui::print_list "+" "$allow_ports" |
| 114 | ui::print_list "+" "$allow_ips" |
| 115 | else |
| 116 | ui::row "Allows" "—" |
| 117 | fi |
| 118 | |
| 119 | if [[ -n "$block_ips" || -n "$block_ports" ]]; then |
| 120 | printf " %-20s\n" "Blocks:" |
| 121 | ui::print_list "-" "$block_ips" |
| 122 | ui::print_list "-" "$block_ports" |
| 123 | else |
| 124 | ui::row "Blocks" "—" |
| 125 | fi |
| 126 | } |
| 127 | |
| 128 | function cmd::inspect::_group_info() { |
| 129 | local name="$1" |
| 130 | |
| 131 | ui::section "Groups" |
| 132 | |
| 133 | local groups=() |
| 134 | mapfile -t groups < <(json::peer_groups "$(ctx::groups)" "$name") |
| 135 | |
| 136 | if [[ ${#groups[@]} -eq 0 ]] || [[ -z "${groups[0]:-}" ]]; then |
| 137 | printf " —\n" |
| 138 | return 0 |
| 139 | fi |
| 140 | |
| 141 | for g in "${groups[@]}"; do |
| 142 | [[ -z "$g" ]] && continue |
| 143 | local count |
| 144 | count=$(json::count "$(group::path "$g")" "peers") |
| 145 | printf " %-20s %s peers\n" "$g" "$count" |
| 146 | done |
| 147 | } |
| 148 | |
| 149 | function cmd::inspect::_firewall_info() { |
| 150 | local name="$1" show_nflog="${2:-false}" |
| 151 | local ip |
| 152 | ip=$(peers::get_ip "$name") |
| 153 | |
| 154 | ui::section "Firewall" |
| 155 | |
| 156 | local count=0 |
| 157 | while IFS=":" read -r pname pcount; do |
| 158 | [[ "$pname" == "$name" ]] && count="$pcount" && break |
| 159 | done < <(json::audit_fw_counts "$(ctx::clients)") |
| 160 | |
| 161 | ui::row "Active rules" "$count" |
| 162 | |
| 163 | if [[ "$count" -gt 0 ]]; then |
| 164 | printf "\n" |
| 165 | iptables -L FORWARD -n </dev/null | grep -F "$ip" | while IFS= read -r rule; do |
| 166 | [[ -z "$rule" ]] && continue |
| 167 | echo "$rule" | grep -q "NFLOG" && continue # skip NFLOG |
| 168 | ui::firewall_rule "$rule" |
| 169 | done |
| 170 | fi |
| 171 | } |
| 172 | |
| 173 | function cmd::inspect::_config() { |
| 174 | local name="$1" |
| 175 | cmd::inspect::_section "Config" |
| 176 | printf "\n" |
| 177 | cat "$(ctx::clients)/${name}.conf" |
| 178 | printf "\n" |
| 179 | } |
| 180 | |
| 181 | # ============================================ |
| 182 | # Run |
| 183 | # ============================================ |
| 184 | |
| 185 | function cmd::inspect::run() { |
| 186 | local name="" type="" show_config=false show_qr=false |
| 187 | |
| 188 | if [[ $# -gt 0 && "$1" != "--"* ]]; then |
| 189 | name="$1" |
| 190 | shift |
| 191 | fi |
| 192 | |
| 193 | while [[ $# -gt 0 ]]; do |
| 194 | case "$1" in |
| 195 | --name) name="$2"; shift 2 ;; |
| 196 | --type) type="$2"; shift 2 ;; |
| 197 | --config) show_config=true; shift ;; |
| 198 | --qr) show_qr=true; shift ;; |
| 199 | --help) cmd::inspect::help; return ;; |
| 200 | *) |
| 201 | log::error "Unknown flag: $1" |
| 202 | cmd::inspect::help |
| 203 | return 1 |
| 204 | ;; |
| 205 | esac |
| 206 | done |
| 207 | |
| 208 | if [[ -z "$name" ]]; then |
| 209 | log::error "Missing required flag: --name" |
| 210 | cmd::inspect::help |
| 211 | return 1 |
| 212 | fi |
| 213 | |
| 214 | name=$(peers::resolve_and_require "$name" "$type") || return 1 |
| 215 | |
| 216 | load_command list |
| 217 | |
| 218 | log::section "Inspect: ${name}" |
| 219 | |
| 220 | cmd::inspect::_peer_info "$name" |
| 221 | cmd::inspect::_rule_info "$name" |
| 222 | cmd::inspect::_group_info "$name" |
| 223 | cmd::inspect::_firewall_info "$name" |
| 224 | |
| 225 | if $show_config; then |
| 226 | cmd::inspect::_config "$name" |
| 227 | fi |
| 228 | |
| 229 | if $show_qr; then |
| 230 | cmd::inspect::_section "QR Code" |
| 231 | printf "\n" |
| 232 | load_command qr |
| 233 | cmd::qr::run --name "$name" |
| 234 | fi |
| 235 | |
| 236 | printf "\n" |
| 237 | } |