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>
This commit is contained in:
2026-09-22 09:31:47 -05:00
co-authored by Claude Opus 5
parent 2d3d322b0e
commit 0eb7f26663
3 changed files with 311 additions and 217 deletions
+31 -17
View File
@@ -120,7 +120,7 @@ Docker. Copy `tls.crt` to that host first and pass it as `BAO_CACERT`:
```bash
BAO_ADDR=https://$OPENBAO_ADDR:8200 BAO_CACERT=/path/to/openbao-ca.crt \
BAO_TOKEN=<root> bash bootstrap.sh
bash bootstrap.sh # export BAO_TOKEN beforehand rather than passing it inline
```
It enables the `kanrisha-tape` KV-v2 mount (with **effectively-unlimited**
@@ -178,7 +178,7 @@ deploy step.
```bash
cd /srv/openbao
bash update.sh check # declared / running / latest + seal state
BAO_TOKEN=<token> bash update.sh update # snapshot, then upgrade
bash update.sh update # snapshot, then upgrade (prompts)
```
Unlike the copyparty and ergo updaters in this repo, this one **will not update
@@ -191,8 +191,11 @@ configured — only auto-unseal makes the vault come back on its own.
Before it changes anything, `update` requires that the container is running, the
vault is **unsealed** (a sealed vault cannot produce a snapshot, so there would
be no rollback plan), and a `BAO_TOKEN` with `sys/storage/raft/snapshot` — the
root token works. It writes the snapshot to `/var/backups/openbao` at `0600`,
be no rollback plan), and a token with `sys/storage/raft/snapshot` — the root
token works. It **prompts** for that token with echo off; never pass it as
`BAO_TOKEN=... bash update.sh`, which puts it in your shell history and in
`/proc/<pid>/cmdline`, where every user on the box can read it. For unattended
use set `BAO_TOKEN_FILE` to a `0600` file. It writes the snapshot to `/var/backups/openbao` at `0600`,
verifies it is a valid non-empty gzip archive, and refuses to continue if it is
not. **Copy it off the host**: it is the rollback plan, and OpenBao's upgrade
guide is explicit that reverting the image alone does not roll back the data
@@ -211,19 +214,26 @@ the external `plugin "kms" "pkcs11"` first.
The vault is the sole recovery path for encrypted tapes — back it up:
Snapshot save/restore are **token-gated** (`sys/storage/raft/snapshot` is
sudo-capable) — pass a token that has that path (the root token works, or mint a
dedicated snapshot-policy token). The container has no ambient token, so supply
it via `-e BAO_TOKEN`.
sudo-capable) — they need a token that has that path (the root token works, or
mint a dedicated snapshot-policy token). The container has no ambient token.
**Do not put the token on a command line.** `docker ... -e BAO_TOKEN=<value>`
places it in the docker process's argv, and `/proc/<pid>/cmdline` is readable by
every user on the host — so it leaks further than your shell history does. Use
`update.sh snapshot`, which prompts with echo off and passes the token to the
container over stdin:
```bash
# Consistent raft snapshot (safe while running); write it to a scratch path, NOT
# into the live raft dir:
docker compose exec -T -e BAO_TOKEN=<token> openbao \
bao operator raft snapshot save -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap
# then stream the RAW file off-box, age-encrypted. Use `exec -T ... cat`, not
# `compose cp openbao:… -` (which emits a TAR wrapper that won't restore):
docker compose exec -T openbao cat /tmp/openbao.snap | \
age -r "$(cat /path/to/globals/age-pubkey.txt)" > "openbao-$(date +%F).snap.age"
cd /srv/openbao
bash update.sh snapshot # prompts; writes to /var/backups/openbao, 0600
```
Then age-encrypt it and move it off-box:
```bash
age -r "$(cat /path/to/globals/age-pubkey.txt)" \
< /var/backups/openbao/openbao-pre-manual-*.snap \
> "openbao-$(date +%F).snap.age"
```
Restore (into a fresh, unsealed vault): decrypt, copy the raw `.snap` in, and
@@ -232,8 +242,12 @@ apply it (also token-gated):
```bash
age -d -i ~/.age/key.txt openbao-DATE.snap.age > openbao.snap
docker compose cp openbao.snap openbao:/tmp/openbao.snap
docker compose exec -T -e BAO_TOKEN=<token> openbao \
bao operator raft snapshot restore -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap
# Same argv problem as above, so hand the token over stdin rather than -e:
read -rs -p "token: " T; echo
printf '%s\n' "$T" | docker compose exec -T openbao sh -c \
'read -r t; BAO_TOKEN="$t" exec bao operator raft snapshot restore \
-address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap'
unset T
```
Snapshots do **not** contain the unseal keys or root token — you still need
+203 -188
View File
@@ -496,23 +496,23 @@ TLS: ${STACK_DIR}/tls/tls.crt (give this to the Kanrisha daemon as
this vault host only ships Docker), pointed at this vault. Copy tls.crt there
first as the BAO_CACERT:
BAO_ADDR=https://${OPENBAO_ADDR}:8200 BAO_CACERT=/path/to/openbao-ca.crt \\
BAO_TOKEN=<root> bash bootstrap.sh
bash bootstrap.sh # export BAO_TOKEN first; do not pass it inline
Then point the Kanrisha daemon at:
[encryption.openbao]
address = "https://${OPENBAO_ADDR}:8200"
ca_cert = "/etc/kanrisha/openbao-ca.crt" # = tls.crt above
3. Back it up (this vault is the sole recovery path for encrypted tapes). Snapshot
save is token-gated -- pass a token with sys/storage/raft/snapshot (the root
token works):
docker compose exec -T -e BAO_TOKEN=<token> openbao \\
bao operator raft snapshot save -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap
docker compose exec -T openbao cat /tmp/openbao.snap > ${STACK_DIR}/openbao.snap
3. Back it up (this vault is the sole recovery path for its secrets). Snapshot
save is token-gated; update.sh prompts for the token with echo off and hands
it to the container over stdin, so it never reaches a command line:
cd ${STACK_DIR} && bash update.sh snapshot
...then age-encrypt + copy it off-box. See the README for the full DR flow.
Do NOT use `-e BAO_TOKEN=<value>`: that puts the token in the docker
process's argv, and /proc/<pid>/cmdline is world-readable.
Manage (run these from ${STACK_DIR}):
bash update.sh check # declared/running/latest + seal state
BAO_TOKEN=<token> bash update.sh update # snapshot, then upgrade (comes back SEALED)
bash update.sh update # snapshot, then upgrade (comes back SEALED)
docker compose logs -f
docker compose pull && docker compose up -d # update
docker compose down # stop, keep the vault data
@@ -530,183 +530,198 @@ fi
# __ARCHIVE_BELOW__ is the embedded tar.gz payload (base64), added by build.sh.
exit 0
__ARCHIVE_BELOW__
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
AA==
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
DUb74sHHHA5zt+RKc3DVviqs6soWp7A2yIyCTxabrvIvf0zzmlMPZEtH9c1xniTBsrhowRGmqY8e
7FUWJ2+Gx4NVPllc5bQi+PDZ9CZKyE6r6MtoCdWcar9946CmQezLE1UIi0ed98/eDs4nyGvj4cma
XMh9ENwS6RK4LXNc7UhIxxUmdHj24+TsYrTfeUQQgLSkPfI1HUQWTpJQz99YsjEPzfbR8HRS8L5G
7bA/ejs4O7kY98occmvOMVZiOxN6+25CRVGBeKtdczR9oAVtVleViXxLV1Fe6eZVVfwZy8A+0mu1
xf5yeMJGwYGpZ7JukEumF1DAphDHDTg+OBOwCmSq5dphobkf5KTzG33YhpQLSSVpzLeUjQOSCfCJ
IqxUdzByOqUzk6fxHR8ntWlW+9vE91613dAjAzTXg0wi4jjCBJM8UzWbImAcLYu/IRCF6SLiLvRO
Sjxd3XyPC1oq8yjgWvqmM7krdgtWwkTfIOBHUBfDbroEgSDDd2D47wP4sgECKPWOmNwbPmSqspBC
0kpisqRVJ6FC0BHkAOwL9d4j01nTYD1xrn02yJK0IKh0kd5j8+vZ7cF8vVtVNn6Z2GNU6intN1BZ
Cw98XaptU9Dki1OPLvKYn/Pfknxi+GOdzPHg+M1+8+mm6wn86/kp/d0JXVl+3R+/m4xPLs4OBped
D/fNFrcYk1uv1X66SZfTPj2w0Vi59Gyi9sHJ6E1v/1P54L5ZdDZTzH1k6kozk2Q5WMsf1d8mEb2e
Lpppxxye1rrxeK6vCKOWg9MN34z3dRTG8hN2jp9/Zm4icMMP9PpecUFd0Nvy1f5/i/+6vOypxHFl
78OHLzdouvU3kgKPaLdrb9v3RIukWZwWlzQn1/vFlM8/3//yvvlSTG7KRxtf7vMTPeh6nWidM833
NS8EVh4sVAz5+Nnk+qu7+2Icx/MZtXL17Gefq2f0640mzneavv2EfrgZzIovU3hvJYeDQ369ksV7
Jo0XL0s/6O0/eMSuUT2v58Heo4mwGLnqIxUFk9p6+52SZj1J1Z6bnNNbTzrUTh2MvtdO23xa7rxN
Z9DNBglm+Hby7uB45WV1FRnTg3i+2RLiYyH8Zz91dnYuuy93tsPLrz7wl04oPlc/RaSKL5vUGb91
0giTHs7ZCS8/e2SO7qzfNzxfPr5WN7y8++S8lY68viRVXvpRbkzo3+QOfWcBUYTRWv1vQrbMRUjK
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
bgCgAAA=
+77 -12
View File
@@ -23,6 +23,7 @@
# Subcommands:
# check (default) declared vs running vs latest, seal state, and whether
# an unattended update would be safe here. Changes nothing.
# snapshot take a raft snapshot and leave it in SNAPSHOT_DIR. Nothing else.
# update do it now: preflight -> snapshot -> down -> pin -> pull -> up
# run what the schedule invokes; obeys UPDATE_POLICY
# install schedule the daily `run`
@@ -35,17 +36,29 @@
#
# Env (also read from /etc/openbao-update.conf; environment wins):
# STACK_DIR=/srv/openbao UPDATE_POLICY=notify
# BAO_TOKEN= required by `update`: the snapshot is token-gated
# BAO_TOKEN= required by `update`/`snapshot`. Prefer NOT to set
# this inline -- see "Supplying the token" below.
# BAO_TOKEN_FILE= read the token from a file (0600) instead
# SNAPSHOT_DIR=/var/backups/openbao
# TARGET_VERSION= pin a specific version instead of latest
# DRY_RUN=0 print what would happen, change nothing
# SKIP_SNAPSHOT=0 DANGEROUS: upgrade with no rollback plan
# GH_REPO=openbao/openbao
#
# Supplying the token:
# `BAO_TOKEN=<root> bash update.sh update` works, but DO NOT do it: the value
# lands in your shell history, and anything on the argv of a child process is
# readable by every user on the box via /proc/<pid>/cmdline. This script never
# puts the token on any command line -- it hands it to the container over
# stdin -- and it will prompt for it with echo off if you do not supply one.
# For unattended use, point BAO_TOKEN_FILE at a 0600 file.
#
# Usage:
# bash update.sh check
# BAO_TOKEN=<root> bash update.sh update
# BAO_TOKEN=<root> TARGET_VERSION=2.6.2 bash update.sh update
# bash update.sh check # no token needed
# bash update.sh snapshot # prompts for the token
# bash update.sh update # prompts for the token
# BAO_TOKEN_FILE=/root/.bao-root bash update.sh update
# TARGET_VERSION=2.6.2 bash update.sh update
set -euo pipefail
@@ -65,6 +78,7 @@ fi
: "${STACK_DIR:=/srv/openbao}"
: "${UPDATE_POLICY:=notify}"
: "${BAO_TOKEN:=}"
: "${BAO_TOKEN_FILE:=}"
: "${SNAPSHOT_DIR:=/var/backups/openbao}"
: "${TARGET_VERSION:=}"
: "${DRY_RUN:=0}"
@@ -105,6 +119,44 @@ env_get() { # <KEY> -> value from .env ('' if absent)
sed -n "s/^$1=//p" "$ENV_FILE" | tail -n1
}
# ---------------------------------------------------------------------------
# Token handling
#
# The token never appears on a command line. `docker exec -e BAO_TOKEN=<value>`
# would put it in the docker process's argv, and /proc/<pid>/cmdline is
# world-readable -- so it would leak to every user on the host, not just to the
# shell history. Instead the value is written to the container's stdin and the
# container's own shell reads it into the environment it then execs `bao` with.
# ---------------------------------------------------------------------------
acquire_token() {
[[ -n "$BAO_TOKEN" ]] && return 0
if [[ -n "$BAO_TOKEN_FILE" ]]; then
[[ -r "$BAO_TOKEN_FILE" ]] || die "BAO_TOKEN_FILE=${BAO_TOKEN_FILE} is not readable."
BAO_TOKEN="$(head -n1 "$BAO_TOKEN_FILE")"
[[ -n "$BAO_TOKEN" ]] || die "BAO_TOKEN_FILE=${BAO_TOKEN_FILE} is empty."
return 0
fi
if [[ -t 0 ]]; then
read -rs -p "OpenBao token (input hidden): " BAO_TOKEN
printf '\n' >&2
[[ -n "$BAO_TOKEN" ]] || die "No token entered."
return 0
fi
die "No token available and stdin is not a terminal. Set BAO_TOKEN_FILE=/path/to/a/0600/file, or run interactively to be prompted. Avoid BAO_TOKEN=... on the command line: it goes into your shell history and into /proc/<pid>/cmdline."
}
# Run `bao <args...>` inside the container with BAO_TOKEN set, passing the token
# over stdin so it never reaches an argv anywhere on the host.
bao_with_token() {
printf '%s\n' "$BAO_TOKEN" | dc exec -T openbao sh -c '
read -r _t
BAO_TOKEN="$_t"
export BAO_TOKEN
exec "$@"
' sh bao "$@"
}
# ---------------------------------------------------------------------------
# State
# ---------------------------------------------------------------------------
@@ -178,7 +230,7 @@ take_snapshot() { # <target-version> -> echoes the snapshot path
return 0
fi
[[ -n "$BAO_TOKEN" ]] || die "BAO_TOKEN is required: the raft snapshot is token-gated (sys/storage/raft/snapshot). The root token works."
acquire_token
if [[ "$DRY_RUN" == "1" ]]; then
echo "DRY: snapshot -> ${dest}" >&2
@@ -190,8 +242,8 @@ take_snapshot() { # <target-version> -> echoes the snapshot path
log "Taking a raft snapshot to ${dest}..." >&2
# Write inside the container, then stream the RAW file out. `compose cp` is
# NOT usable here: it emits a TAR wrapper that will not restore.
dc exec -T -e BAO_TOKEN="$BAO_TOKEN" openbao \
bao operator raft snapshot save -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/pre-upgrade.snap \
bao_with_token operator raft snapshot save \
-address=https://127.0.0.1:8200 -tls-skip-verify /tmp/pre-upgrade.snap \
>/dev/null || die "Snapshot failed -- not upgrading. Check the token has sys/storage/raft/snapshot."
dc exec -T openbao cat /tmp/pre-upgrade.snap > "$dest" || die "Could not stream the snapshot out -- not upgrading."
dc exec -T openbao rm -f /tmp/pre-upgrade.snap >/dev/null 2>&1 || true
@@ -250,7 +302,7 @@ do_check() {
log "Update available: ${declared:-?} -> ${latest}"
if [[ "$auto" == no ]]; then
warn "A restart will leave the vault SEALED until someone enters three unseal keys."
warn "Run it when you can do that: BAO_TOKEN=<token> bash ${SELF} update"
warn "Run it when you can do that: bash ${SELF} update (it will prompt for a token)"
fi
}
@@ -332,6 +384,16 @@ data store. Restore the pre-upgrade snapshot -- see the README's DR runbook.
EOF
}
do_snapshot() {
[[ -d "$STACK_DIR" ]] || die "No stack at $STACK_DIR (set STACK_DIR)."
container_running || die "The openbao container is not running."
local state; state="$(seal_state)"
[[ "$state" == 0 ]] || die "The vault is SEALED (or unreadable): a snapshot can only be taken from an unsealed vault."
local dest; dest="$(take_snapshot manual)"
[[ -n "$dest" ]] || die "No snapshot was taken."
log "Snapshot written to ${dest} -- copy it OFF this host."
}
do_run() {
# What the schedule invokes. The whole point of this branch is that it is
# conservative: an unattended update of a manually-unsealed vault takes it
@@ -371,9 +433,11 @@ STACK_DIR=${STACK_DIR}
UPDATE_POLICY=${UPDATE_POLICY}
SNAPSHOT_DIR=${SNAPSHOT_DIR}
GH_REPO=${GH_REPO}
# BAO_TOKEN is intentionally absent. \`update\` needs one for the snapshot; pass
# it in the environment for that run. UPDATE_POLICY=auto therefore only works
# with auto-unseal AND a token supplied some other way.
# BAO_TOKEN is intentionally absent: a long-lived root token in a file next to
# the vault it opens defeats the vault. \`update\`/\`snapshot\` prompt for one.
# For unattended use set BAO_TOKEN_FILE below to a 0600 file holding a token
# with sys/storage/raft/snapshot -- not the root token.
#BAO_TOKEN_FILE=/root/.bao-snapshot-token
EOF
chmod 0600 "$OPENBAO_UPDATE_CONF"
}
@@ -439,9 +503,10 @@ do_uninstall() {
case "${1:-check}" in
check) do_check ;;
snapshot) do_snapshot ;;
update) do_update ;;
run) do_run ;;
install) do_install ;;
uninstall) do_uninstall ;;
*) die "Usage: $(basename "$0") {check|update|run|install|uninstall}" ;;
*) die "Usage: $(basename "$0") {check|snapshot|update|run|install|uninstall}" ;;
esac