bao: serve the browser UI to admins via a loopback-only listener
openbao gains a second listener, `ui`, on 127.0.0.1:<deploy.bao.uiPort> (default 8204) with TLS off and no client-certificate requirement, and `ui = true`. The existing listeners are unchanged. An nginx inside the store's container, on 127.0.0.1:<deploy.bao.uiProxyPort> (default 8206), forwards only /ui/ and /v1/ to it, redirects / to /ui/, answers 403 on sys/unseal, sys/seal, sys/step-down, sys/rekey* and sys/generate-root*, and 404 on everything else. The gateway on the store's host serves `swarm.bao.ui.domain` (default bao-ui.<swarm>) behind the authelia auth_request subrequest, proxying to that nginx; the name joins serviceDomains and localNames like every other gateway-published swarm service. authelia gets an access_control rule restricting that name to group:admins, rendered wherever authelia runs, since the default policy admits any session. Trade-off, ruled by the operator on the parent issue: the UI listener asks for no client certificate, so on that door a bao token alone is the credential. Three comments and a doc line claimed every API listener demands a client certificate; they now except the loopback UI listener. The module-eval case counting declared listeners excludes `ui` by name, as it already did `metrics`. On a self-signed gateway, the UI's name is a swarm service name, so its host requests the services leaf from the store. `swarm-services-cert` sits Before= and RequiredBy= the gateway's cert import, which nginx Requires=. On a host whose only swarm name is the UI, that would hold nginx, and with it the stream passthrough every reader dials, on a login to a store that may be sealed. hive-tls drops those two edges exactly when the UI is the only local swarm name: nginx starts on the existing hive-leaf fallback, and the script's existing re-import reloads nginx once the leaf issues. Every other host keeps both edges.
This commit is contained in:
parent
a86379eac5
commit
3898ca33c7
10 changed files with 376 additions and 29 deletions
|
|
@ -118,8 +118,8 @@ which accepts the leaf `/var/lib/swarm-bao-pki/granter.pem`. Every
|
|||
`swarm-bao-*-policy` unit then logs in with that leaf. The controller's unit
|
||||
mounts the KV and pki engines and writes the `swarm-controller` role, and each
|
||||
sibling unit writes its own principal's policy and role. Every one runs on the
|
||||
host, because every API listener demands a client certificate and the host is
|
||||
the side that has one.
|
||||
host, because every API listener but the loopback UI one demands a client
|
||||
certificate and the host is the side that has one.
|
||||
|
||||
**Confirm with `systemctl status swarm-bao-granter-role`**, which should log
|
||||
`Uploaded policy: bao-granter` and `Data written to: auth/cert/certs/bao-granter`.
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ leaf it signed and folded into `trust-bundle.pem`. On the host running
|
|||
the store it's also at
|
||||
`/var/lib/swarm-bao-services-pki/services-root.pem`. That's the file to
|
||||
hand a browser, and reading it needs no store login — which matters,
|
||||
because every store listener demands a client certificate.
|
||||
because every store listener but the loopback UI one demands a client
|
||||
certificate, and that one is gated behind authelia to the `admins`
|
||||
group, not open to an anonymous browser fetching a trust anchor.
|
||||
|
||||
**The granting unit generates the root once, and never again.** It asks
|
||||
the mount whether it already has an issuer (`bao list pki/issuers`)
|
||||
|
|
|
|||
|
|
@ -428,6 +428,25 @@ agent CA. The listener reads both from `listener-client-ca.pem`, which no
|
|||
cert-auth role pins. The passthrough carries
|
||||
whichever certificate the reader presents, unchanged.
|
||||
|
||||
## The browser UI
|
||||
|
||||
OpenBao's built-in web UI is at `https://bao-ui.<swarm domain>/`
|
||||
(`swarm.bao.ui.domain`), for members of authelia's `admins` group only. Log in
|
||||
with a bao token. The gateway vhost checks the session with authelia and
|
||||
proxies to an nginx inside the store's container on
|
||||
`127.0.0.1:<deploy.bao.uiProxyPort>`. That nginx forwards `/ui/` and `/v1/` to
|
||||
a second openbao listener on `127.0.0.1:<deploy.bao.uiPort>`, answers 403 on
|
||||
the unseal, seal, step-down, rekey and generate-root endpoints, and 404 on
|
||||
everything else. ⚠️ That listener asks for **no client certificate**, because
|
||||
a browser has none to present. So on this one door a bao token is the whole
|
||||
credential. Anything on the store's host that can dial loopback, and any
|
||||
`admins` session through the vhost, needs only a token to use the API. Unseal
|
||||
from the host's `bao` CLI; the UI's unseal form gets a 403. On a self-signed
|
||||
gateway where the UI is the only swarm name its host fronts, that host's nginx
|
||||
doesn't wait for the store: it serves the hive certificate on the UI's name (a
|
||||
browser warning) until the unsealed store issues the services leaf, so the
|
||||
stream passthrough readers use on that host comes up with the store sealed.
|
||||
|
||||
## The constraint that decides where the root lives
|
||||
|
||||
A hive CA carries `nameConstraints=permitted;DNS:<hive domain>`, and **a swarm
|
||||
|
|
|
|||
Loading…
Reference in a new issue