Adds --mode break-glass-drill with its own signed admission (drill: "break-glass", attempt id RPF-WP-0064-T10-*, snapshot_label). After the phase A restore and unseal it checks the held root token with lookup-self only, runs a fresh generate-root ceremony with re-entered shares, does one read-only sys/auth action, revokes the generated token (204 then 403) and drills a cancelled attempt. Phase A is unchanged. The verifier types the receipt extension and rejects token, accessor, OTP, nonce, encoded-token and share shapes. Offline 34 and native 16 pass. Founder runbooks for the drill and the read-only production SSO-down walk are in the T10 section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 4515@bnt-lap001 Assistant-Session: 0bf96127-6e04-41be-bfda-d11717c3f354
40 lines
2.7 KiB
Bash
40 lines
2.7 KiB
Bash
#!/usr/bin/env bash
|
|
# Attended launcher: isolated original-OpenBao restore, phase A or T10 break-glass drill (RPF-WP-0064).
|
|
# Founder workstation only. Never railiance01, never a kubeconfig, no network.
|
|
set +x
|
|
set -euo pipefail
|
|
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
cd -- "$ROOT"
|
|
umask 077
|
|
unset KUBECONFIG BAO_TOKEN VAULT_TOKEN BAO_ADDR VAULT_ADDR
|
|
usage='Usage: run-isolated-restore.sh [--break-glass-drill] ADMISSION ADMISSION_SIG ADMISSION_SHA256 CIPHERTEXT RECEIPT_DIR'
|
|
MODE=phase-a
|
|
if [[ "${1:-}" == --break-glass-drill ]]; then MODE=break-glass-drill; shift; fi
|
|
[[ $# == 5 ]] || { printf '%s\n' "$usage"; exit 1; }
|
|
[[ "$3" =~ ^sha256:[a-f0-9]{64}$ ]] || { printf '%s\n' 'ERROR: exact sha256:<hex> admission digest required.'; exit 1; }
|
|
for index in 1 2 4; do
|
|
[[ -f "${!index}" && ! -L "${!index}" ]] || { printf '%s\n' 'ERROR: provide regular owner-controlled input files.'; exit 1; }
|
|
done
|
|
[[ -d "$5" && ! -L "$5" && "$5" == /* ]] || { printf '%s\n' 'ERROR: RECEIPT_DIR must be an existing absolute directory.'; exit 1; }
|
|
[[ -t 0 && -t 1 && -t 2 ]] || { printf '%s\n' 'ERROR: run in your own interactive terminal.'; exit 1; }
|
|
[[ "$(hostname -s)" != railiance01* ]] || { printf '%s\n' 'ERROR: never on railiance01.'; exit 1; }
|
|
for tool in python3 age age-keygen docker ssh-keygen git; do
|
|
command -v "$tool" >/dev/null || { printf 'ERROR: required tool unavailable: %s\n' "$tool"; exit 1; }
|
|
done
|
|
RECEIPT="$5/rpf64-${MODE}-$(date -u +%Y%m%dT%H%M%SZ).json"
|
|
python3 scripts/openbao-isolated-restore/preflight.py --mode "$MODE" --admission "$1" --admission-sig "$2" \
|
|
--admission-sha256 "$3" --ciphertext "$4" --receipt "$RECEIPT"
|
|
if [[ "$MODE" == break-glass-drill ]]; then
|
|
printf '%s\n' 'Break-glass drill (RPF-WP-0064-T10) on an isolated, networkless restore. Never production.'
|
|
printf '%s\n' 'Prompts: backup age identity, two unseal shares, the held initial root token (lookup-self only),'
|
|
printf '%s\n' 'then two shares AGAIN for a fresh generate-root ceremony. Production root-token validity,'
|
|
printf '%s\n' 'production generate-root availability and the SSO-down access path are not claimed.'
|
|
else
|
|
printf '%s\n' 'Phase A only: networkless restore and metadata readback. No consumer continuity,'
|
|
printf '%s\n' 'live JWT login, phase B network or owner acceptance is claimed.'
|
|
printf '%s\n' 'You will be asked for the backup age identity, then two original unseal shares.'
|
|
fi
|
|
printf '%s\n' 'Press Ctrl-C at any time to abort; the container is removed and an aborted receipt written.'
|
|
python3 scripts/openbao-isolated-restore/restore_adapter.py --mode "$MODE" --admission "$1" --admission-sig "$2" \
|
|
--admission-sha256 "$3" --ciphertext "$4" --receipt "$RECEIPT"
|
|
printf 'Safe receipt: %s\n' "$RECEIPT"
|