Ultima attività 1 month ago

Revisione 700fe2ab5b98a12390a1d7e2c276f00efef39ed4

net.command.sh Raw
1#!/usr/bin/env bash
2
3function cmd::net::on_load() {
4 flag::register --name
5 flag::register --ip
6 flag::register --port
7 flag::register --desc
8 flag::register --tag
9 flag::register --detailed
10 flag::register --force
11
12 command::mixin json_output
13}
14
15function cmd::net::help() {
16 cat <<EOF
17Usage: wgctl net <subcommand> [options]
18
19Manage named network services for use with block/allow rules.
20Services map names to IPs and ports, making rules more readable.
21
22Subcommands:
23 list List all services
24 show --name <name> Show service details
25 add --name <name> --ip <ip> Add a service
26 add --name <svc:port-name> --port <port:proto>
27 Add a port to a service
28 rm --name <name> Remove service or port
29 rm --name <svc:ports> Remove all ports from service
30
31Options for add (service):
32 --name <name> Service name (e.g. proxmox)
33 --ip <ip> Service IP address
34 --desc <description> Optional description
35 --tag <tag> Optional tag (repeatable)
36
37Options for add (port):
38 --name <svc:port-name> Service:port-name (e.g. proxmox:web-ui)
39 --port <port:proto> Port and protocol (e.g. 8006:tcp)
40 --desc <description> Optional description
41
42Options for list:
43 --detailed Show ports for each service
44 --tag <tag> Filter by tag
45
46Examples:
47 wgctl net list
48 wgctl net list --detailed
49 wgctl net list --tag admin
50 wgctl net show --name proxmox
51 wgctl net add --name proxmox --ip 10.0.0.100 --desc "Proxmox VE"
52 wgctl net add --name proxmox:web-ui --port 8006:tcp --desc "Web UI"
53 wgctl net add --name proxmox:ssh --port 22:tcp
54 wgctl net rm --name proxmox:web-ui
55 wgctl net rm --name proxmox:ports
56 wgctl net rm --name proxmox
57EOF
58}
59
60function cmd::net::run() {
61 local subcmd="${1:-list}"
62 shift || true
63
64 if command::json; then
65 cmd::net::_output_json
66 return 0
67 fi
68
69 case "$subcmd" in
70 list) cmd::net::list "$@" ;;
71 show) cmd::net::show "$@" ;;
72 add) cmd::net::add "$@" ;;
73 rm|remove|del) cmd::net::rm "$@" ;;
74 help) cmd::net::help ;;
75 *)
76 log::error "Unknown subcommand: '${subcmd}'"
77 cmd::net::help
78 return 1 ;;
79 esac
80}
81
82# ============================================
83# List
84# ============================================
85
86function cmd::net::list() {
87 local detailed=false filter_tag=""
88
89 while [[ $# -gt 0 ]]; do
90 case "$1" in
91 --detailed) detailed=true; shift ;;
92 --tag) filter_tag="$2"; shift 2 ;;
93 --help) cmd::net::help; return ;;
94 *) log::error "Unknown flag: $1"; return 1 ;;
95 esac
96 done
97
98 local net_file
99 net_file="$(ctx::net)"
100
101 if [[ ! -f "$net_file" ]]; then
102 log::wg_warning "No services configured. Use 'wgctl net add' to add one."
103 return 0
104 fi
105
106 # Collect filtered data and build ports display per service
107 local filtered_data=""
108 while IFS="|" read -r name ip desc tags port_count; do
109 [[ -z "$name" ]] && continue
110 [[ -n "$filter_tag" && "$tags" != *"$filter_tag"* ]] && continue
111
112 # Build ports display from json::net_show
113 local ports_display=""
114 while IFS="|" read -r ptype pname pport pproto pdesc; do
115 [[ "$ptype" != "port" ]] && continue
116 local port_str=":${pport}"
117 [[ -n "$pproto" && "$pproto" != "tcp" ]] && port_str="${port_str}/${pproto}"
118 ports_display+="${port_str}, "
119 done < <(json::net_show "$net_file" "$name")
120 ports_display="${ports_display%, }"
121 [[ -z "$ports_display" ]] && ports_display="-"
122
123 filtered_data+="${name}|${ip}|${desc}|${tags}|${ports_display}"$'\n'
124 done < <(json::net_list "$net_file")
125
126 [[ -z "$filtered_data" ]] && {
127 [[ -n "$filter_tag" ]] && \
128 log::wg_warning "No services with tag: ${filter_tag}" || \
129 log::wg_warning "No services configured"
130 return 0
131 }
132
133 # Measure column widths
134 local w_name=12 w_ip=13 w_ports=16
135 while IFS="|" read -r name ip desc tags ports; do
136 [[ -z "$name" ]] && continue
137 (( ${#name} > w_name )) && w_name=${#name}
138 (( ${#ip} > w_ip )) && w_ip=${#ip}
139 (( ${#ports} > w_ports )) && w_ports=${#ports}
140 done <<< "$filtered_data"
141 (( w_name += 2 ))
142 (( w_ip += 2 ))
143 (( w_ports += 2 ))
144
145 log::section "Network Services"
146 echo ""
147
148 if display::is_table "net_list"; then
149 cmd::net::_render_table "$filtered_data"
150 return 0
151 fi
152
153 while IFS="|" read -r name ip desc tags ports; do
154 [[ -z "$name" ]] && continue
155 ui::net::list_row "$name" "$ip" "$desc" "$tags" "$ports" \
156 "$w_name" "$w_ip" "$w_ports"
157
158 if $detailed; then
159 while IFS="|" read -r ptype pname pport pproto pdesc; do
160 [[ "$ptype" != "port" ]] && continue
161 ui::net::show_port_row "$pname" "$pport" "$pproto" "$pdesc"
162 done < <(json::net_show "$net_file" "$name")
163 echo ""
164 fi
165 done <<< "$filtered_data"
166
167 echo ""
168}
169
170function cmd::net::_render_table() {
171 local data="${1:-}"
172 [[ -z "$data" ]] && return 0
173
174 ui::net::list_header_table
175 while IFS='|' read -r name ip desc tags port_count; do
176 [[ -z "$name" ]] && continue
177 ui::net::list_row_table "$name" "$ip" "$desc" "$tags" "$port_count"
178 done <<< "$data"
179}
180
181# ============================================
182# Show
183# ============================================
184
185function cmd::net::show() {
186 local name=""
187 while [[ $# -gt 0 ]]; do
188 case "$1" in
189 --name) util::require_flag "--name" "${2:-}" || return 1
190 name="$2"; shift 2 ;;
191 --help) cmd::net::help; return ;;
192 *) log::error "Unknown flag: $1"; return 1 ;;
193 esac
194 done
195
196 [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
197 net::require_exists "$name" || return 1
198
199 log::section "Service: ${name}"
200 printf "\n"
201
202 local has_ports=false
203 while IFS="|" read -r key val1 val2 val3 val4; do
204 case "$key" in
205 name) ui::row "Name" "$val1" ;;
206 desc) ui::row "Description" "${val1:-}" ;;
207 tags) ui::row "Tags" "${val1:-}" ;;
208 ip) ui::row "IP" "$val1" ;;
209 port)
210 if ! $has_ports; then
211 printf " %-20s\n" "Ports:"
212 has_ports=true
213 fi
214 ui::net::show_port_row "$val1" "$val2" "$val3" "$val4"
215 ;;
216 esac
217 done < <(json::net_show "$(ctx::net)" "$name")
218
219 printf "\n"
220}
221
222# ============================================
223# Add
224# ============================================
225
226function cmd::net::add() {
227 local name="" ip="" port="" desc="" tags=()
228
229 while [[ $# -gt 0 ]]; do
230 case "$1" in
231 --name) util::require_flag "--name" "${2:-}" || return 1
232 name="$2"; shift 2 ;;
233 --ip) ip="$2"; shift 2 ;;
234 --port) port="$2"; shift 2 ;;
235 --desc) desc="$2"; shift 2 ;;
236 --tag) tags+=("$2"); shift 2 ;;
237 --help) cmd::net::help; return ;;
238 *) log::error "Unknown flag: $1"; return 1 ;;
239 esac
240 done
241
242 [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
243
244 if [[ "$name" == *:* ]]; then
245 # Port mode: proxmox:web-ui
246 local svc_name="${name%%:*}"
247 local port_name="${name##*:}"
248
249 [[ -z "$port" ]] && log::error "Missing required flag: --port" && return 1
250 net::require_exists "$svc_name" || return 1
251
252 local port_num proto
253 if [[ "$port" == *:* ]]; then
254 port_num="${port%%:*}"
255 proto="${port##*:}"
256 else
257 port_num="$port"
258 proto="tcp"
259 fi
260
261 json::net_add_port "$(ctx::net)" "$svc_name" "$port_name" \
262 "$port_num" "$proto" "$desc"
263
264 log::wg_success "Added port: ${svc_name}:${port_name}${port_num}/${proto}"
265 else
266 # Service mode: proxmox
267 [[ -z "$ip" ]] && log::error "Missing required flag: --ip" && return 1
268
269 local tags_str
270 tags_str=$(IFS=','; echo "${tags[*]}")
271
272 json::net_add_service "$(ctx::net)" "$name" "$ip" "$desc" "$tags_str"
273
274 log::wg_success "Service added: ${name}${ip}"
275 fi
276 return 0
277}
278
279# ============================================
280# Remove
281# ============================================
282
283function cmd::net::rm() {
284 local name="" force=false
285
286 while [[ $# -gt 0 ]]; do
287 case "$1" in
288 --name) util::require_flag "--name" "${2:-}" || return 1
289 name="$2"; shift 2 ;;
290 --force) force=true; shift ;;
291 --help) cmd::net::help; return ;;
292 *) log::error "Unknown flag: $1"; return 1 ;;
293 esac
294 done
295
296 [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
297
298 # Validate existence
299 if [[ "$name" == *:* ]]; then
300 local svc_name="${name%%:*}"
301 local port_name="${name##*:}"
302 if [[ "$port_name" != "ports" ]]; then
303 # Check specific port exists
304 local exists
305 exists=$(json::net_exists "$(ctx::net)" "$name")
306 if [[ "$exists" != "true" ]]; then
307 log::error "Port not found: ${name}"
308 return 1
309 fi
310 else
311 net::require_exists "$svc_name" || return 1
312 fi
313 else
314 net::require_exists "$name" || return 1
315 fi
316
317 if ! $force; then
318 local what="service '${name}'"
319 [[ "$name" == *:ports ]] && what="all ports from '${name%%:*}'"
320 [[ "$name" == *:* && "$name" != *:ports ]] && what="port '${name}'"
321 read -r -p "Remove ${what}? [y/N] " confirm
322 case "$confirm" in
323 [yY]*) ;;
324 *) log::info "Aborted"; return 0 ;;
325 esac
326 fi
327
328 json::net_remove "$(ctx::net)" "$name"
329 log::wg_success "Removed: ${name}"
330 return 0
331}
332
333function cmd::net::_output_json() {
334 local net_file
335 net_file="$(ctx::net)"
336 local data
337 data=$(json::net_list "$net_file" 2>/dev/null)
338
339 local -a services=()
340 while IFS='|' read -r name ip desc tags port_count; do
341 [[ -z "$name" ]] && continue
342 # Build tags array
343 local tags_json="[]"
344 if [[ -n "$tags" ]]; then
345 local tags_array
346 tags_array=$(echo "$tags" | tr ',' '\n' | \
347 while IFS= read -r t; do [[ -n "$t" ]] && printf '"%s",' "$t"; done | sed 's/,$//')
348 tags_json="[${tags_array}]"
349 fi
350 services+=("$(printf '{"name":"%s","ip":"%s","desc":"%s","tags":%s,"port_count":%s}' \
351 "$name" "$ip" "$desc" "$tags_json" "$port_count")")
352 done <<< "$data"
353
354 local count=${#services[@]}
355 local array
356 array=$(printf '%s\n' "${services[@]:-}" | paste -sd ',' -)
357 printf '{"services":[%s]}' "${array:-}" | json::envelope "net list" "$count"
358}