fix(guard): operator-scoped XFF hop peers + tailnet allowlist (live-incident fix) (#650)

Two coupled hardenings from the chain-peers adversarial rounds plus the
root-cause fix for the live post-deploy incident where the CEO was
blocked from the panel ('IP not allowed: 100.x.x.x').

Hop peel-set: the whole docker bridge pool leaves the XFF hop set — hops
are now loopback plus operator-named single addresses only
(ROBOCO_GUARD_TRUSTED_CHAIN_PEERS, plain IPs; CIDR entries rejected with
a warning because a range readmits sibling containers). Default-empty
closes the CGNAT-forge residual outright; a gateway-fronted Tailscale
Serve deploy sets its real gateway IP, and a rate-limited detection log
names exactly that IP when an unconfigured host-proxied tailnet chain is
seen, so the silent-regression shape is observable. The connecting-peer
gate (may nginx present XFF at all) deliberately keeps the broad bridge
pool — different check, unchanged.

Incident root cause: guard-core's whitelist is an EXCLUSIVE allowlist
(any non-member is refused), so honestly resolving the tailnet client IP
made ip_security reject the CEO. The tailnet CGNAT range joins
_guard_whitelist() deliberately: Tailscale authenticates device
membership before a packet arrives, real-IP stamping still buys correct
attribution, and any future non-tailnet exposure keeps full scrutiny.
Both compose files now pass ROBOCO_GUARD_EMERGENCY_WHITELIST through to
the orchestrator (the operator escape hatch previously did nothing in a
compose deploy).

NAS is running ROBOCO_GUARD_PASSIVE_MODE=true as interim mitigation —
flip back to false when this deploys. 66 tests.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-23 00:05:29 +02:00
committed by GitHub
co-authored by Renn F
parent da4d9b333d
commit 3806317aa7
8 changed files with 372 additions and 43 deletions
+4
View File
@@ -652,6 +652,10 @@ services:
ROBOCO_GUARD_ENABLED: ${ROBOCO_GUARD_ENABLED:-true}
ROBOCO_GUARD_PASSIVE_MODE: ${ROBOCO_GUARD_PASSIVE_MODE:-false}
ROBOCO_GUARD_FAIL_SECURE: ${ROBOCO_GUARD_FAIL_SECURE:-false}
# Emergency-lockdown whitelist escape hatch. Without this line here,
# setting it in .env silently does nothing — only vars listed in this
# stanza reach the container.
ROBOCO_GUARD_EMERGENCY_WHITELIST: ${ROBOCO_GUARD_EMERGENCY_WHITELIST:-}
volumes:
# Docker socket - allows spawning agent containers
- /var/run/docker.sock:/var/run/docker.sock