Newer
Older
openstack-caracal-dc-dc / docs / changelog-20260713-opnsense-api-client.md

2026-07-13 -- D-113(a2) step 1: a thin OPNsense REST API client

First delivery under the D-113 ruling (ADOPTED: option (a2) -- stay on OPNsense, move configuration OFF hand-authored config.xml and ONTO the documented REST API).

What shipped

  • scripts/opnsense-api.sh -- thin REST client. [--dry-run] <GET|POST> <api-path> [json-body].
  • tests/opnsense-api/run-tests.sh -- offline harness, 21 PASS, no network, no real key.
  • Gauntlet ALL GREEN (53 harnesses, was 52). repo-lint 0 fail.

Why this exists (the D-113 argument, in one line)

Hand-authoring the appliance's GUI-owned config.xml was the SINGLE root cause of DOCFIX-191 (no sshd/key -> lockout), DOCFIX-192 (no console), DOCFIX-193 (no DHCP), and the 667-element migration self-heal we ended up depending on. None of those is expressible through the API, where sshd, DHCP and firewall rules are typed resources with defaults. You cannot forget to enable sshd in a format where sshd is a typed field.

Design decisions worth keeping

The secret never reaches argv. Credentials are read from a file and passed to curl via --config on STDIN. curl -u key:secret would put the secret in the process's argv, where ANY user on the box can read it out of ps. Harness case T9 stubs curl with an argv recorder and asserts the secret is absent from argv and present on stdin -- so a future "simplification" to curl -u turns the harness red. That test is the point of the file.

The API host is never inferred (hard rule 2). $OPNSENSE_API_HOST unset is a loud failure, not a default of 10.10.0.1. Harness T3.

--insecure is deliberate and scoped. The edge presents a self-signed cert that OPNsense REGENERATES ON EVERY BOOT (measured: "Created web GUI TLS certificate" appears in the config revision trail after each reboot), so pinning it is pointless. Acceptable ONLY because the transport is a private lab LAN leg (virbr2). Not to be copied to anything tenant-facing.

Dry-run reads no credentials (T11), which is what lets the harness prove URL construction offline.

Not done yet -- the blocking step

The API is alive but has no key. Measured on the live edge 2026-07-13: lighttpd IS listening on 443/80 and the API answers 401 (i.e. it is up and demanding auth), and root has 0 API keys.

Minting a key must happen in the GUI (System > Access > Users > root > API keys > "+"). That is deliberate: writing apikeys into config.xml by hand would be the exact anti-pattern D-113 just retired. The downloaded key/secret file goes to ~/vr1-office1-creds/opnsense-api.txt (operator-only; never read into agent context -- SEC-007 covers that directory).

Once the key exists, the proof-of-path is: drive the DHCP subnet we pushed by hand yesterday through the API instead, and confirm it round-trips. If the API can express it, the template retires to a MINIMAL bootstrap config (sshd + root key + console + the API key) and everything else -- DHCP, firewall, interfaces -- moves to the API. If it cannot, that is the signal that OPNsense is not buying the abstraction we are paying for, and D-113 gets revisited.

Revert

git rm scripts/opnsense-api.sh
git rm -r tests/opnsense-api/

Nothing live is touched by this change; the client has never been run against the edge (no key exists yet).