最后活跃于 1 month ago

nuno 修订了这个 Gist 1 month ago. 转到此修订

1 file changed, 235 insertions

gistfile1.txt(文件已创建)

@@ -0,0 +1,235 @@
1 + #!/usr/bin/env bash
2 +
3 + # ============================================
4 + # Lifecycle
5 + # ============================================
6 +
7 + function cmd::add::on_load() {
8 + flag::register --name
9 + flag::register --type
10 + flag::register --subtype
11 + flag::register --rule
12 + flag::register --group
13 + flag::register --ip
14 + flag::register --guest
15 + flag::register --tunnel
16 + flag::register --show-config
17 + flag::register --show-qr
18 + }
19 +
20 + # ============================================
21 + # Help
22 + # ============================================
23 +
24 + function cmd::add::help() {
25 + cat <<EOF
26 + Usage: wgctl add --name <name> --type <type> [options]
27 +
28 + Add a new WireGuard client.
29 +
30 + Options:
31 + --name <name> Client name (e.g. nuno)
32 + --type <type> Device type: desktop, laptop, phone, tablet, guest
33 + --subtype <subtype> Guest subtype: desktop, laptop, phone, tablet (mostly used for guest)
34 + --ip <ip> Override auto-assigned IP (optional)
35 + --guest Shorthand for --type guest
36 + --tunnel <mode> Tunnel mode: split|full (default: split)
37 + --rule <rule> Assign rule on creation (default: user, guest types: guest)
38 + --group <group> Add to group on creation (group must exist)
39 + --show-config Shows the WireGuard peer config
40 + --show-qr Shows the WireGuard config in a QR Code
41 +
42 + Device Types and Subnets:
43 + desktop 10.1.1.x
44 + laptop 10.1.2.x
45 + phone 10.1.3.x
46 + tablet 10.1.4.x
47 + guest 10.1.100.x
48 +
49 + Rules:
50 + Automatically assigned based on type (guest → guest rule, others → user rule).
51 + Override with --rule. Manage rules with: wgctl rule help
52 +
53 + Tunnel Modes:
54 + split Route only VPN subnet + LAN through WireGuard (default)
55 + full Route all traffic through WireGuard
56 +
57 + Examples:
58 + wgctl add --name nuno --type phone
59 + wgctl add --name nuno --type laptop --ip 10.1.2.5
60 + wgctl add --name nuno --type phone --tunnel full
61 + wgctl add --name guest1 --type phone --guest
62 + wgctl add --name restricted --type desktop
63 + wgctl add --name dev --type laptop --rule dev-01
64 + wgctl add --name visitor --type guest --show-qr
65 + EOF
66 + }
67 +
68 + # ============================================
69 + # Validation
70 + # ============================================
71 +
72 + function cmd::add::validate() {
73 + local name="$1"
74 + local type="$2"
75 + local ip="$3"
76 + local tunnel="$4"
77 +
78 + if [[ -z "$name" ]]; then
79 + log::error "Missing required flag: --name"
80 + return 1
81 + fi
82 +
83 + if [[ -z "$type" ]]; then
84 + log::error "Missing required flag: --type"
85 + return 1
86 + fi
87 +
88 + if ! config::is_valid_type "$type"; then
89 + log::error "Invalid device type: ${type}"
90 + log::info "Valid types: $(config::device_types | tr ' ' ', ')"
91 + return 1
92 + fi
93 +
94 + if [[ -n "$tunnel" && "$tunnel" != "split" && "$tunnel" != "full" ]]; then
95 + log::error "Invalid tunnel mode: ${tunnel} (use 'split' or 'full')"
96 + return 1
97 + fi
98 +
99 + if [[ -n "$ip" ]]; then
100 + ip::require_valid "$ip"
101 + fi
102 +
103 + local full_name="${type}-${name}"
104 +
105 + if [[ -f "$(ctx::clients)/${full_name}.conf" ]]; then
106 + log::error "Client already exists: ${full_name}"
107 + return 1
108 + fi
109 + }
110 +
111 + # ============================================
112 + # Display helpers
113 + # ============================================
114 +
115 + function cmd::add::is_mobile() {
116 + local type="$1"
117 + [[ "$type" == "phone" || "$type" == "tablet" ]]
118 + }
119 +
120 + # ============================================
121 + # Run
122 + # ============================================
123 +
124 + function cmd::add::run() {
125 + local name=""
126 + local type=""
127 + local subtype=""
128 + local rule=""
129 + local group=""
130 + local ip=""
131 + local tunnel=""
132 + local guest=false
133 + local show_config=false
134 + local show_qr=false
135 +
136 + # Parse flags
137 + while [[ $# -gt 0 ]]; do
138 + case "$1" in
139 + --name) name="$2"; shift 2 ;;
140 + --type) type="$2"; shift 2 ;;
141 + --subtype) subtype="$2"; shift 2 ;;
142 + --rule) rule="$2"; shift 2 ;;
143 + --group) group="$2"; shift 2 ;;
144 + --ip) ip="$2"; shift 2 ;;
145 + --guest) guest=true; shift ;;
146 + --tunnel) tunnel="$2"; shift 2 ;;
147 + --show-config) show_config=true; shift ;;
148 + --show-qr) show_qr=true; shift ;;
149 + --help) cmd::add::help; return ;;
150 + *)
151 + log::error "Unknown flag: $1"
152 + cmd::add::help
153 + return 1
154 + ;;
155 + esac
156 + done
157 +
158 + $guest && type="guest"
159 +
160 + local effective_type
161 + effective_type=$(cmd::add::_resolve_type "$type" "$subtype") || return 1
162 +
163 + local full_name="${type}-${name}"
164 + cmd::add::validate "$name" "$type" "$ip" "$tunnel" || return 1
165 +
166 + [[ -z "$tunnel" ]] && tunnel=$(config::default_tunnel_for "$type")
167 + [[ -z "$rule" ]] && rule=$(cmd::add::_default_rule "$type")
168 +
169 + rule::exists "$rule" || { log::error "Rule not found: ${rule}"; return 1; }
170 +
171 + local allowed_ips
172 + allowed_ips=$(config::allowed_ips_for "$effective_type" "$tunnel") || return 1
173 +
174 + log::section "Adding client: ${full_name}"
175 + [[ -z "$ip" ]] && ip=$(ip::next_for_type "$effective_type") || return 1
176 +
177 + log::wg_add "Name: ${full_name}"
178 + log::wg_add "Type: ${type}"
179 + log::wg_add "IP: ${ip}"
180 + log::wg_add "Tunnel: ${tunnel} (${allowed_ips})"
181 + log::wg_add "Endpoint: $(config::endpoint)"
182 + log::wg_add "Rule: ${rule:-none}"
183 +
184 + keys::generate_pair "$full_name" || return 1
185 + peers::create_client_config "$full_name" "$effective_type" "$ip" "$allowed_ips" || return 1
186 + [[ -n "$subtype" ]] && peers::set_meta "$full_name" "subtype" "$subtype"
187 +
188 + if [[ -n "$group" ]]; then
189 + if ! group::exists "$group"; then
190 + log::wg_warning "Group '${group}' not found — skipping group assignment"
191 + else
192 + group::add_peer "$group" "$full_name"
193 + log::wg "Added to group: ${group}"
194 + fi
195 + fi
196 +
197 + local public_key
198 + public_key=$(keys::public "$full_name") || return 1
199 + peers::add_to_server "$full_name" "$public_key" "$ip" || return 1
200 +
201 + [[ -n "$rule" ]] && rule::apply "$rule" "$ip" || return 1
202 + peers::reload || return 1
203 +
204 + log::wg_success "Client added successfully: ${full_name} (${ip}) [${tunnel} tunnel]"
205 + cmd::add::_show_result "$full_name" "${subtype:-$type}"
206 + }
207 +
208 + function cmd::add::_resolve_type() {
209 + local type="$1" subtype="$2"
210 + if [[ "$type" == "guest" && -n "$subtype" ]]; then
211 + local valid_subtypes="desktop laptop phone tablet"
212 + if ! echo "$valid_subtypes" | grep -qw "$subtype"; then
213 + log::error "Invalid subtype: ${subtype} (valid: desktop, laptop, phone, tablet)"
214 + return 1
215 + fi
216 + echo "guest-${subtype}"
217 + else
218 + echo "$type"
219 + fi
220 + }
221 +
222 + function cmd::add::_default_rule() {
223 + local type="$1"
224 + config::is_guest_type "$type" && echo "guest" || echo "user"
225 + }
226 +
227 + function cmd::add::_show_result() {
228 + local full_name="$1" display_type="$2"
229 + if cmd::add::is_mobile "$display_type"; then
230 + keys::qr "$full_name"
231 + else
232 + log::section "Client Config"
233 + cat "$(ctx::clients)/${full_name}.conf"
234 + fi
235 + }
上一页 下一页