wgusr 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wgusr-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 wgusr contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
wgusr-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,440 @@
1
+ Metadata-Version: 2.4
2
+ Name: wgusr
3
+ Version: 1.0.0
4
+ Summary: WireGuard user management tool — add, delete, list, view, clear VPN peers
5
+ Author: wgusr contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/olk/wgusr
8
+ Project-URL: Repository, https://github.com/olk/wgusr
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: System Administrators
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: System :: Networking
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: wgconfig>=1.2.0
22
+ Requires-Dist: wireguard-tools>=0.6.0
23
+ Requires-Dist: pynacl>=1.5.0
24
+ Requires-Dist: qrcode>=7.4.2
25
+ Requires-Dist: pypng>=0.0.21
26
+ Requires-Dist: click>=8.0.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
29
+ Requires-Dist: ruff>=0.7.0; extra == "dev"
30
+ Requires-Dist: mypy>=1.11.0; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # wgusr
34
+
35
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
36
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
37
+
38
+ WireGuard peer management — add, delete, list, view, and sync VPN users on a `wg-quick` server.
39
+
40
+ `wgusr` edits the `[Peer]` section of your `wg0.conf` directly: it generates keys and preshared keys, allocates VPN IPs from the existing pool, writes per-user client configs and QR codes, and hot-applies changes to the running interface — all under a file lock so concurrent invocations are safe.
41
+
42
+ ---
43
+
44
+ ## Why wgusr?
45
+
46
+ Managing `wg0.conf` by hand means copy-pasting base64 keys, tracking IPs in a spreadsheet, and regenerating QR codes for every new user. `wgusr` automates that plumbing:
47
+
48
+ - **Atomic, locked edits** — `fcntl.flock` + temp-file-rename so a crash mid-write never corrupts your config.
49
+ - **Automatic IP allocation** — derives free IPs from existing `AllowedIPs`; no sidecar pool file.
50
+ - **Key generation in pure Python** — no `wg genkey` subprocess needed.
51
+ - **Client artifacts** — ready-to-scan WireGuard app configs (ASCII QR + PNG QR) written to `<wg-dir>/clients/`.
52
+ - **Hot sync** — apply a single peer or all peers to the live interface without restarting `wg-quick`.
53
+
54
+ ## What wgusr does NOT do
55
+
56
+ - Does not install or load the `wireguard` kernel module.
57
+ - Does not bring the interface up (`wg-quick up wg0` is your responsibility).
58
+ - Does not create the `wg-quick@wg0.service` systemd unit (it ships with `wireguard-tools`).
59
+ - Does not configure firewall rules beyond the `nft` `PostUp`/`PostDown` hooks it emits during `init`.
60
+ - Does not provide a web UI.
61
+
62
+ `wgusr` edits the config file; `wg-quick` and the kernel apply it.
63
+
64
+ ---
65
+
66
+ ## Prerequisites
67
+
68
+ - **Python ≥ 3.11**
69
+ - **Root** — required to read/write `/etc/wireguard`.
70
+ - **`wg` and `wg-quick`** from `wireguard-tools` — needed for `init` and `sync` (key generation itself is pure-Python and does not require them).
71
+ - **Loaded `wireguard` kernel module:**
72
+ ```sh
73
+ modprobe wireguard
74
+ ```
75
+ - **`net.ipv4.ip_forward=1`** — required for the server to route VPN traffic:
76
+ ```sh
77
+ echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-wireguard.conf
78
+ sysctl --system
79
+ ```
80
+ - **`nft` table with `WG_IFACES` sets** — `wgusr init` emits `nft add element … WG_IFACES { wg0 }` in `PostUp`. The sets must exist before `wg-quick up` runs, otherwise the interface fails to start. Create them in e.g. `/etc/nftables.d/wireguard.nft`:
81
+ ```
82
+ table ip filter {
83
+ set WG_IFACES { type ifname; }
84
+ }
85
+ table ip nat {
86
+ set WG_IFACES { type ifname; }
87
+ }
88
+ ```
89
+ Apply with: `nft -f /etc/nftables.d/wireguard.nft`.
90
+ - **Systemd unit** — `systemctl enable --now wg-quick@wg0` (or bring the interface up manually).
91
+
92
+ ---
93
+
94
+ ## Installation
95
+
96
+ ### Development
97
+
98
+ ```sh
99
+ make install-dev # editable install, includes ruff + mypy
100
+ ```
101
+
102
+ ### Release
103
+
104
+ ```sh
105
+ make build # builds dist/wgusr-*.whl and *.tar.gz
106
+ make install # pip install dist/wgusr-*-py3-none-any.whl
107
+ ```
108
+
109
+ Or directly:
110
+
111
+ ```sh
112
+ pip install .
113
+ ```
114
+
115
+ Verify:
116
+
117
+ ```sh
118
+ wgusr --version
119
+ wgusr --help
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Quick start
125
+
126
+ ### 1. Initialise the interface
127
+
128
+ ```sh
129
+ sudo wgusr init
130
+ ```
131
+
132
+ `wgusr init` is interactive. It will ask for:
133
+
134
+ | Prompt | Default | Description |
135
+ |---|---|---|
136
+ | Listen port | `51820` | UDP port for the server |
137
+ | Server public address | *(required)* | Your server's public hostname or IP, e.g. `vpn.example.com` |
138
+ | VPN network CIDR | `10.0.73.0/24` | Tunnel network; server takes `.1` |
139
+ | Use existing private/public key? | `n` | Generate new keys, paste existing, or read from a file |
140
+
141
+ `init` creates `/etc/wireguard/wg0.conf` with `Address`, `ListenPort`, `PrivateKey`, `SaveConfig = true`, and `PostUp`/`PostDown` nft hooks. It refuses to overwrite an existing config.
142
+
143
+ Bring the interface up:
144
+
145
+ ```sh
146
+ sudo wg-quick up wg0
147
+ sudo systemctl enable --now wg-quick@wg0 # optional: start on boot
148
+ ```
149
+
150
+ Print the server's public key (needed by clients):
151
+
152
+ ```sh
153
+ sudo wg show wg0 public-key
154
+ ```
155
+
156
+ ### 2. Add a user
157
+
158
+ ```sh
159
+ sudo wgusr add alice vpn.example.com
160
+ ```
161
+
162
+ Output:
163
+
164
+ ```
165
+ user 'alice' added with IP 10.0.73.2
166
+
167
+ Client config: /etc/wireguard/clients/alice-wg0.conf
168
+
169
+ QR code (scan with WireGuard app):
170
+ ███████ ...
171
+ ```
172
+
173
+ Distribute the QR code or the `.conf` file to the client. The client's `[Interface] PrivateKey` and the server's `[Peer] PublicKey` are stored in `/etc/wireguard/clients/`.
174
+
175
+ ### 3. Sync the peer to the live interface
176
+
177
+ Changes to `wg0.conf` are not applied automatically — `wg-quick` only reads the config at startup. Use `sync` to push a peer live without restarting anything:
178
+
179
+ ```sh
180
+ # sync a single user
181
+ sudo wgusr sync alice
182
+
183
+ # sync all peers at once (wg syncconf)
184
+ sudo wgusr sync --all
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Usage workflow
190
+
191
+ ```sh
192
+ # Create a new interface (one-time)
193
+ sudo wgusr init
194
+
195
+ # Add a peer
196
+ sudo wgusr add alice vpn.example.com # auto IP
197
+ sudo wgusr add bob vpn.example.com --ip 10.0.73.50 # fixed IP
198
+ sudo wgusr add carol vpn.example.com --select-ip # interactive
199
+
200
+ # Push to the live interface
201
+ sudo wgusr sync alice # one peer
202
+ sudo wgusr sync --all # all peers at once
203
+
204
+ # Inspect
205
+ sudo wgusr list # table: USER, IP, PUBKEY
206
+ sudo wgusr view alice # client config + QR
207
+
208
+ # Remove a peer
209
+ sudo wgusr delete alice # by name
210
+ sudo wgusr delete --key <pubkey> # by public key (escape hatch)
211
+ sudo wgusr clear -y # wipe ALL peers (confirmation required)
212
+
213
+ # Short-hand
214
+ wgusr -v alice # equivalent to: wgusr view alice
215
+ ```
216
+
217
+ ### `wgusr add` flags
218
+
219
+ | Flag | Description |
220
+ |---|---|
221
+ | `--ip 10.0.73.x` | Assign a specific VPN IP |
222
+ | `--select-ip` | Interactively choose from available IPs |
223
+ | `--dns 1.1.1.1` | DNS server for the client (default: server tunnel IP) |
224
+ | `--keepalive 25` | `PersistentKeepalive` seconds (0 = off) |
225
+ | `--no-psk` | Skip preshared key (not recommended) |
226
+ | `--existing-keys` | Use a provided keypair instead of generating |
227
+ | `--private-key` | Private key (requires `--existing-keys`) |
228
+ | `--public-key` | Public key (requires `--existing-keys`) |
229
+
230
+ ---
231
+
232
+ ## Using wgusr on an existing server
233
+
234
+ `wgusr init` refuses to overwrite an existing `wg0.conf`. For servers you already have running, `wgusr` works immediately — it only reads the `[Interface]` section to learn the address, port, and private key:
235
+
236
+ ```sh
237
+ sudo wgusr list # shows existing peers
238
+ sudo wgusr add dave vpn.example.com # appends a new peer
239
+ sudo wgusr sync dave # push dave live
240
+ ```
241
+
242
+ Peers added by hand (without `wgusr`) may lack the `# {user}` leading comment. `wgusr` reconciles these automatically from `clients/*.pub` files on every command run.
243
+
244
+ ---
245
+
246
+ ## Example output
247
+
248
+ ### `wgusr list`
249
+
250
+ ```
251
+ USER IP PUBKEY
252
+ -------------------------------------------------------------------------------------
253
+ alice 10.0.73.2/32 XElbIX...qFE= 1 user(s)
254
+ ```
255
+
256
+ ### `wgusr add alice`
257
+
258
+ ```
259
+ user 'alice' added with IP 10.0.73.2
260
+
261
+ Client config: /etc/wireguard/clients/alice-wg0.conf
262
+
263
+ QR code (scan with WireGuard app):
264
+ ███████
265
+ █ ▄▄▄█
266
+ █ █▄█ █
267
+ ...
268
+ ```
269
+
270
+ ### `clients/` directory
271
+
272
+ ```
273
+ /etc/wireguard/clients/
274
+ ├── alice-wg0.conf # WireGuard app config (full tunnel)
275
+ ├── alice-wg0.png # QR code PNG
276
+ ├── alice.pri # client's private key
277
+ ├── alice.pub # client's public key
278
+ └── alice.psk # preshared key
279
+ ```
280
+
281
+ ---
282
+
283
+ ## Command reference
284
+
285
+ ### Global options
286
+
287
+ | Flag | Default | Description |
288
+ |---|---|---|
289
+ | `--wg-dir DIR` | `/etc/wireguard` | WireGuard config directory |
290
+ | `--interface`, `-i` | `wg0` | Interface name |
291
+ | `--clients-dir DIR` | `<wg-dir>/clients` | Where per-user artifacts are stored |
292
+
293
+ ### `wgusr init`
294
+
295
+ Interactively create a new `wg0.conf`. One-time setup; refuses to overwrite an existing file.
296
+
297
+ ### `wgusr add <user> <endpoint-host>`
298
+
299
+ Add a peer to `wg0.conf`, generate keys, allocate an IP, and write client artifacts.
300
+
301
+ ```
302
+ wgusr add alice vpn.example.com
303
+ wgusr -i wg0 add bob 192.168.1.100 --ip 10.0.73.50
304
+ ```
305
+
306
+ ### `wgusr delete <user>`
307
+
308
+ Remove a peer and its client artifacts. Cross-checks the `# {user}` comment against `clients/{user}.pub` before deleting.
309
+
310
+ ```
311
+ wgusr delete alice
312
+ wgusr delete --key XElbIX...qFE= # direct public-key delete
313
+ ```
314
+
315
+ ### `wgusr list`
316
+
317
+ List all peers with their VPN IPs and public keys.
318
+
319
+ ```
320
+ sudo wgusr list
321
+ ```
322
+
323
+ ### `wgusr view <user>`
324
+
325
+ Print a user's client config and QR code to stdout.
326
+
327
+ ```
328
+ sudo wgusr view alice
329
+ ```
330
+
331
+ ### `wgusr sync <user>` / `wgusr sync --all`
332
+
333
+ Apply peer(s) to the live interface via `wg set` (single user) or `wg syncconf` (all).
334
+
335
+ ```
336
+ sudo wgusr sync alice # one peer live immediately
337
+ sudo wgusr sync --all # full wg syncconf
338
+ ```
339
+
340
+ ### `wgusr clear`
341
+
342
+ Remove **all** peers and all client artifacts. Requires `--yes` or confirmation prompt.
343
+
344
+ ```
345
+ sudo wgusr clear -y
346
+ ```
347
+
348
+ ---
349
+
350
+ ## Reloading the live interface
351
+
352
+ `wg-quick` reads `wg0.conf` only at startup. To apply config changes to a running interface:
353
+
354
+ - **Single peer:** `wgusr sync <user>` — `wg set` updates just that peer, no disruption to others.
355
+ - **All peers:** `wgusr sync --all` — `wg syncconf` does a full atomic reconcile.
356
+
357
+ ---
358
+
359
+ ## How it works
360
+
361
+ `wgusr` reads and writes `wg0.conf` using the `wgconfig` library. Every mutation:
362
+
363
+ 1. Acquires an advisory `fcntl.flock` on `<iface>.lock` (shared for reads, exclusive for writes).
364
+ 2. Reconciles `# {user}` leading comments from `clients/*.pub` files.
365
+ 3. Writes to a temp file in the same directory (mode 0o600).
366
+ 4. Atomically replaces the target via `os.replace` (guaranteed atomic on POSIX).
367
+
368
+ IP addresses are derived from existing peer `AllowedIPs` — no sidecar pool file is needed. `wgusr sync` shells out to `wg set` or `wg syncconf` to push changes into the kernel; it does not restart `wg-quick`.
369
+
370
+ ---
371
+
372
+ ## Backup & recovery
373
+
374
+ `/etc/wireguard/clients/` is the source of truth for all private keys, preshared keys, and client configs. To back up or migrate:
375
+
376
+ ```sh
377
+ tar -czf wg-backup.tar.gz /etc/wireguard/wg0.conf /etc/wireguard/clients/
378
+ ```
379
+
380
+ **Security:** `.pri` and `.psk` files are plaintext (mode 0o600). Store backups encrypted at rest and never sync them to untrusted storage.
381
+
382
+ To restore: extract the archive and run `wgusr sync --all` to re-apply all peers.
383
+
384
+ ---
385
+
386
+ ## Troubleshooting
387
+
388
+ | Symptom | Fix |
389
+ |---|---|
390
+ | `error: 'wg' binary not found` | `apt install wireguard-tools` (or your distro's package) |
391
+ | `wg-quick up wg0` fails on `nft add element` | Define the `WG_IFACES` sets before starting the interface (see Prerequisites) |
392
+ | VPN clients can't reach the internet | `sysctl net.ipv4.ip_forward=1` and add a masquerade rule to your nft/iptables |
393
+ | `wgusr list` shows `(no name)` | Peer lacks a `# {user}` comment; auto-reconciled on the next `wgusr` command |
394
+ | Permission denied in `clients/` | Run `wgusr` as root |
395
+ | `wg-quick@wg0` is running stale config | `sudo wgusr sync --all` to push current conf, or `systemctl restart wg-quick@wg0` |
396
+
397
+ ---
398
+
399
+ ## Multiple interfaces
400
+
401
+ Manage multiple WireGuard interfaces by passing `--interface`:
402
+
403
+ ```sh
404
+ sudo wgusr -i wg1 add dave vpn.example.com
405
+ sudo wgusr -i wg1 sync dave
406
+ ```
407
+
408
+ Each interface has its own `wg{n}.conf` and its own `PostUp`/`PostDown` hooks. The `PostDown` hook removes **only** that interface from the shared `WG_IFACES` sets (bug fixed in v0.1.1; older versions flushed the entire set).
409
+
410
+ ---
411
+
412
+ ## Uninstall
413
+
414
+ ```sh
415
+ pip uninstall wgusr
416
+ ```
417
+
418
+ This removes the CLI. Left behind:
419
+
420
+ - `/etc/wireguard/wg0.conf` — your server config
421
+ - `/etc/wireguard/clients/` — all private keys and client configs
422
+ - The running `wg-quick@wg0` service and the nft ruleset
423
+
424
+ Clean these up manually if no longer needed.
425
+
426
+ ---
427
+
428
+ ## Security notes
429
+
430
+ - All file writes run under `umask 0o077`; artifacts are mode 0o600.
431
+ - Private keys are created with exclusive-create (`O_CREAT | O_EXCL`) — `wgusr` refuses to overwrite an existing key.
432
+ - All user inputs are validated before use: username, interface name, IP address, public key, and keepalive are checked against strict regexes and ranges.
433
+ - Path-traversal guards prevent `wg-dir` or `interface` from escaping the intended directory.
434
+ - After any manual edit to `wg0.conf`, run `wgusr sync --all` to bring the live interface in sync.
435
+
436
+ ---
437
+
438
+ ## License
439
+
440
+ Licensed under the [MIT License](LICENSE).