CIDR allowlist enforced at the agent
Defence in depth above the cloud-side scope check. Heartbeat-pushed CIDR list, locally enforced; refuses out-of-scope tasks without a single socket touched.
Last updated May 27, 2026
What it is
A second wall around the agent’s network scope. The cloud already enforces “submitted IPs belong in the claimed CIDR” on result ingest. With this feature, the agent ALSO refuses to probe anything outside its authorised scope, even if a compromised control plane tells it to.
What it produces
- Pushed on every 30 s heartbeat: the agent’s authorised CIDR list
(every active
internal_cidrtarget bound to this agent) - Local in-memory cache on the agent, refreshed each heartbeat
- Refusal at the dispatcher: an out-of-scope
discover_networktask errors withrefusing task: cidr X is outside agent's authorised scope [...]. No socket opens.
What to use it for
- Defence-in-depth posture review (single-point-of-trust failures are the most common breach pattern; this PR adds the symmetric wall)
- Customer security review: hand them this doc + the source references for the local check
- Quick scope changes: pause a target on the dashboard, the next heartbeat removes that CIDR from the agent’s accepted scope
Why it matters
A SaaS that ships an agent into customer networks has to assume
its own control plane will be compromised someday. Without
agent-side enforcement, that day means the attacker can task
every customer agent to probe anything reachable: 8.8.8.8,
AWS-metadata, internal HR systems out of scope. With agent-side
enforcement, the attacker can task the agents but the agents
refuse.
Why it’s valuable
- No code path bypasses it. Every task kind (discover, proxy,
future kinds) goes through the same
agentCheckScopeshim. - DNS-rebinding mitigated. URL hosts are resolved; every resolved IP must be in scope. A hostname that resolves to one in-scope + one out-of-scope IP gets refused.
- CIDR subset check, not point check. A control plane that says “scan 10.0.0.0/8” when only “10.0.0.0/24” is enrolled gets refused; we don’t need to enumerate IPs to know it’s outside.
How to use it
Automatic: the agent enforces by default. Bind / unbind targets on the dashboard; the scope updates on the next heartbeat (~30 s).
The agent’s /healthz endpoint shows the cached scope so an
operator on the host can verify what their agent currently
accepts:
curl http://127.0.0.1:7780/healthz
# {"scope":["10.0.0.0/24","192.168.5.0/24"], "scope_loaded": true, ...}