- Update app/ submodule to commit ff80d9c - Complete WGRplane with Vue.js frontend and Go backend - Includes Glassmorphism UI, webhooks, i18n, and plugins
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
wg0.conf: The standard WireGuard configuration. Contains standard[Peer]configs alongside a custom#Accesstag.wg-sync-policy.sh: Safely parseswg0.confand generates a structured/etc/wireguard/policy.jsonatomically.wg-policy-engine.sh: Readspolicy.jsonto generate robust rules, applyingiptablesandipsetdirectly to the system.- Watcher Daemon: Monitors
wg0.conffor changes viainotifywaitand triggers the pipeline seamlessly when updates are made.
📁 Installation
The easiest way to install is using the provided install.sh script, which automatically installs dependencies, copies all scripts to /usr/local/bin/, sets up the systemd daemon, and enables the service.
# Install everything
sudo ./install.sh install
# Uninstall everything
sudo ./install.sh uninstall
Building the Installer (For Developers)
If you modify any of the source .sh or .service files, you must rebuild the install.sh script using the provided builders.
On Linux (Bash):
./build.sh
On Windows (CMD/PowerShell):
build.bat
📦 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 = ...orPostDown = ...line. Bare commands without thePostUp =prefix will causeConfiguration parsing error.
[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'sSaveConfigfeature overwriteswg0.confdirectly and strips all comments, which will permanently delete all#Accesstags. If you are using a Web UI/Dashboard, make sure it does not strip unknown comments when saving.
[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.
# 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 use the install.sh script, the daemon is automatically installed, enabled, and started for you. It monitors wg0.conf for changes and triggers the pipeline seamlessly.
Manual File Installation (If not using install.sh)
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 |
# 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.
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:
journalctl -u wg-policy.service -f