Date: 2026-07-19
Proposed
AllowedIPs on the client [Peer] config block controls two different things at once, and it's important not to conflate them:
Today, (1) is a single global free-text field (Settings.wgClientAllowedIps) with every enabled gateway peer's site CIDR appended automatically, uniformly, to every client — there is no full/split distinction and no way to reason about what "split" should even mean. (2) is handled correctly and separately: nftables rules computed from RoleResourceGrant at the hub (F-08, ADR-0006) — a client can route toward a network and still have every packet into it dropped at the hub if no grant covers it.
The open question, raised when scoping split tunnel as a real feature (not just "whatever string the admin types"), was: should the routed network list for a client be derived from that peer's current role grants (route only to sites/subnets the peer's role can currently reach something in), or should it be a fixed list of every known network, independent of grants, with only a binary full-vs-split choice left to the admin?
The grant-derived option was the initial instinct — it reads as "tighter," routing nothing the peer can't use anyway. It has a real operational cost: every role-grant change (a resource added to a site, a grant added or revoked, a new site created) would need to reconsider, and potentially rewrite, the AllowedIPs line of every peer whose role touches that change. A changed AllowedIPs line means an already-provisioned client — a phone that scanned a QR code weeks ago — no longer has a locally correct config until it re-imports. WireGuard has no live config-push mechanism to a road-warrior client; the only way to update AllowedIPs on a device is to give the user a new .conf or QR code to import by hand, or to run a management daemon on every client to do it automatically (wg-quick does not poll, Islandr does not run a client-side agent). ACL grants are expected to change routinely as an organisation's access needs evolve — that is the entire point of role-based grants existing separately from static peer config. Tying AllowedIPs to grants means routine ACL administration silently breaks already-deployed client configs.
AllowedIPs for a client peer lists every known network — the VPN subnet plus every site's CIDR with an enabled gateway peer — regardless of what that peer's role is currently granted. The only per-deployment choice is binary: full tunnel (0.0.0.0/0) or split tunnel (the fixed network list above). Role-grant changes never touch AllowedIPs and therefore never require a peer to re-import its config. Access enforcement inside those routed networks remains entirely the hub firewall's job (F-08) — unchanged from today, and the reason this is safe: a peer routing toward a site it has no grant in still can't reach anything there, because nftables drops it at the hub. AllowedIPs answers "where might traffic go," never "where is traffic allowed" — that second question already has a correct, separate answer.
RoleResourceGrant) — completely untouched by this decision.Baseline: A — fixed network list (VPN subnet + all gateway-backed site CIDRs), binary full/split toggle (the decision).
| Criterion (weight) | A: fixed list, binary toggle (baseline) | B: grant-derived AllowedIPs per peer | C: keep today's free-text global field, no toggle |
|---|---|---|---|
| Client config stays valid across ACL changes (5) | 0 | −1 | 0 |
| Consistency with hub-only enforcement model (4) | 0 | −1 | 0 |
| Implementation effort (3) | 0 | −1 | +1 |
| Operator predictability ("what will this peer route to") (3) | 0 | 0 | −1 |
| Minimizes what's routed toward the client, defense-in-depth (2) | 0 | +1 | 0 |
| Weighted total | 0 | −7 | 0* |
* C ties A on the weighted total but loses on the criterion this ADR exists to fix (predictability / the full-vs-split concept not existing at all) — included for completeness, not as a live contender.
A wins because it treats AllowedIPs purely as a routing-scope decision, decoupled from the authorization model that already lives correctly at the hub, and because it never requires re-provisioning a device over an ACL change — which, for role-based access that is expected to change often, is the property that actually matters.
Positive
.conf/QR code — the operational cost this ADR exists to avoid.Risks created
wg show/ip route, even though nothing is reachable there). Accepted: a road-warrior client already trusts the hub with far more (its own assigned IP, hub public key); the CIDR list is not sensitive by itself, and this is the standard WireGuard AllowedIPs-as-routing-scope pattern documented for the tool.AllowedIPs.Accepted trade-offs
AllowedIPs in v1. If a genuine future need for OS-level routing minimization per peer emerges (e.g. a hard regulatory requirement, not just "feels tighter"), that is a new, separate decision — this ADR's baseline does not preclude one, but nothing in scope today asks for it.AllowedIPs is deliberately not derived from it.peer/PeerService.java:539-580 — renderConf, where AllowedIPs is assembled today (single global string + auto-appended site CIDRs).