The founder's first real run stopped at ResolverError on all checks: real tokens belong to an LDAP user and the archived resolver names the in-cluster directory. Restored LLDAP now joins the database's network-free namespace, archived resolver addresses are repointed to loopback in the disposable copy only (any other address is refused), and the checks call POST /validate/check by user and realm as KeyCape does. Fifteen native fixture tests pass (HOTP and TOTP+PIN, negatives, bundle). Also covers the user-to-token resolver and the encrypted bind password. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 328925@bnt-lap001 Assistant-Session: 14b795fd-c632-4c01-8d28-e7e490c7b0ed
42 lines
2.4 KiB
Bash
Executable file
42 lines
2.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Owner-run existing-token MFA check. No key, PIN or code belongs in arguments.
|
|
set +x
|
|
set -euo pipefail
|
|
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
cd -- "$ROOT"
|
|
umask 077
|
|
[[ $# == 1 ]] || { printf '%s\n' 'Usage: run-isolated-privacyidea-mfa.sh VERIFIED_TRANSFER_RECEIPT'; exit 1; }
|
|
[[ -f "$1" && ! -L "$1" ]] || { printf '%s\n' 'ERROR: provide the existing verified transfer receipt.'; exit 1; }
|
|
[[ -t 0 && -t 1 && -t 2 ]] || { printf '%s\n' 'ERROR: run in your own interactive terminal.'; exit 1; }
|
|
for tool in python3 age age-keygen openssl docker; do
|
|
command -v "$tool" >/dev/null || { printf 'ERROR: required tool unavailable: %s\n' "$tool"; exit 1; }
|
|
done
|
|
python3 - <<'PY'
|
|
import importlib.util
|
|
from pathlib import Path
|
|
p=Path('tools/bootstrap-recovery/isolated-privacyidea-mfa.py')
|
|
spec=importlib.util.spec_from_file_location('isolated',p)
|
|
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
|
|
try:
|
|
m.preflight()
|
|
except Exception:
|
|
raise SystemExit('ERROR: exact local Docker target, pinned images or tmpfs preflight failed; no key requested.')
|
|
print('OK: exact bnt-lap001 recovery engine, pinned images and tmpfs available.')
|
|
PY
|
|
NK49_PI_RUN="$(mktemp -d /tmp/nk49-isolated-privacyidea-mfa-XXXXXXXX)"
|
|
printf 'Safe evidence directory: %s\n' "$NK49_PI_RUN"
|
|
printf '%s\n' 'This restores the privacyIDEA database, its original keys and the LLDAP directory into disposable network-free containers.'
|
|
printf '%s\n' 'Prompts, in order: the backup age key (hidden), your login name, then a token PIN+current code as typed at login (hidden).'
|
|
printf '%s\n' 'Have your authenticator open first. Checks: wrong key refused, wrong code refused, your code accepted, by user and realm.'
|
|
printf '%s\n' 'The restored copy is disposable; production is untouched.'
|
|
if bash sso-mfa/bootstrap/sops-custody-unlock.sh \
|
|
--expected-recipient age1zvryunvjhvpkmasskauga2heeg0ztnte9ymgppvjge36ekumk50syr3tsz \
|
|
--prompt -- python3 tools/bootstrap-recovery/isolated-privacyidea-mfa.py \
|
|
--transfer-receipt "$1" --receipt "$NK49_PI_RUN/recovery.json"; then
|
|
printf 'Safe receipt: %s/recovery.json\n' "$NK49_PI_RUN"
|
|
else
|
|
NK49_PI_EXIT=$?
|
|
printf 'Check stopped or needs review. Safe receipt: %s/recovery.json\n' "$NK49_PI_RUN"
|
|
exit "$NK49_PI_EXIT"
|
|
fi
|
|
printf '%s\n' 'Return only the outcome and evidence directory. The KeyCape consumer run remains separate.'
|