Newer
Older
openstack-caracal-dc-dc / docs / changelog-20260714-netbox-importers-waf-useragent.md

2026-07-14 -- the two pynetbox importers could NEVER have written upstream (WAF User-Agent)

A C2 (feed-the-carve-upstream) blocker, found while establishing C2's feasibility. Measured, not inferred.

The gap

The upstream apex netbox.baldurkeep.com sits behind a filter that 403s the default Python User-Agent (references/platform-traps.md). The same token works from curl -- it looks EXACTLY like an auth failure and is not. MEASURED 2026-07-14 from vcloud: curl -> HTTP 200; urllib/pynetbox -> HTTP 403.

The stdlib tools (prod-draft-dump.py, sandbox-seed.py, d115-office-carve.py) already send an accepted UA (curl/8.5.0). But the two pynetbox-based importers -- roles-aggregates-import.py and dc-dc-prefixes-import.py -- built pynetbox.api(url, token) with the default UA and never overrode it. So they would 403 against upstream: they could only ever have written to the sandbox (which has no WAF), never to the apex C2 must feed. The gap was invisible because every real run so far was against the sandbox.

The fix

get_nb() in both importers now sets the accepted UA on the underlying requests session:

nb.http_session.headers["User-Agent"] = "curl/8.5.0"

Same value and rationale as the stdlib tools -- pynetbox exposes its requests.Session as .http_session, so one line covers every call it makes.

Tests

  • tests/dc-dc-prefixes-import/ -> 86: the fake now has an .http_session (mirroring pynetbox's real one), and the test asserts AT RUNTIME that get_nb() sets the UA to curl/8.5.0 -- not just that the string appears in source.
  • tests/roles-aggregates-import/ -> 20: grep assertion that get_nb sets the WAF-safe UA.

GAUNTLET ALL GREEN (58); repo-lint 0 fail.

What this does and does NOT unblock

Does: removes the WAF 403 from the two pynetbox importers' upstream write path.

Does NOT (still open for C2):

  • pynetbox is NOT installed on vcloud (measured). The two importers must run on a host that HAS it and can reach upstream -- office1-netbox has pynetbox 7.0.0. d115-office-carve.py is stdlib and runs anywhere.
  • The sandbox re-verify (hardened sandbox-fidelity-check.py) needs the operator-held sandbox token.
  • The upstream write itself is a PRODUCTION IPAM mutation and stays operator-gated.

Revert

git revert <this commit>

The single line in each get_nb() is the only functional change; the rest is tests. No NetBox was written.