OpenBao¶
Single-replica Raft, sealed with a static key from a SealedSecret and reached at
https://openbao.cbc.gewis.nl:8443.
It self-initialises. Auto-unseal cannot unseal a barrier that was never
initialised, and a StatefulSet will not start pod 1 until pod 0 is Ready, so an
uninitialised OpenBao deadlocks at pod 0. The initialize stanza breaks that on
first boot, and it only runs against empty storage — a partially-initialised
PVC has to be deleted for it to re-run.
Self-init requests take flat values only; a nested map is rejected as invalid
request, and a failed request is fatal to the process. The root token is created
and immediately revoked, so the stanza must also provision a way in — here the
Kubernetes auth method bound to the openbao-admin ServiceAccount, which avoids
storing an admin password anywhere:
The .envrc of every root with a vault provider exports that token as
TF_VAR_bao_jwt. It passes --request-timeout=2s, so entering the directory
without a route to kube.gewis.nl:6443 costs two seconds and leaves the variable
unset instead of stalling on the API server's dial timeout.
disable_mlock is not a valid OpenBao 2.x option; it was removed and is only
warned about, not rejected.
People log in through authentik¶
or OIDC on the UI's login screen. The method is split over two roots:
40_openbao-config mounts the bare auth/oidc, and 50_authentik-config
(openbao.tf) creates the openbao client in authentik,
writes auth/oidc/config with its issuer and secret, and owns the roles. The mount has
no dependencies; its configuration needs the client, so it lives where the client is.
The single role, authentik, only accepts members of
CBC - Application Hosting Team (ADM), through bound_claims on the groups claim.
That claim is built from memberOfFlattened, so nested membership counts — see
the groups claim.
authentik enforces the same group first: an expression policy bound to the openbao
application checks that attribute, so anyone else is refused at authentik and does
not see the app on their dashboard. A plain group binding would not do, because
authentik's own groups only hold direct members. Both checks read
openbao_login_group in locals.tf. Besides default, the role grants only
ssh-sign-admin, which signs SSH certificates and
reads nothing. Management stays with OpenTofu through the Kubernetes path above.
OpenBao never returns oidc_client_secret when the config is read, so the
vault_generic_endpoint sets ignore_absent_fields — without it every plan would show
the secret as changed. It also sets disable_delete, because the config endpoint
cannot be deleted; destroying the method means removing the mount in
40_openbao-config.