2 Commits
Author SHA1 Message Date
57_WolveandClaude Opus 5 0eb7f26663 fix(openbao): never put the vault token on a command line
`BAO_TOKEN=<root> bash update.sh update` -- which the previous commit and the
pre-existing DR runbook both recommended -- leaks the token twice over. It goes
into the shell history, and `docker compose exec -e BAO_TOKEN=<value>` puts it
in the docker process's argv, where /proc/<pid>/cmdline makes it readable by
every user on the host. The second is the worse one and is not fixed by
anything the operator does at their prompt.

The token now reaches the container over STDIN and is never an argument to
anything: the container's own shell reads one line, exports it, and execs bao.
Verified locally that the child process sees the exact value -- including &, |
and a backslash -- while its argv contains zero occurrences of it.

How a token is supplied, in order: an already-exported BAO_TOKEN; a 0600 file
named by the new BAO_TOKEN_FILE; otherwise an echo-off prompt. With no token
and no terminal it dies telling the operator to use BAO_TOKEN_FILE rather than
an inline assignment, and says why.

Added a `snapshot` subcommand so the DR runbook no longer needs an inline
pipeline at all. That runbook and the deploy banner both carried the -e form
before this deployment had an updater, so both are corrected: taking a backup
is now `bash update.sh snapshot`, and restore keeps the stdin shape rather than
-e. The Kanrisha bootstrap lines say to export the token first instead of
passing it inline.

The conf file still refuses to hold a token, but now points at BAO_TOKEN_FILE
as the unattended answer -- and suggests a snapshot-policy token rather than
the root token for it.

Not verified without a live host: that `docker compose exec -T` forwards stdin
as expected. If it does not, the snapshot fails loudly with a permission error
and the upgrade aborts before touching anything, which is the safe direction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 09:31:47 -05:00
57_WolveandClaude Opus 5 2d3d322b0e feat(openbao): add update.sh, an updater that refuses to strand a sealed vault
Follows the copyparty/ergo updater idiom -- check/update/run/install/uninstall,
a conf file the environment overrides, the version pinned into .env so the
running release is explicit, DRY_RUN -- but inverts its central assumption.

copyparty and ergo come back by themselves after a recreate. OpenBao comes back
SEALED: with the default Shamir seal a restart needs three unseal keys typed in
by a human. A scheduled `latest` update would therefore take the vault offline
at 03:00 and leave it there. So the scheduled path defaults to UPDATE_POLICY=
notify and never changes the running version; `install` schedules a daily CHECK
and says so. UPDATE_POLICY=auto opts in, and is STILL refused unless an
uncommented `seal` stanza is present in config.hcl -- only auto-unseal makes an
unattended update defensible.

`update` preflights before touching anything, because every one of these fails
worse halfway through than up front:
- the container must be running;
- the vault must be UNSEALED, since a sealed vault cannot produce a snapshot and
  there would be no rollback plan;
- BAO_TOKEN must be present, because the snapshot is token-gated on
  sys/storage/raft/snapshot;
- the target must not cross into 2.7.x while a built-in seal "pkcs11" stanza is
  active. That stanza is REMOVED in 2.7.0, not deprecated, so the vault would
  start with no way to unseal at all.

The snapshot is the rollback plan, not a formality: OpenBao's upgrade guide
states that reverting the image alone does not roll back the data store. It is
streamed out with `exec -T ... cat` rather than `compose cp`, which emits a TAR
wrapper that will not restore; written 0600 to /var/backups/openbao; and checked
for being a non-empty valid gzip archive, with a failure treated as fatal.
SKIP_SNAPSHOT=1 exists and warns exactly what it costs.

A failed pull or start rolls the OPENBAO_TAG pin back and restarts the previous
version. BAO_TOKEN is deliberately never written to the conf file: a long-lived
root token sitting next to the vault it opens defeats the vault.

Verified: the 2.7-with-active-pkcs11 refusal and its three negative cases
(2.6.2 active, 3.x active, 2.7 commented); auto-unseal detection distinguishing
a commented stanza from a live one; set_env/env_get; the release-tag parse with
and without a leading v; and all four `run` policy branches, including that
auto refuses without auto-unseal and that an unknown policy dies.

Not verified without a live host: the snapshot, pull and recreate themselves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 09:21:11 -05:00