From 41d09bc998c001ac2b5e6b7c039c3105b0ed3f43 Mon Sep 17 00:00:00 2001 From: William Gill Date: Wed, 16 Sep 2026 15:09:35 -0500 Subject: [PATCH] docs(openbao): the Proxmox per-VM firewall, and reaching the API over SSH Two things that cost real debugging time on a live deploy. A Docker-published port on a Proxmox guest is filtered by the per-VM firewall on the PVE host, upstream of everything in the guest. The failure is maximally misleading: inside the VM the DNAT rule, the FORWARD jumps, the docker-proxy socket and a local curl to the bind address are all correct and the listener answers 503, while a LAN client times out. SSH working proves nothing -- it only proves a rule exists for 22. The decisive test is `tcpdump -ni eth0 'tcp port 8200'` capturing zero packets during a failed connection, which says the guest never saw them. And: the publish is pinned to OPENBAO_BIND, so a browser on another subnet cannot reach the UI. Document the SSH tunnel rather than widening the publish -- the generated cert already carries DNS:localhost + IP:127.0.0.1, so https://localhost:8200 validates against it unchanged. That is also the better way to run the first `operator init`, since the unseal keys are then shown in a browser instead of a root shell's scrollback. Co-Authored-By: Claude Opus 5 --- deployments/openbao/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/deployments/openbao/README.md b/deployments/openbao/README.md index 538a4c2..5cff261 100644 --- a/deployments/openbao/README.md +++ b/deployments/openbao/README.md @@ -136,6 +136,14 @@ This is a secrets store on the LAN, not a public service. Two things to know: `OPENBAO_ADDR` when that is an IP, so the API listens only on that LAN IP. To restrict by *source host*, use a `FORWARD`/`DOCKER-USER` rule or network segmentation, not `INPUT`. +- **On a Proxmox guest, the per-VM firewall filters before the guest sees + anything.** If the VM's NIC has `firewall=1` and the datacenter firewall is + enabled, a Docker-published port needs its own `IN ACCEPT -p tcp -dport 8200` + rule in `/etc/pve/firewall/.fw`. SSH working does **not** prove the path + — it only proves there is a rule for 22. The give-away is that everything + inside the guest looks perfect (`curl` to the bind address answers, the DNAT + and `FORWARD` jumps are present) while `tcpdump -ni eth0 'tcp port 8200'` + captures zero packets during a failed connection. - **mTLS (optional):** to require the tape host to present a client cert, enable the `tls_require_and_verify_client_cert` stanza in [`config.hcl`](config.hcl) and issue the tape host a client cert from the same CA. @@ -144,6 +152,21 @@ And **initialise the vault immediately** after `deploy.sh` — an uninitialised vault reachable on the LAN can be `bao operator init`'d by anyone who connects, handing them the root token and unseal keys. +### Reaching it without LAN access + +The API publishes on `OPENBAO_BIND` only, so a browser on another subnet (or +behind a firewall you do not control) cannot reach it. Tunnel over SSH instead +of widening the publish — the generated cert already carries `DNS:localhost` +and `IP:127.0.0.1` in its SANs, so it validates as-is: + +```bash +ssh -L 8200::8200 root@ +# then browse https://localhost:8200 +``` + +This is also the better way to do the first `operator init`: the unseal keys and +root token are shown in your browser instead of a root shell's scrollback. + ## Backup / DR The vault is the sole recovery path for encrypted tapes — back it up: