Files
2026-05-08 02:01:46 +07:00

5.9 KiB

AGENTS.md -- WireGuard Policy Firewall + WGRplane

Generated: 2026-05-08 Project: 03.wireguard-policy

OVERVIEW

Hybrid project: (1) WireGuard Policy Firewall -- shell script iptables/ipset engine driven by wg0.conf #Access comments. (2) WGRplane -- Go binary (port 10087) serving REST API + Vue 3 SPA for WireGuard control plane management.

STRUCTURE

03.wireguard-policy/
├── app/                    # WGRplane: Go backend + Vue 3 frontend (see app/AGENTS.md)
│   ├── *.go                # 13 Go files, flat package (no subdirs)
│   ├── frontend/           # Vue 3 SPA (see app/frontend/src/AGENTS.md)
│   └── docs/               # Swagger auto-generated (DO NOT EDIT)
├── wg-sync-policy.sh       # Parses wg0.conf → policy.json (atomic write + flock)
├── wg-policy-engine.sh     # Reads policy.json → iptables/ipset WG_POLICY chain
├── wg-sync-watch.sh        # inotifywait daemon, debounces wg0.conf changes
├── wg-policy-lib.sh        # Shared shell library (source only, never execute directly)
├── wg-policy-ctl           # CLI wrapper for operator use
├── wg-policy-cleanup.sh    # PostDown cleanup (run by WireGuard)
├── *.service / *.timer     # systemd units for daemon + health check
├── install.sh              # Unified installer (embeds all scripts, built by build.sh)
├── build.sh / build.bat    # Rebuilds install.sh from source scripts
├── Dockerfile              # Multi-stage: Go build + frontend build
└── README.md               # Full user documentation

WHERE TO LOOK

Task Location
Policy firewall logic wg-sync-policy.sh, wg-policy-engine.sh, wg-policy-lib.sh
Firewall rule chain wg-policy-engine.sh -- WG_POLICY iptables chain
Policy JSON schema wg-sync-policy.sh output / wg-policy-engine.sh input
WGRplane API handlers app/handlers.go
WGRplane DB models app/models.go
Auth middleware app/auth.go (CAUTION: see known issues)
nftables rules (forward mode) app/nftables.go
Webhook delivery app/webhook.go
Vue frontend app/frontend/src/
i18n translations app/frontend/src/i18n/locales/ (en, id, zh)
Backend i18n app/active.{en,id,zh}.json
Systemd service config wg-policy.service, wgrplane.service

CRITICAL DESIGN RULES

Policy Firewall -- #Access MUST NOT be changed

  • WireGuard AllowedIPs on the server side = Cryptokey Routing, not firewall whitelist
  • Putting destination IPs in server's AllowedIPs breaks routing (WG tunnels those packets INTO client)
  • #Access comment is the ONLY correct way to declare firewall destinations per-peer
  • NEVER refactor to parse targets from AllowedIPs -- architecturally incorrect

Atomic Writes

  • Always mv -f tmp target for policy.json -- never write directly
  • wg-sync-policy.sh uses flock -- never bypass locking

Rollback

  • wg-policy-engine.sh creates WG_POLICY_BAK chain; traps ERR for rollback

No SaveConfig

  • WireGuard SaveConfig = true strips ALL comments including #Access -- NEVER enable

KNOWN ISSUES / GOTCHAS

  • AuthMiddleware NOT applied: auth.go defines AuthMiddleware but it is NOT wired to any routes in main.go. All API endpoints currently unprotected (auth header still checked inside handlers via manual if-check, but middleware chain is absent).
  • TOTP secrets in-memory: totpSecrets map in auth.go is not persisted; lost on restart.
  • WebSocket stats are MOCK: stats.go broadcasts randomly generated numbers, not real WireGuard traffic.
  • Plugin system is stub: plugins.go TelegramNotifier/SlackNotifier just print to stdout.
  • Binary + DB in app/: wgrplane binary and wgrplane.db live in app/ (non-standard, intentional).
  • Duplicate i18n: app/active.*.json (backend i18n) and app/frontend/src/i18n/locales/ (frontend i18n) are separate systems.

ANTI-PATTERNS

  • Never parse firewall targets from AllowedIPs -- use #Access only
  • Never write policy.json without atomic mv + flock
  • Never run wg-policy-lib.sh directly (source-only library)
  • Never enable SaveConfig = true in wg0.conf
  • Do NOT edit app/docs/docs.go -- auto-generated by swaggo
  • Do NOT put business logic in app/main.go -- it's bootstrap only

COMMANDS

Policy Firewall

wg-policy-ctl status    # Health: interface, JSON validity, rule counts
wg-policy-ctl validate  # Validate policy.json without applying
wg-policy-ctl rules     # Show active WG_POLICY iptables rules
wg-policy-ctl reload    # Force re-sync from wg0.conf + re-apply
wg-policy-ctl policy    # Show raw policy.json
wg-policy-ctl log       # Show dropped packet logs (rate-limited)
systemctl restart wg-policy.service
journalctl -u wg-policy.service -f

WGRplane (Go App)

cd app && go build -o ../wgrplane .          # Build Go binary
cd app/frontend && npm install && npm run build  # Build Vue frontend
./wgrplane                                    # Run (serves :10087)
systemctl restart wgrplane.service
journalctl -u wgrplane.service -f
curl -H "wg-rplane-datadunia: test-api-key" http://localhost:10087/api/servers

Installer

./build.sh           # Rebuild install.sh from source scripts (Linux)
./build.bat          # Rebuild install.sh from source scripts (Windows)
sudo ./install.sh install    # Full install (deps + scripts + systemd + service)
sudo ./install.sh uninstall  # Remove all

DEPENDENCIES

  • Shell: jq, inotify-tools (required); ipset (optional, O(1) lookup)
  • Go: 1.25.1, SQLite (glebarez/sqlite), Gorilla Mux, GORM, JWT, TOTP, WebSocket
  • Frontend: Vue 3, TypeScript, Vite, TailwindCSS 4, vue-i18n 9
  • Auth headers: wg-rplane-datadunia: <KEY> (API key) | Authorization: Bearer <JWT> | X-TOTP: <CODE>
  • Env vars: WG_API_KEY (default: test-api-key), JWT_SECRET, WG_RPLANE_MODE (forward|standalone), APP_FRONTEND_DIR