weida

GitHub crates.io

weida-openbao

Rendered from docs/libraries/weida-openbao.md at 5a20f15

Contents
  1. 1. What it is for
  2. 2. The hand-off
  3. 3. What the role must allow
  4. 4. What it does not do
  5. 5. Tests

OpenBao (and Vault) for weida (decisions/0032): the client, the three ways a process authenticates, and the sources it drives. Its own crate, because the OpenBao API is JSON over HTTPS and nothing else in the workspace speaks either; reqwest (rustls, no default features, the TLS configuration built here on the same ring provider weida uses) and serde_json (Value only) are the two dependencies that justify the cut.

1. What it is for

A weida process that must keep the same key across restarts — which the redial of 0031 §4.7 depends on — and have its certificate issued and rotated by an authority rather than self-signed, and prove itself to that authority first. Three parts, in the order a process needs them:

PartTypeWhat it does
the clientOpenBao, Config, Authauthenticates by token, AppRole or the wrapped hand-off; keeps the token renewed; post_wrapped and revoke_accessor for the controller side
identity signed by a rolePkiSign → weida::IdentitySourcea CSR over the key a files source holds, pki/sign/<role>, renewal under the same key at a fraction of the TTL
trust anchored on the CAPkiAnchor → weida::TrustSourcepki/cert/ca_chain (or cert/ca), refreshed at an interval
material from KVKv → weida::IdentitySourcechain and key as two fields of a KV v2 secret; distribution, not rotation

pki/issue is deliberately absent: it would generate a new key per issuance, and under pinning a new key every TTL is a new peer every TTL.

2. The hand-off

The flow of 0032 §2: a controller mints a child token of the service's role, wrapped — auth/token/create/<role> with X-Vault-Wrap-TTL — and the service redeems the wrapping token as its very first request. Auth::Handoff reads it from a file or a systemd credential, calls sys/wrapping/unwrap before anything else, and turns the one failure that matters — 400 wrapping token is not valid or does not exist — into Error::HandoffStolen, on which the service exits. The controller keeps the child's wrapped_accessor from the minting answer and revokes by it at every exit, which also kills whatever a thief redeemed.

examples/handoff/ is the whole thing as a systemd user unit plus a service binary, proved on the project workstation against bao server -dev: a clean start redeems, signs an identity with the role and answers a weida request pinned by that identity's fingerprint; a start whose wrapping token was redeemed first by hand exits with status 3 and ExecStopPost= leaves the thief with a revoked token. One finding from that proof, recorded in the unit: the credentials directory is mounted read-only for ExecStartPre= as for the service, so the wrapping token travels through RuntimeDirectory= (0700, the unit's uid, gone with the unit) and the service unlinks it once redeemed.

3. What the role must allow

From 0032 §2, checked where the crate can and stated here where it cannot:

Role settingValueWhy
key_type, key_bitsec, 256weida's generated key is ECDSA P-256; a mismatch fails the sign with the role's own error
allowed_domains, allow_bare_domains, allow_ip_sanswhat peers dialthe anchor path checks names
server_flag, client_flagas the peer's rolerustls' verifiers require the EKU
use_csr_sanstruethe names come from the CSR
require_cnfalsea SAN is the name; the crate still sends the first name as CN for a role that insists
max_ttl≥ PkiSign::ttla request above it is refused

4. What it does not do

  • Revocation. No CRL, no OCSP, on any path. The TTL is the answer and PkiSign is what makes a short one affordable.
  • A Python surface. Nothing in weida-py reaches this crate yet.
  • The controller in Rust. Minting is a bao invocation in a unit file; post_wrapped exists for a controller that is itself a weida process.

5. Tests

tests/scripted.rs runs the client, the three auths, the renewal pace, and the three sources against a scripted HTTP server in the crate — six tests in under three seconds, in the gate. tests/bao_dev.rs runs the hand-off and the PKI path against the local bao binary in dev mode, #[ignore]d because the gate has no OpenBao:

cargo test -p weida-openbao --test bao_dev -- --ignored