docs: add WireGuard AllowedIPs server vs client architecture rule
NexusGuard CI / server-core-test (push) Failing after 4s
NexusGuard CI / server-core-build (push) Has been skipped
NexusGuard CI / device-agent-test (push) Failing after 3s
NexusGuard CI / device-agent-cross-build (amd64, linux) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (amd64, windows) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (arm64, linux) (push) Has been skipped
NexusGuard CI / dashboard-test (push) Failing after 4s
NexusGuard CI / dashboard-dist (push) Has been skipped

Documents the critical distinction between server-side (kernel routing table)
and client-side (conf file) AllowedIPs to prevent the same bug from recurring.

Server-side: always InternalIP/32 per peer.
Client-side: use EndpointAllowedIPs from DB.
This commit is contained in:
datadunia
2026-06-04 11:23:03 +07:00
parent 352714f4e3
commit c23814bcda
+37
View File
@@ -83,6 +83,43 @@ NexusGuard SD-WAN Suite — Enterprise Zero-Trust SD-WAN with WireGuard tunnelin
- **NEVER** force push - **NEVER** force push
- **NEVER** create cross-phase workarounds - **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 ## UNIQUE STYLES
- **Zero-Attack Surface**: `/auth/register` locked; admin via `-create-admin` CLI only - **Zero-Attack Surface**: `/auth/register` locked; admin via `-create-admin` CLI only
- **Stealth Agent**: No `/etc/wireguard/` — config in memory only - **Stealth Agent**: No `/etc/wireguard/` — config in memory only