166 lines
6.5 KiB
Markdown
166 lines
6.5 KiB
Markdown
# WireGuard Dynamic Policy Firewall
|
|
|
|
A lightweight, robust, and highly dynamic iptables/ipset policy firewall engine designed to restrict and control WireGuard peer traffic (egress traffic mapping) straight from `wg0.conf`.
|
|
|
|
Rather than allowing all VPN clients to reach any part of your internal network, this tool isolates clients from each other by default and reads a custom `#Access` comment inside `wg0.conf` to automatically generate strict `iptables` rules and `ipset` whitelists per-client on the fly.
|
|
|
|
---
|
|
|
|
## 🎯 Architecture & Data Flow
|
|
|
|
1. **`wg0.conf`**: The standard WireGuard configuration. Contains standard `[Peer]` configs alongside a custom `#Access` tag.
|
|
2. **`wg-sync-policy.sh`**: Safely parses `wg0.conf` and generates a structured `/etc/wireguard/policy.json` atomically.
|
|
3. **`wg-policy-engine.sh`**: Reads `policy.json` to generate robust rules, applying `iptables` and `ipset` directly to the system.
|
|
4. **Watcher Daemon**: Monitors `wg0.conf` for changes via `inotifywait` and triggers the pipeline seamlessly when updates are made.
|
|
|
|
---
|
|
|
|
## 📁 File Locations & Installation
|
|
|
|
All scripts must be placed in `/usr/local/bin/` and made executable.
|
|
|
|
| File | Location | Description |
|
|
|------|----------|-------------|
|
|
| `wg-policy-lib.sh` | `/usr/local/bin/wg-policy-lib.sh` | Shared library and core validation tools. |
|
|
| `wg-sync-policy.sh` | `/usr/local/bin/wg-sync-policy.sh` | Extracts config into atomic JSON format. |
|
|
| `wg-policy-engine.sh` | `/usr/local/bin/wg-policy-engine.sh` | Translates JSON into iptables & ipset logic. |
|
|
| `wg-policy-cleanup.sh` | `/usr/local/bin/wg-policy-cleanup.sh` | Reverts and cleans up all firewall traces safely. |
|
|
| `wg-sync-watch.sh` | `/usr/local/bin/wg-sync-watch.sh` | Debounced file watcher (daemon). |
|
|
| `wg-policy-ctl` | `/usr/local/bin/wg-policy-ctl` | Handy command-line interface tool. |
|
|
|
|
Make sure they are executable:
|
|
```bash
|
|
chmod +x /usr/local/bin/wg-*.sh /usr/local/bin/wg-policy-ctl
|
|
```
|
|
|
|
---
|
|
|
|
## 📦 Prerequisites
|
|
|
|
| Package | Required | Install |
|
|
|---------|----------|---------|
|
|
| `jq` | **Yes** | `apt install jq` |
|
|
| `inotify-tools` | **Yes** (for watcher daemon) | `apt install inotify-tools` |
|
|
| `ipset` | Optional | `apt install ipset` |
|
|
|
|
If `ipset` is not installed, the engine will automatically fall back to per-rule `iptables` whitelist entries. This works fine for small deployments. For large numbers of clients/targets, `ipset` is recommended for O(1) lookup performance.
|
|
|
|
---
|
|
|
|
## ⚙️ Integrasi ke `wg0.conf`
|
|
|
|
To integrate the engine, you need to append hooks into your `wg0.conf` interface block, and declare the `#Access` tags under each peer.
|
|
|
|
### 1. Interface Block (Hooks)
|
|
Add the `PostUp` and `PostDown` scripts so the engine initializes correctly during VPN startup and removes traces upon shutdown.
|
|
|
|
> **⚠️ WireGuard does NOT support multiline values.** Every command must be on a `PostUp = ...` or `PostDown = ...` line. Bare commands without the `PostUp =` prefix will cause `Configuration parsing error`.
|
|
|
|
```ini
|
|
[Interface]
|
|
Address = 10.0.0.1/24
|
|
ListenPort = 51820
|
|
PrivateKey = <SERVER_PRIVATE_KEY>
|
|
|
|
# Policy engine: auto-handles NAT, IP forwarding, and dynamic firewall rules
|
|
PostUp = /usr/local/bin/wg-sync-policy.sh; /usr/local/bin/wg-policy-engine.sh
|
|
|
|
# Policy engine: cleanup all firewall and routing traces
|
|
PostDown = /usr/local/bin/wg-policy-cleanup.sh
|
|
```
|
|
|
|
### 2. Peer Block (`#Access` Tags)
|
|
For each client, use the `#Access` comment line. Define the destinations (targets) the peer is allowed to access. You can separate multiple IPs or CIDRs with commas or semicolons.
|
|
|
|
> **⚠️ WARNING: Do NOT use `SaveConfig = true`!**
|
|
> WireGuard's `SaveConfig` feature overwrites `wg0.conf` directly and **strips all comments**, which will permanently delete all `#Access` tags. If you are using a Web UI/Dashboard, make sure it does not strip unknown comments when saving.
|
|
|
|
```ini
|
|
[Peer]
|
|
PublicKey = <CLIENT_1_PUBKEY>
|
|
AllowedIPs = 10.0.0.2/32
|
|
#Access 192.168.1.10/32, 192.168.12.0/24
|
|
|
|
[Peer]
|
|
PublicKey = <CLIENT_2_PUBKEY>
|
|
AllowedIPs = 10.0.0.3/32
|
|
#Access = 10.0.0.1/32
|
|
|
|
[Peer]
|
|
PublicKey = <CLIENT_3_PUBKEY>
|
|
AllowedIPs = 10.0.0.4/32
|
|
#Access
|
|
# ^ (Empty Access implies internet-only, client isolation applies)
|
|
```
|
|
|
|
**⚠️ Important constraint:** Why `#Access` instead of just using `AllowedIPs` directly?
|
|
WireGuard uses `AllowedIPs` for Cryptokey Routing (deciding which tunnel interface to route outbound packets). If you put an internal server IP inside the server's `wg0.conf` AllowedIPs block, WireGuard will aggressively capture and redirect packets bound for that internal server into the VPN client's tunnel. The `#Access` comment separates routing parameters from firewall parameters cleanly.
|
|
|
|
---
|
|
|
|
## 🛠 `wg-policy-ctl` CLI Usage
|
|
|
|
You don't need to manually interact with `iptables` or `.json` files. Use the `wg-policy-ctl` wrapper.
|
|
|
|
```bash
|
|
# View the health of the firewall engine and active locks
|
|
wg-policy-ctl status
|
|
|
|
# View the raw, parsed JSON policy
|
|
wg-policy-ctl policy
|
|
|
|
# Inspect active iptables rules
|
|
wg-policy-ctl rules
|
|
|
|
# Check memory sets mapping IP targets (ipset)
|
|
wg-policy-ctl ipset
|
|
|
|
# Manually re-sync rules immediately
|
|
wg-policy-ctl reload
|
|
|
|
# Inspect dropped packets (rate-limited log output)
|
|
wg-policy-ctl log
|
|
|
|
# See connection statistics, how many targets loaded
|
|
wg-policy-ctl stats
|
|
|
|
# Force validation of the policy schema
|
|
wg-policy-ctl validate
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Systemd Integration (Watcher Daemon)
|
|
|
|
If you are using the daemon mode to auto-sync changes instantly upon editing `wg0.conf` (without needing to run `wg-policy-ctl reload` or restarting the interface).
|
|
|
|
### 1. File Installation
|
|
Place the three provided systemd unit files into `/etc/systemd/system/`.
|
|
|
|
| Systemd File | Location | Description |
|
|
|--------------|----------|-------------|
|
|
| `wg-policy.service` | `/etc/systemd/system/wg-policy.service` | The main daemon that runs `wg-sync-watch.sh` |
|
|
| `wg-policy-health.timer` | `/etc/systemd/system/wg-policy-health.timer` | Triggers the health check every 5 minutes |
|
|
| `wg-policy-health.service`| `/etc/systemd/system/wg-policy-health.service`| Executes the actual health check logic |
|
|
|
|
```bash
|
|
# Example copy command
|
|
cp wg-policy.service wg-policy-health.timer wg-policy-health.service /etc/systemd/system/
|
|
```
|
|
|
|
### 2. Enable & Start Services
|
|
After copying the files, reload systemd to recognize them, then enable and start the services.
|
|
|
|
```bash
|
|
systemctl daemon-reload
|
|
systemctl enable wg-policy.service
|
|
systemctl enable wg-policy-health.timer
|
|
systemctl start wg-policy.service
|
|
systemctl start wg-policy-health.timer
|
|
```
|
|
|
|
Check the watcher logs:
|
|
```bash
|
|
journalctl -u wg-policy.service -f
|
|
```
|