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 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 15:09:35 -05:00
co-authored by Claude Opus 5
parent 185f404549
commit 41d09bc998
+23
View File
@@ -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/<vmid>.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:<bind-addr>:8200 root@<host>
# 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: