Остання активність 1 month ago

Версія 689ca29cb9b345e5df35d88a43bc9ecf121824cc

gistfile1.txt Неформатований
1#!/usr/bin/env bash
2
3# ============================================
4# Lifecycle
5# ============================================
6
7function cmd::block::on_load() {
8 flag::register --name
9 flag::register --type
10 flag::register --force
11 flag::register --quiet
12 flag::register --ip
13 flag::register --port
14 flag::register --proto
15 flag::register --subnet
16 flag::register --block-name
17
18 # System - NET Services
19 flag::register --service
20}
21
22# ============================================
23# Help
24# ============================================
25
26function cmd::block::help() {
27 cat <<EOF
28Usage: wgctl block --name <name> [options]
29
30Block a client entirely or restrict access to specific IPs/ports/subnets.
31Block rules are persisted and restored on WireGuard restart.
32
33Options:
34 --name <name> Client name (e.g. phone-nuno)
35 --type <type> Device type (optional, combines with --name)
36 --ip <ip> Block access to specific IP (repeatable)
37 --subnet <cidr> Block access to subnet (repeatable)
38 --port <ip:port:proto> Block specific port, e.g. 10.0.0.210:9000:tcp (repeatable)
39 --force Skip confirmation prompt
40 --quiet Suppress output (used by group block)
41
42Examples:
43 wgctl block --name phone-nuno
44 wgctl block --name nuno --type phone
45 wgctl block --name phone-nuno --ip 10.0.0.210
46 wgctl block --name phone-nuno --subnet 10.0.0.0/24
47 wgctl block --name phone-nuno --port 10.0.0.210:9000:tcp
48 wgctl ban --name phone-nuno
49EOF
50}
51
52# ============================================
53# Block Run
54# ============================================
55
56function cmd::block::run() {
57 local name="" type="" block_name=""
58 local ips=() subnets=() ports=() services=()
59 local quiet=false force=false
60 local changed=false
61
62 while [[ $# -gt 0 ]]; do
63 case "$1" in
64 --name) name="$2"; shift 2 ;;
65 --type) type="$2"; shift 2 ;;
66 --ip) ips+=("$2"); shift 2 ;;
67 --block-name) block_name="$2"; shift 2 ;;
68 --service) services+=("$2"); shift 2 ;;
69 --force) force=true; shift ;;
70 --quiet) quiet=true; shift ;;
71 --subnet) subnets+=("$2"); shift 2 ;;
72 --port) ports+=("$2"); shift 2 ;;
73 --help) cmd::block::help; return ;;
74 *)
75 log::error "Unknown flag: $1"
76 cmd::block::help
77 return 1 ;;
78 esac
79 done
80
81 [[ -z "$name" ]] && log::error "Missing required flag: --name" && \
82 cmd::block::help && return 1
83
84 name=$(peers::resolve_and_require "$name" "$type") || return 1
85
86 local client_ip
87 client_ip=$(peers::get_ip "$name") || return 1
88
89 # Full block if no specific targets
90 if [[ ${#ips[@]} -eq 0 && ${#ports[@]} -eq 0 && \
91 ${#subnets[@]} -eq 0 && ${#services[@]} -eq 0 ]]; then
92 if peers::is_blocked "$name" || block::has_file "$name"; then
93 log::wg_warning "Client is already blocked: ${name}"
94 return 0
95 fi
96 monitor::update_endpoint_cache
97 cmd::block::_block_all "$name" "$client_ip" "$quiet"
98 return 0
99 fi
100
101 # Specific rules — check if already fully blocked
102 if block::has_file "$name"; then
103 local direct
104 direct=$(block::is_blocked_direct "$name")
105 if [[ "$direct" == "true" ]]; then
106 log::wg_warning "${name} is fully blocked — unblock first to add specific rules"
107 return 1
108 fi
109 fi
110
111 # Block specific IPs
112 for ip in "${ips[@]}"; do
113 ip::require_valid "$ip"
114 fw::block_ip "$client_ip" "$ip"
115 block::add_rule "$name" "$client_ip" "ip" "${block_name:-}" "$ip"
116 $quiet || log::wg_success "${ip} has been blocked for ${name}"
117 done
118
119 # Block specific subnets
120 for subnet in "${subnets[@]}"; do
121 ip::require_valid "$subnet"
122 fw::block_subnet "$client_ip" "$subnet"
123 block::add_rule "$name" "$client_ip" "subnet" "${block_name:-}" "$subnet"
124 $quiet || log::wg_success "${subnet} has been blocked for ${name}"
125 done
126
127 # Block specific ports
128 for entry in "${ports[@]}"; do
129 local b_target b_port b_proto
130 IFS=":" read -r b_target b_port b_proto <<< "$entry"
131 ip::require_valid "$b_target"
132 fw::block_port "$client_ip" "$b_target" "$b_port" "${b_proto:-tcp}"
133 block::add_rule "$name" "$client_ip" "port" "${block_name:-}" \
134 "$b_target" "$b_port" "${b_proto:-tcp}"
135 $quiet || log::wg_success "${client_ip}:${b_port}:${b_proto:-tcp} has been blocked for ${name}"
136 done
137
138 # Block services
139 for svc in "${services[@]}"; do
140 local resolved_lines=()
141 mapfile -t resolved_lines < <(net::resolve "$svc" 2>/dev/null)
142 if [[ ${#resolved_lines[@]} -eq 0 ]]; then
143 log::error "Service not found or has no ports: ${svc}"
144 return 1
145 fi
146
147 # Check if already blocked
148 local already_blocked=true
149 for resolved in "${resolved_lines[@]}"; do
150 if [[ "$resolved" == *:*:* ]]; then
151 local b_ip b_port b_proto
152 IFS=":" read -r b_ip b_port b_proto <<< "$resolved"
153 fw::has_block_rule "$client_ip" "$b_ip" "$b_proto" "$b_port" 2>/dev/null || \
154 { already_blocked=false; break; }
155 else
156 fw::has_block_rule "$client_ip" "$resolved" 2>/dev/null || \
157 { already_blocked=false; break; }
158 fi
159 done
160
161 if $already_blocked; then
162 $quiet || log::wg_warning "${svc} is already blocked for ${name}"
163 continue
164 fi
165
166 for resolved in "${resolved_lines[@]}"; do
167 if [[ "$resolved" == *:*:* ]]; then
168 local b_ip b_port b_proto
169 IFS=":" read -r b_ip b_port b_proto <<< "$resolved"
170 fw::block_port "$client_ip" "$b_ip" "$b_port" "$b_proto"
171 block::add_rule "$name" "$client_ip" "port" "$svc" \
172 "$b_ip" "$b_port" "$b_proto"
173 else
174 fw::block_ip "$client_ip" "$resolved"
175 block::add_rule "$name" "$client_ip" "ip" "$svc" "$resolved"
176 fi
177 done
178
179 changed=true
180 $quiet || log::wg_success "${svc} has been blocked for ${name}"
181 done
182
183 [[ ${#ips[@]} -gt 0 || ${#ports[@]} -gt 0 || \
184 ${#subnets[@]} -gt 0 ]] && changed=true
185
186 # Reapply in correct order: rule ACCEPT first, then peer DROP rules
187 # Only reorder if rules were actually added
188 if $changed; then
189 local peer_rule
190 peer_rule=$(peers::get_meta "$name" "rule")
191 if [[ -n "$peer_rule" ]] && rule::exists "$peer_rule"; then
192 fw::flush_peer "$client_ip"
193 rule::apply "$peer_rule" "$client_ip" "$name"
194 block::restore_rules_for "$name" "$client_ip"
195 fi
196 fi
197
198 return 0
199}
200
201function cmd::block::_get_endpoint() {
202 local name="$1" public_key="$2"
203 local endpoint
204 endpoint=$(monitor::endpoint_for_key "$public_key")
205 if [[ -z "$endpoint" || "$endpoint" == "(none)" ]]; then
206 endpoint=$(monitor::get_cached_endpoint "$name")
207 fi
208 echo "$endpoint"
209}
210
211function cmd::block::_block_all() {
212 local name="${1:?name required}"
213 local client_ip="${2:?client_ip required}"
214 local quiet="${3:-false}"
215
216 # Apply fw rules and remove from server
217 block::apply_full "$name" "$client_ip"
218
219 # Mark as directly blocked
220 block::set_direct "$name" "$client_ip" "true"
221
222 $quiet || log::wg_success "${name} has been blocked."
223}