secrets-engine/scripts/demo-e2e.sh

103 lines
3.7 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# End-to-end MVP demo for the whynot-design npm publish lane.
#
# Boots a throwaway OpenBao dev server, then drives the full secrets-engine chain:
# plan (dry-run) -> apply -> provision (from mode-0600 file) -> verify +/-
# -> exec (npm-config injection into a child) -> route -> revoke.
#
# Nothing here is production. The dev server is in-memory and discarded on exit.
# The "token" is a throwaway local string written to a mode-0600 temp file.
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO"
# shellcheck disable=SC1091
source .venv/bin/activate
BAO_BIN="$(command -v bao)"
WORK="$(mktemp -d)"
export BAO_ADDR="http://127.0.0.1:8270"
export BAO_TOKEN="se-demo-root"
export SECRETS_ENGINE_HUB_URL="" # offline; uses local decision fixture
export SECRETS_ENGINE_UNSAFE_DEMO="1" # required for live prod-lane demos
# Keep secret material OUTSIDE the repo worktree (engine enforces this).
TOKENFILE="$WORK/whynot.token"
NEGATIVE_TOKENFILE="$WORK/unrelated.token"
cleanup() {
set +e
[[ -n "${BAO_PID:-}" ]] && kill "$BAO_PID" 2>/dev/null
rm -rf "$WORK"
}
trap cleanup EXIT
echo "### 0. boot throwaway OpenBao dev server (in-memory)"
"$BAO_BIN" server -dev -dev-root-token-id="$BAO_TOKEN" \
-dev-listen-address="127.0.0.1:8270" >"$WORK/bao.log" 2>&1 &
BAO_PID=$!
for _ in $(seq 1 30); do
"$BAO_BIN" status -address="$BAO_ADDR" >/dev/null 2>&1 && break
sleep 0.2
done
echo
echo "### 1. plan (dry-run, no mutation)"
secrets-engine plan whynot-design-npm-publish --stage prod | sed 's/^/ /'
echo
echo "### 2. apply approved metadata (policy + approle) to OpenBao"
secrets-engine apply whynot-design-npm-publish --stage prod | sed 's/^/ /'
echo
echo "### 2b. apply is idempotent (re-run shows 'unchanged')"
secrets-engine apply whynot-design-npm-publish --stage prod | sed 's/^/ /'
echo
echo "### 3. provision the token from a mode-0600 file outside the repo"
printf 'npm_demoTOKENvalue1234567890abcd' > "$TOKENFILE"
chmod 600 "$TOKENFILE"
secrets-engine provision whynot-design-npm-publish --stage prod \
--field npm_token --from-file "$TOKENFILE" | sed 's/^/ /'
echo
echo "### 4. verify positive (approved consumer can read) + negative (others denied)"
"$BAO_BIN" policy write se-demo-unrelated - <<'EOF' >/dev/null
path "secret/data/prod/coulomb/whynot-design/npm" {
capabilities = ["deny"]
}
EOF
umask 077
"$BAO_BIN" token create -field=token -policy=se-demo-unrelated > "$NEGATIVE_TOKENFILE"
secrets-engine verify whynot-design-npm-publish --field npm_token \
--positive --negative --negative-token-file "$NEGATIVE_TOKENFILE" | sed 's/^/ /'
echo
echo "### 5. exec-time delivery: child sees the token via a temp npmrc; parent never does"
cat > "$WORK/fake-npm" <<'EOF'
#!/usr/bin/env bash
# Stand-in for 'npm publish' — proves the child can resolve the token and the
# parent shell cannot. Prints only whether the token is reachable, never the value.
if [[ -n "${NPM_CONFIG_USERCONFIG:-}" ]] && grep -q '_authToken' "$NPM_CONFIG_USERCONFIG"; then
echo " [child] npm userconfig present; _authToken resolvable: yes"
else
echo " [child] NO token available"; exit 1
fi
echo " [child] would run: npm $*"
EOF
chmod +x "$WORK/fake-npm"
secrets-engine exec --catalog whynot-design-npm-publish -- "$WORK/fake-npm" publish
echo " [parent] SE_NPM_TOKEN in parent shell: '${SE_NPM_TOKEN:-<unset>}'"
echo
echo "### 6. ops-warden routing pointer (ready=true expected)"
secrets-engine route whynot-design-npm-publish --json | sed 's/^/ /'
echo
echo "### 7. revoke (deactivate the lane)"
secrets-engine revoke whynot-design-npm-publish | sed 's/^/ /'
echo
echo "### DONE — full chain exercised against a live OpenBao."