Dernière activité 1 month ago

nuno a révisé ce gist 1 month ago. Aller à la révision

1 file changed, 228 insertions

gistfile1.txt(fichier créé)

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