diff --git a/AGENTS.md b/AGENTS.md index c1a8c33..3ae47b3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,6 +83,43 @@ NexusGuard SD-WAN Suite — Enterprise Zero-Trust SD-WAN with WireGuard tunnelin - **NEVER** force push - **NEVER** create cross-phase workarounds +## WIREGUARD AllowedIPs — SERVER vs CLIENT (CRITICAL) + +WireGuard `AllowedIPs` has **two different meanings** depending on context. Mixing them causes only 1 peer to work. + +### Rule +| Context | Where | Value | Purpose | +|---------|-------|-------|---------| +| **Server-side** (kernel `wg set`) | `SyncLocalPeers()` → `peer_sync.go` | `InternalIP/32` per peer | WireGuard routing table — each IP must belong to exactly ONE peer | +| **Client-side** (`.conf` file) | `getDeviceConfig()` → `peers.go` | `EndpointAllowedIPs` from DB (e.g. `/24`, `0.0.0.0/0`) | Tells client which traffic routes through VPN tunnel | +| **Firewall** (nftables) | `AddForwardRule()` → `nftables_linux.go` | `EndpointAllowedIPs` from DB | Controls which IPs peer can reach via forwarding | + +### Why +WireGuard uses `AllowedIPs` as an **internal routing table**. When two peers share the same `/24`, WireGuard assigns the AllowedIPs to the **last peer configured only** — the first peer gets `(none)`. This is not a bug; it's how WireGuard routing works. + +### Anti-pattern +```go +// WRONG — uses client config for server-side routing +if d.EndpointAllowedIPs != "" { + allowedIPs = d.EndpointAllowedIPs // "10.172.21.0/24" ← SAME for both peers! +} +``` + +### Correct pattern +```go +// CORRECT — server-side always /32 per peer +allowedIPs := *d.InternalIP + "/32" // "10.172.21.2/32" for gogo2 +if d.AllowInternet { + allowedIPs = "0.0.0.0/0" +} +// Do NOT override with EndpointAllowedIPs here +``` + +### Database field: `endpoint_allowed_ips` +- Used for **client config** and **firewall rules** +- NOT used for server-side WireGuard kernel config +- Example: `10.172.21.0/24` allows peer to reach full subnet via firewall + routes full subnet through VPN on client + ## UNIQUE STYLES - **Zero-Attack Surface**: `/auth/register` locked; admin via `-create-admin` CLI only - **Stealth Agent**: No `/etc/wireguard/` — config in memory only