4 Commits
Author SHA1 Message Date
57_WolveandClaude Opus 5 f8c839a163 feat(openbao): pin 2.6.2, and correct the mlock and recovery-path claims
2.5.5 was two releases behind. Verified against the openbao/openbao source at
tag v2.6.2 that the bump is safe for an existing vault: raft's on-disk format
is unchanged (identical bbolt / hashicorp-raft / raft-boltdb pins,
byte-identical physical/raft/types.proto), snapshots restore in both
directions, and every stanza this config.hcl uses still parses -- the config
parser only moved import paths and the listener diff is additive.

Three corrections that came out of that check.

deploy.sh's printed crash-loop recovery command was already broken by 2.6.0.
That release adds `USER openbao` to the alpine image, so the container no
longer starts as root; the `docker run ... --entrypoint sh ... chown` it tells
the operator to run now executes as uid 100 and fails. Added `--user 0:0`, and
dropped the stale 2.5.5 literal from the same string. The detection logic
itself is unaffected -- `id -u openbao` still reads /etc/passwd and still
returns 100, verified by extracting the passwd layer from both published
images -- so a02524a holds and the volume does not need re-chowning.

The mlock story was wrong, and was already wrong on 2.5.5. OpenBao removed
mlock support: its own config parser carries "OpenBao has dropped support for
mlock. Please remove the line disable_mlock = false from your config and
disable or encrypt swap instead." So `cap_add: [IPC_LOCK]` and
`ulimits: memlock: -1` are inert, and config.hcl's "mlock keeps key material
off swap -- REQUIRED" was describing something that does not happen. The
BEHAVIOUR was right all along, because deploy.sh disables swap, which is the
actual mitigation; only the explanation was wrong. Corrected in config.hcl,
docker-compose.yml and the README. The two compose settings stay: they are
harmless, and removing them would recreate every deployed container for no
gain.

The built-in `seal "pkcs11"` stanza is deprecated in 2.6.0 for removal in
v2.7.0, and the HSM distribution is discontinued by then; PKCS#11 auto-unseal
continues only via the external `plugin "kms" "pkcs11" {}`. Noted where the
commented-out stanza lives, since a stack relying on it has to move before
taking 2.7.x.

Also worth recording and NOT acting on: do not switch to the openbao-distroless
image variant. It ships no shell, which breaks every `docker compose run
--entrypoint sh` probe deploy.sh uses.

Upgrading a LIVE vault is not automatic -- snapshot first, and the restart
comes back sealed. That is the next commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 09:10:20 -05:00
57_WolveandClaude Opus 5 dc9761a668 feat(openbao): serve the web UI, so init/unseal can happen in a browser
config.hcl shipped `ui = false`, so there was no UI to reach even with working
connectivity. That forces init and unseal through `docker compose exec`, which
puts the unseal keys and root token into a server shell's history and
scrollback -- the one place they should never be.

Make it a substitution point driven by OPENBAO_UI, default on. This adds no
exposure: the UI is served on the same listener, and anything that can reach
:8200 can already do everything through the API. What it changes is where the
unseal material is displayed -- the operator's browser instead of the host.

Rendering config.hcl rather than copying it needed three things to be right:

- Precedence. OPENBAO_UI passed to a run wins; otherwise the value .env already
  deploys applies; otherwise the default. Without that, `OPENBAO_UI=0 bash
  deploy.sh` against an existing node would report success and change nothing.
  Env-presence is captured before the ":=" default, so an explicit 0 is
  distinguishable from "not mentioned" and a re-run cannot silently re-enable
  the UI. A value passed this run is written back to .env.
- Reload. A bind-mounted file's CONTENTS are not part of the compose config
  hash, so `up -d` leaves a changed config.hcl unloaded -- the same trap fixed
  in copyparty (76d2a09) and simplex (0f22735). Restart only on an actual
  change, and say loudly that a restart RE-SEALS the vault, because that is not
  a free action on a running one.
- `cmp -s` stays inside an `if` condition; as `cmp -s A B && CONFIG_CHANGED=1`
  it would trip set -e whenever the files matched.

Verified: default renders `ui = true` and seeds .env; an identical re-run
reports no change; .env's 0 is honoured when nothing is passed; a passed 1 beats
.env's 0 and is written through; a non-boolean value dies.

Note for LAN-isolated hosts: the listener publishes on OPENBAO_BIND only, so a
browser on another subnet still cannot reach it. The generated cert already
carries DNS:localhost + IP:127.0.0.1 in its SANs, so
`ssh -L 8200:<bind-addr>:8200` and https://localhost:8200 works against the
existing cert without widening the publish.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 15:01:56 -05:00
57_WolveandClaude Opus 4.8 0812f345a8 fix(openbao): sanity-audit fixes — 4 blockers + hardening
A multi-agent sanity audit of the freshly-merged deployment found four
end-to-end blockers (and several smaller issues); all fixed here.

HIGH (were blocking):
- deploy.sh never called install_docker(), so `docker compose pull` hit
  command-not-found on any host without Docker. Now called before the
  compose steps.
- The container's server process runs as the image's own (often non-root)
  user but the mounted config/TLS were root-owned 0640/0600 and the raft
  volume root-owned -> vault crash-looped, never binding :8200. deploy.sh
  now detects the image UID after pull and aligns ownership of config.hcl,
  ./tls and the data volume (a no-op when the image runs as root);
  config.hcl is installed 0644 (holds no secrets).
- Docs told operators to set the daemon key `openbao_ca_cert`, but the
  Kanrisha daemon's key is `ca_cert` (config.go, mapstructure:"ca_cert").
  The wrong key is fatal on strict unmarshal / leaves TLS unverified.
  Renamed in all 5 places (config.hcl, gen-tls.sh, deploy.sh x2, README).
- DR backup used `docker compose cp openbao:… -`, which emits a TAR stream,
  so the age-encrypted snapshot was tar-wrapped and would not restore.
  Switched to `docker compose exec -T openbao cat` for the raw bytes, wrote
  the snapshot to a scratch path (not the live raft dir), and documented the
  matching restore.

MEDIUM:
- Swap detection used `swapon --show` (absent on BusyBox) and `\s` (GNU-only)
  -> silently no-op on Alpine, leaving swap on. Now uses /proc/swaps and
  [[:space:]] so mlock hardening actually holds on musl.
- A Docker-published port bypasses the host INPUT firewall, so the source
  rule was illusory. deploy.sh now narrows OPENBAO_BIND to OPENBAO_ADDR when
  it is an IP, the compose/README/.env comments state the reality, and a new
  Exposure section + an init-immediately warning were added.
- Fixed broken ../kanrisha/ and deployments/kanrisha/ links (separate repo).

LOW:
- OPENBAO_TLS_SANS is now honored (folded into the SAN list from the env).
- .gitignore excludes *.snap / *.snap.age.
- Bootstrap note clarifies bootstrap.sh needs the `bao` CLI (run it from the
  Kanrisha host/workstation, not this Docker-only vault host).
- README multi-OS count corrected (eight stacks) + automations.sh header
  lists openbao.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:11:37 -05:00
57_WolveandClaude Opus 4.8 2efc9dbffb feat(openbao): hardened same-LAN tape-encryption key store for Kanrisha
A dedicated OpenBao deployment, kept OFF the Kanrisha tape host so a compromise
of the tape node can't reach the vault.

- Native TLS on the listener (self-signed by default via gen-tls.sh, or a
  CA-signed cert from a Smallstep CA over ACME) — no Caddy/Let's Encrypt;
  reached over the LAN, not the public internet.
- Integrated raft storage (clean snapshot-based DR).
- mlock on (cap_add IPC_LOCK + memlock unlimited + host swapoff in deploy.sh).
- Manual unseal by default; optional PKCS#11 HSM auto-unseal.
- deploy.sh: Docker install (Alpine/Debian/Alma), self-signed cert, .env seed,
  swapoff, firewall 8200/tcp, compose up; then prints init/unseal + the KV-v2 +
  AppRole bootstrap for Kanrisha + the raft-snapshot DR flow. Self-contained
  (config payload embedded by build.sh).
- Registered in automations.sh + the README deployment table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 14:10:22 -05:00