Files
automations/deployments/openbao/build.sh
T
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

35 lines
939 B
Bash

#!/usr/bin/env bash
#
# build.sh -- (re)embed docker-compose.yml, config.hcl, gen-tls.sh and
# .env.example into deploy.sh as a base64-encoded tar.gz payload after
# __ARCHIVE_BELOW__. Idempotent: strips any existing payload first.
set -euo pipefail
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
SCRIPT="$DIR/deploy.sh"
MARKER="__ARCHIVE_BELOW__"
[[ -f "$SCRIPT" ]] || { echo "deploy.sh not found at $SCRIPT" >&2; exit 1; }
for f in docker-compose.yml config.hcl gen-tls.sh update.sh .env.example; do
[[ -f "$DIR/$f" ]] || { echo "Missing $DIR/$f" >&2; exit 1; }
done
PAYLOAD=$(tar -czf - -C "$DIR" docker-compose.yml config.hcl gen-tls.sh update.sh .env.example | base64)
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
sed "/^${MARKER}\$/,\$d" "$SCRIPT" > "$TMP"
{
echo "$MARKER"
echo "$PAYLOAD"
} >> "$TMP"
mv "$TMP" "$SCRIPT"
chmod +x "$SCRIPT"
trap - EXIT
size=$(wc -c < "$SCRIPT")
echo "Built $SCRIPT (${size} bytes)"