# WGRplane **WireGuard Control Plane with Dynamic Policy Firewall.** WGRplane is a Go-native WireGuard management dashboard paired with a shell-based dynamic iptables/ipset policy engine. It gives you full peer lifecycle management, per-peer firewall policies, webhook integrations, and a glassmorphism-styled Vue 3 frontend. --- ## Overview WGRplane operates in two complementary layers: | Layer | What it does | |-------|-------------| | **WGRplane (Go + Vue 3)** | Web dashboard and REST API for managing WireGuard servers, peers, webhooks, SMTP, and scheduling. | | **Policy Firewall (Shell)** | Dynamic iptables/ipset engine that reads `#Access` comments from `wg0.conf` to enforce per-peer egress rules. | ### Hybrid Mode The Go backend supports two server modes: - **`forward`** -- The WGRplane instance directly applies nftables rules on the local machine. Peer access policies are enforced immediately via `nft` commands. - **`standalone`** -- The instance acts as a control plane that triggers webhooks to remote WireGuard servers. Policy enforcement happens on the remote side. ### 2-Column Policy Each peer has two independent policy columns: | Column | Purpose | |--------|---------| | **AllowAccess** | List of CIDRs the peer can reach (internal targets). | | **AllowInternet** | Boolean flag. When `true`, the peer gets unrestricted internet egress. | Peers with neither rule are isolated from each other and from the internet by default. --- ## Architecture ```mermaid graph TB subgraph "WGRplane Dashboard" A[Vue 3 Frontend] -->|HTTP/WebSocket| B[Go REST API :10087] end subgraph "Go Backend" B --> C[(SQLite DB)] B --> D[Webhook Engine] B --> E[Scheduler Cron] B --> F[nftables Engine] B --> G[SMTP / Email] B --> H[Plugin Manager] end subgraph "Policy Firewall" I[wg0.conf #Access] -->|inotifywait| J[wg-sync-watch.sh] J --> K[wg-sync-policy.sh] K -->|atomic write| L[policy.json] L --> M[wg-policy-engine.sh] M --> N[iptables / ipset] end D -.->|peer_created/updated/deleted| O[External Webhooks] F -.->|forward mode| N E -.->|daily/monthly| P[Peer Lifecycle] ``` ### Data Flow ``` wg0.conf (#Access comments) ↓ inotifywait detects change wg-sync-watch.sh (debounced trigger) ↓ wg-sync-policy.sh (parses wg0.conf → policy.json, atomic write) ↓ wg-policy-engine.sh (reads policy.json → iptables/ipset rules) ↓ Active firewall rules (WG_POLICY chain) ``` ### Tech Stack | Component | Technology | |-----------|------------| | **Backend** | Go 1.25, Gorilla Mux, GORM (SQLite) | | **Frontend** | Vue 3, TypeScript, Vite, TailwindCSS 4, vue-i18n 9 | | **Auth** | JWT (golang-jwt), TOTP (pquerna/otp), API Key | | **Webhooks** | Go net/http with retry + exponential backoff | | **Scheduling** | robfig/cron v3 | | **Firewall** | Bash, iptables, ipset, nftables, inotify-tools, jq | | **Container** | Docker (multi-stage build), docker-compose | --- ## Quick Start ### Option 1: Docker Compose (Recommended) ```bash # Clone the repository git clone https://git.datadunia.com/hainzero/WGRplane.git cd 03.wireguard-policy # Start both WGRplane and WireGuard docker compose up -d # Access the dashboard # http://localhost:10087 ``` The compose stack runs: - **WGRplane** on port `10087` (Go API + Vue frontend) - **WireGuard** container with host networking for kernel module access ### Option 2: Install Script ```bash # Run the automated installer (Ubuntu/Debian/CentOS) sudo ./install.sh install # Uninstall sudo ./install.sh uninstall ``` The install script handles Docker installation, repository cloning, `.env` creation, and service startup. ### Option 3: Manual Build ```bash # Build the Go binary cd app go build -o ../wgrplane . cd .. # Build the frontend cd app/frontend npm install && npm run build cd ../.. # Run ./wgrplane # Server starts on :10087 ``` ### Option 4: Systemd Service ```bash # Install the service file sudo cp wgrplane.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable wgrplane.service sudo systemctl start wgrplane.service # View logs journalctl -u wgrplane.service -f ``` --- ## API Documentation All API endpoints are served on port `10087`. Authentication is via API key header (`wg-rplane-datadunia`) or JWT Bearer token with optional TOTP. ### Authentication | Method | Header | Notes | |--------|--------|-------| | API Key | `wg-rplane-datadunia: ` | Set via `WG_API_KEY` env var. Default: `test-api-key`. | | JWT | `Authorization: Bearer ` | 15-minute expiry. Set secret via `JWT_SECRET` env var. | | TOTP | `X-TOTP: ` | Required if user has TOTP enabled. | ### Servers | Endpoint | Method | Description | |----------|--------|-------------| | `/api/servers` | `GET` | List all registered WireGuard servers. | | `/api/servers` | `POST` | Create a new server entry. Body: `{name, mode, publicKey, endpoint}`. | | `/api/servers/{id}` | `GET` | Get a single server by ID. | | `/api/servers/{id}` | `PUT` | Update server fields (name, mode, publicKey, endpoint). | | `/api/servers/{id}` | `DELETE` | Delete a server and cascade-remove its peers and webhooks. | ### Peers | Endpoint | Method | Description | |----------|--------|-------------| | `/api/servers/{id}/peers` | `GET` | List all peers for a server. | | `/api/servers/{id}/peers` | `POST` | Create a new peer. Body: `{publicKey, ip, allowAccess, allowInternet}`. Applies nftables rules in `forward` mode or triggers webhooks in `standalone` mode. | | `/api/peers/{id}` | `PUT` | Update a peer. Computes diffs and applies incremental nftables changes. | | `/api/peers/{id}` | `DELETE` | Delete a peer. Cleans up nftables rules and triggers webhooks. | | `/api/peers/{id}/config` | `GET` | Download the peer's WireGuard `.conf` file. | | `/api/peers/{id}/qrcode` | `GET` | Get a QR code PNG image of the peer config (for mobile import). | ### Webhooks | Endpoint | Method | Description | |----------|--------|-------------| | `/api/servers/{id}/webhooks` | `GET` | List webhooks for a server. | | `/api/servers/{id}/webhooks` | `POST` | Create a webhook. Body: `{name, url, template, customBody, customHeaders, subscribedActions, isEnabled, verifySSL}`. | | `/api/webhooks/{id}` | `DELETE` | Delete a webhook. | ### SMTP Settings | Endpoint | Method | Description | |----------|--------|-------------| | `/api/settings/smtp` | `GET` | Get current SMTP configuration. | | `/api/settings/smtp` | `POST` | Save SMTP settings for email notifications. | ### Statistics | Endpoint | Method | Description | |----------|--------|-------------| | `/api/stats` | `GET` | Global stats: total servers, peers, webhooks. | | `/api/servers/{id}/stats` | `GET` | Per-server stats: peer count, webhook count, server details. | ### WebSocket | Endpoint | Protocol | Description | |----------|----------|-------------| | `/ws/stats` | WebSocket | Real-time stats broadcast (5-second interval). Connects to the Hub for live peer/traffic updates. | --- ## Webhook Payload Spec Webhooks are triggered on peer lifecycle events (`peer_created`, `peer_updated`, `peer_deleted`, `policy_changed`). The engine supports three template modes: `default` (raw JSON), `mikrotik` (RouterOS-formatted), and `custom` (Go template). ### Default Payload ```json { "event": "peer_created", "timestamp": "2026-05-03T10:30:00Z", "server": { "id": 1, "name": "wg-server-01", "mode": "forward", "publicKey": "abc123...", "endpoint": "vpn.example.com:51820" }, "peer": { "id": 2, "publicKey": "xyz789...", "ip": "10.0.0.2", "allowAccess": ["192.168.1.0/24", "10.10.0.0/16"], "allowInternet": true, "enabled": true, "dataLimitGB": 0, "expiresAt": "0001-01-01T00:00:00Z" }, "policy": { "action": "created", "changes": ["peer_created"] } } ``` ### Mikrotik Template ```json { "action": "created", "peer": { "public_key": "xyz789...", "ip": "10.0.0.2", "allow_access": "[\"192.168.1.0/24\"]", "allow_internet": true }, "server": { "name": "wg-server-01", "mode": "forward" } } ``` ### Webhook Features - **Retry with backoff**: Failed deliveries retry up to 3 times with exponential backoff (2s, 4s, 8s). - **SSL verification**: Toggleable per-webhook via `verifySSL`. - **Custom headers**: Per-webhook header injection via `customHeaders` JSON. - **Global webhooks**: Set `isGlobal: true` to fire across all servers. - **Action filtering**: Subscribe to specific events via `subscribedActions` array. --- ## UI Features The frontend is a Vue 3 + TypeScript SPA with a glassmorphism design language. ### Design System - **Glassmorphism**: Frosted glass cards, buttons, and inputs with backdrop blur effects. - **TailwindCSS 4**: Utility-first styling with full dark mode support via `dark:` variants. - **Responsive**: Mobile-first layout that adapts to all screen sizes. ### Features | Feature | Description | |---------|-------------| | **Multi-language (i18n)** | English, Indonesian, and Chinese via vue-i18n 9. Locale auto-detected from browser. | | **Theme Switching** | Dark / Light / Auto (follows system preference) via `@vueuse/core`. | | **Real-time Stats** | WebSocket connection broadcasts live peer counts and traffic data every 5 seconds. | | **Charts** | Traffic visualization via Chart.js + vue-chartjs. | | **Toast Notifications** | Non-intrusive alerts via vue-sonner. | | **QR Code Import** | Generate scannable QR codes for quick mobile WireGuard client setup. | ### Frontend Structure ``` app/frontend/src/ ├── App.vue # Root component with theme/i18n providers ├── main.ts # App bootstrap (Vue, Router, i18n) ├── router/ # Vue Router definitions ├── i18n/ # Locale files (en.json, id.json, zh.json) ├── components/ # Glass UI components (Card, Button, Input, Toggle) ├── composables/ # Vue composables (useTheme, etc.) ├── views/ # Page components (Dashboard, Servers, Peers, Settings) └── types/ # TypeScript type definitions ``` --- ## Policy Firewall (`#Access`) The shell-based policy engine enforces per-peer firewall rules directly from `wg0.conf`. ### How It Works 1. Add `#Access` comments under each `[Peer]` block in `wg0.conf`. 2. The watcher daemon (`wg-sync-watch.sh`) detects file changes via `inotifywait`. 3. `wg-sync-policy.sh` parses the config and writes `policy.json` atomically. 4. `wg-policy-engine.sh` reads the JSON and applies iptables/ipset rules. ### Example `wg0.conf` ```ini [Interface] Address = 10.0.0.1/24 ListenPort = 51820 PrivateKey = PostUp = /usr/local/bin/wg-sync-policy.sh; /usr/local/bin/wg-policy-engine.sh PostDown = /usr/local/bin/wg-policy-cleanup.sh [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 = internet-only, client isolation applies ``` ### Why `#Access` Instead of `AllowedIPs`? WireGuard uses `AllowedIPs` for Cryptokey Routing. Putting destination IPs in the server's `AllowedIPs` would cause WireGuard to route traffic for those IPs into the client tunnel. The `#Access` comment cleanly separates firewall policy from routing configuration. ### CLI: `wg-policy-ctl` ```bash wg-policy-ctl status # Health check, lock status, rule counts wg-policy-ctl policy # View raw policy.json wg-policy-ctl rules # Inspect active iptables rules wg-policy-ctl ipset # View ipset mappings wg-policy-ctl reload # Force re-sync and re-apply wg-policy-ctl log # View dropped packet logs wg-policy-ctl stats # Connection statistics wg-policy-ctl validate # Validate policy.json schema ``` ### Prerequisites | Package | Required | Install | |---------|----------|---------| | `jq` | Yes | `apt install jq` | | `inotify-tools` | Yes (watcher daemon) | `apt install inotify-tools` | | `ipset` | Optional | `apt install ipset` | Without `ipset`, the engine falls back to per-rule iptables entries. This works for small deployments. For large peer counts, `ipset` provides O(1) lookup performance. --- ## Scheduler The Go backend runs three background cron jobs: | Schedule | Job | Action | |----------|-----|--------| | Daily 2:00 AM | `deleteExpiredPeers` | Removes peers past their `ExpiresAt` date. | | Daily 3:00 AM | `restrictOverLimitPeers` | Disables peers that exceeded `DataLimitGB`. | | 1st of month | `resetMonthlyUsage` | Resets `CurrentDataUsageBytes` to zero for all peers. | --- ## Plugins The plugin system provides a simple notification interface. Built-in plugins include: - **TelegramNotifier** -- Sends Telegram messages on events. - **SlackNotifier** -- Sends Slack messages on events. - **TrafficLogger** -- Logs traffic events for debugging. Plugins are loaded at startup via `PluginManager.LoadPlugins()` and receive events through `Trigger(event, payload)`. --- ## Configuration ### Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `WG_API_KEY` | `test-api-key` | API key for header-based authentication. | | `JWT_SECRET` | `secret` | Secret for JWT token signing. | | `APP_FRONTEND_DIR` | `/var/www/frontend` | Path to built frontend assets. | | `WG_RPLANE_MODE` | `forward` | Default server mode (`forward` or `standalone`). | ### Database WGRplane uses SQLite by default (`wgrplane.db`). Models auto-migrate on startup: - **Server** -- WireGuard server entries with mode and endpoint. - **Peer** -- Peer entries with IP, access rules, data limits, expiry. - **Webhook** -- Webhook configurations with templates and action filters. - **SMTPSettings** -- SMTP server configuration for email notifications. --- ## Project Structure ``` 03.wireguard-policy/ ├── app/ # Go backend + Vue frontend │ ├── handlers.go # REST API route handlers │ ├── main.go # Server bootstrap, routes │ ├── models.go # GORM models (Server, Peer, Webhook, SMTP) │ ├── auth.go # JWT, TOTP, API key auth middleware │ ├── nftables.go # nftables rule management (forward mode) │ ├── webhook.go # Webhook engine with retry/backoff │ ├── scheduler.go # Cron jobs (expiry, data limit, reset) │ ├── stats.go # WebSocket Hub for real-time stats │ ├── email.go # SMTP email notifications │ ├── plugins.go # Plugin system (Telegram, Slack, Logger) │ ├── validation.go # Input validators (IP, CIDR, PublicKey) │ ├── wg.go # WireGuard key generation, config export │ ├── i18n.go # Backend i18n (en/id/zh) │ ├── frontend/ # Vue 3 SPA (TypeScript, TailwindCSS) │ └── active.{en,id,zh}.json # Translation files ├── wg-sync-policy.sh # Parses wg0.conf → policy.json ├── wg-policy-engine.sh # Applies policy.json → iptables/ipset ├── wg-sync-watch.sh # inotifywait watcher daemon ├── wg-policy-ctl # CLI wrapper for management ├── wg-policy-cleanup.sh # Cleanup script for PostDown ├── wg-policy.service # Systemd unit for watcher daemon ├── wgrplane.service # Systemd unit for Go backend ├── install.sh # Automated installer (Docker + services) ├── Dockerfile # Multi-stage Docker build ├── docker-compose.yml # Docker Compose stack ├── build.sh / build.bat # Installer rebuild scripts └── README.md # This file ``` --- ## License This project builds upon concepts from WGDashboard (donaldzou/WGDashboard) with modifications for policy.json API integration and dynamic firewall enforcement.