# 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. ```ini [Interface] Address = 10.0.0.1/24 ListenPort = 51820 PrivateKey = # PostUp: sync policy + apply engine # default PostUp iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; # modification PostUp = /usr/local/bin/wg-sync-policy.sh && /usr/local/bin/wg-policy-engine.sh # PostDown: safe cleanup # default PostUp iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; # modification 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. ```ini [Peer] PublicKey = AllowedIPs = 10.0.0.2/32 #Access 192.168.1.10/32, 192.168.12.0/24 [Peer] PublicKey = AllowedIPs = 10.0.0.3/32 #Access = 10.0.0.1/32 [Peer] PublicKey = 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 ```