From a4bbc6ef3d2517935718c82dc31e4c767f058e82 Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Wed, 8 Jul 2026 13:20:58 +0300 Subject: [PATCH] docs: document opn-cli for managing the OPNsense site routers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a "Managing the OPNsense routers" note to generic.md §11 (Network): opn-cli talks to a site's OPNsense over its API, credentials in a per-site ~/.opn-cli/.yml (api_key/secret + url + internal-CA ca:), --config is required. The split-horizon .internal records and public-name host-overrides live in each router's Unbound, so `unbound host list/create/update/delete` is how you inspect and fix them: opn-cli --config ~/.opn-cli/hanzalova.yml unbound host list opn-cli --config ~/.opn-cli/kosherinata.yml unbound host list Cross-referenced from reverse-proxies.md §2 as the tool to reach for when a mesh client resolves a name to the wrong host — exactly the rob.fyi case. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_016fKZzDpvjiJ9eYbPGgJvUP --- generic.md | 11 +++++++++++ reverse-proxies.md | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/generic.md b/generic.md index f9ac398..2bed521 100644 --- a/generic.md +++ b/generic.md @@ -490,6 +490,17 @@ This is the environment these apps deploy into. Claude Code should assume it. - Per-site OPNsense router handles WAN/LAN and the WireGuard endpoints. - Internal DNS split-horizon via `.internal` domains (`hanzalova.internal`, `kosherinata.internal`, etc.). +#### Managing the OPNsense routers (`opn-cli`) + +Interact with a site's OPNsense router over its API with [`opn-cli`](https://github.com/andeman/opn-cli). Credentials/endpoint live in a **per-site config file** under `~/.opn-cli/.yml` (holding `api_key` / `api_secret`, the router `url`, and `ca:` pointing at the internal root CA — operator-local, never committed). The config is required and selects which router you talk to; **always pass `--config`** (`-c`), or `opn-cli` falls back to a nonexistent `~/.opn-cli/conf.yaml` and errors: + +```sh +opn-cli --config ~/.opn-cli/hanzalova.yml unbound host list +opn-cli --config ~/.opn-cli/kosherinata.yml unbound host list +``` + +The **split-horizon `.internal` records and any host-overrides for public names live in each router's Unbound** — so `unbound host {list,create,update,delete}` is how you inspect and fix them. This is the tool to reach for when a mesh client resolves a name to the wrong host (`reverse-proxies.md` §2): list the overrides, confirm where the name points, and correct the offending record. Other subcommands (`firewall`, `route`, `haproxy`, `ipsec`, `openvpn`, …) manage the rest of the router the same way. + ### TLS / PKI - Internal PKI via Smallstep `step-ca` at `https://ca.internal`. - Every host runs `step.service` (the Smallstep renewer) which keeps the host's cert fresh. **Certs are issued with a 24-hour expiry** and renewed continuously — services must tolerate cert rotation, not assume certs are stable for the life of the process. diff --git a/reverse-proxies.md b/reverse-proxies.md index baa9689..98a27c9 100644 --- a/reverse-proxies.md +++ b/reverse-proxies.md @@ -41,6 +41,12 @@ A service can be reached two ways, and they are **not** interchangeable: This is why dual-audience services get **two vhosts** on the same proxy — one public (LE), one internal (`lair` CA) — usually sharing one webroot and one upstream. +> **Debugging a wrong-host resolution.** When a mesh client reaches the wrong site (wrong +> cert / wrong content), the culprit is usually a split-horizon record in the site +> router's Unbound — a missing `*.internal` override, or a stale host-override sending a +> public name to the wrong proxy. Inspect and fix it with `opn-cli` (`generic.md` §11 +> Network): `opn-cli --config ~/.opn-cli/.yml unbound host list`. + ## 3. Per-vhost cert choice | vhost audience | name | cert | doc |