diff --git a/docs/en/e2e-ci.md b/docs/en/e2e-ci.md index 3a5e1dbc..8789cc7e 100644 --- a/docs/en/e2e-ci.md +++ b/docs/en/e2e-ci.md @@ -159,17 +159,24 @@ bootroot infra install --compose-file "$COMPOSE_FILE" # DB credentials are read from .env created by infra install. # POSTGRES_HOST and POSTGRES_PORT are set by the script so that # build_admin_dsn_from_env() connects via the host-mapped port. -# The piped sequence must answer every prompt the run reaches: init -# fails on EOF rather than answering an unanswered prompt itself. -printf "y\ny\ny\n" | BOOTROOT_LANG=en bootroot init \ +# Every prompt is answered by its own flag and stdin is closed, so the +# run depends on no piped answer sequence and on no leftover file: +# an overwrite flag whose file is absent is a silent no-op. init fails +# on EOF rather than answering an unanswered prompt itself. +BOOTROOT_LANG=en bootroot init \ --compose-file "$COMPOSE_FILE" \ --secrets-dir "$SECRETS_DIR" \ --summary-json "$INIT_SUMMARY_JSON" \ --enable auto-generate,show-secrets,db-provision \ --no-eab \ + --save-unseal-keys \ + --overwrite-password \ + --overwrite-ca-json \ + --overwrite-state \ + --confirm-db-provision \ --db-user "step" \ --db-name "stepca" \ - --responder-url "$RESPONDER_URL" + --responder-url "$RESPONDER_URL" "$INIT_RAW_LOG" 2>&1; then + --responder-url "$RESPONDER_URL" "$INIT_RAW_LOG" 2>&1; then { echo "bootroot init failed (raw tail):" tail -n 160 "$INIT_RAW_LOG" || true diff --git a/scripts/impl/run-local-lifecycle.sh b/scripts/impl/run-local-lifecycle.sh index ab9674d6..26d9bbb3 100755 --- a/scripts/impl/run-local-lifecycle.sh +++ b/scripts/impl/run-local-lifecycle.sh @@ -328,7 +328,11 @@ run_bootstrap_chain() { log_phase "init" rm -f "$WORKSPACE_DIR/state.json" - if ! BOOTROOT_LANG=en printf "y\ny\ny\n" | run_bootroot init \ + # Answer every prompt with its own flag and run with stdin closed, so + # the run neither depends on which of password.txt, ca.json and + # state.json the cleanup above happened to leave behind nor needs a + # TTY. An overwrite flag whose file is absent is a silent no-op. + if ! BOOTROOT_LANG=en run_bootroot init \ --compose-file "$COMPOSE_FILE" \ --secrets-dir "$SECRETS_DIR" \ --summary-json "$INIT_SUMMARY_JSON" \ @@ -337,9 +341,14 @@ run_bootstrap_chain() { --stepca-password "password" \ --http-hmac "dev-hmac" \ --no-eab \ + --save-unseal-keys \ + --overwrite-password \ + --overwrite-ca-json \ + --overwrite-state \ + --confirm-db-provision \ --db-user "step" \ --db-name "stepca" \ - --responder-url "$RESPONDER_URL" >"$INIT_RAW_LOG" 2>&1; then + --responder-url "$RESPONDER_URL" "$INIT_RAW_LOG" 2>&1; then { echo "bootroot init failed (raw tail):" tail -n 160 "$INIT_RAW_LOG" || true diff --git a/scripts/impl/run-reinit-recovery.sh b/scripts/impl/run-reinit-recovery.sh index 3ed54a27..6bcb9b97 100755 --- a/scripts/impl/run-reinit-recovery.sh +++ b/scripts/impl/run-reinit-recovery.sh @@ -438,12 +438,12 @@ run_bootstrap_init() { log_phase "bootstrap-init" # `infra install` writes state.json (to capture the openbao_bind_addr - # intent) before init runs, so init's overwrite-state prompt fires - # under default-yes-no semantics. Pipe `y` answers to clear that - # prompt (and any future ca.json / password.txt overwrite prompts - # that may appear on rerun) so the bootstrap pass stays - # non-interactive. - if ! printf 'y\ny\ny\n' | BOOTROOT_LANG=en run_bootroot init \ + # intent) before init runs, so init's overwrite-state prompt fires; + # ca.json and password.txt prompt too on a rerun, and `db-provision` + # adds its own confirmation. Answer all four with their per-prompt + # flags and run with stdin closed, so the bootstrap pass stays + # non-interactive whatever the workspace was left holding. + if ! BOOTROOT_LANG=en run_bootroot init \ --compose-file "$COMPOSE_FILE" \ --secrets-dir "$SECRETS_DIR" \ --summary-json "$INIT_SUMMARY_JSON" \ @@ -452,10 +452,14 @@ run_bootstrap_init() { --http-hmac "dev-hmac" \ --no-eab \ --save-unseal-keys \ + --overwrite-password \ + --overwrite-ca-json \ + --overwrite-state \ + --confirm-db-provision \ --db-user "step" \ --db-name "stepca" \ --responder-url "http://localhost:8080" \ - --skip responder-check >"$INIT_RAW_LOG" 2>&1; then + --skip responder-check "$INIT_RAW_LOG" 2>&1; then { echo "bootroot init failed (raw tail):" tail -n 200 "$INIT_RAW_LOG" || true diff --git a/scripts/impl/run-remote-lifecycle.sh b/scripts/impl/run-remote-lifecycle.sh index e90847e3..5fad74fb 100755 --- a/scripts/impl/run-remote-lifecycle.sh +++ b/scripts/impl/run-remote-lifecycle.sh @@ -281,7 +281,11 @@ run_bootstrap_chain() { log_phase "init" rm -f "$CONTROL_DIR/state.json" - if ! BOOTROOT_LANG=en printf "y\ny\ny\n" | run_bootroot_control init \ + # Answer every prompt with its own flag and run with stdin closed, so + # the run neither depends on which of password.txt, ca.json and + # state.json the cleanup above happened to leave behind nor needs a + # TTY. An overwrite flag whose file is absent is a silent no-op. + if ! BOOTROOT_LANG=en run_bootroot_control init \ --compose-file "$COMPOSE_FILE" \ --secrets-dir "$SECRETS_DIR" \ --summary-json "$INIT_SUMMARY_JSON" \ @@ -289,10 +293,15 @@ run_bootstrap_chain() { --stepca-provisioner "acme" \ --stepca-password "password" \ --no-eab \ + --save-unseal-keys \ + --overwrite-password \ + --overwrite-ca-json \ + --overwrite-state \ + --confirm-db-provision \ --http-hmac "dev-hmac" \ --db-user "step" \ --db-name "stepca" \ - --responder-url "$RESPONDER_URL" >"$INIT_RAW_LOG" 2>&1; then + --responder-url "$RESPONDER_URL" "$INIT_RAW_LOG" 2>&1; then { echo "bootroot init failed (raw tail):" tail -n 160 "$INIT_RAW_LOG" || true diff --git a/scripts/preflight/ci/test-core.sh b/scripts/preflight/ci/test-core.sh index f3365f1b..660d645f 100755 --- a/scripts/preflight/ci/test-core.sh +++ b/scripts/preflight/ci/test-core.sh @@ -25,16 +25,24 @@ cargo test --test monitoring_integration echo "[test-core] installing infrastructure" cargo run --bin bootroot -- infra install -# --- Zero-config Init (answer n, no show-secrets) --- -# Two answers, one per prompt this run reaches: decline EAB registration, -# then decline saving the unseal keys. The second `n` is what makes the -# assertion below exercise the declined path deliberately — `init` fails -# on EOF rather than reading an unanswered prompt as "no". -echo "[test-core] zero-config init (answer n, no show-secrets)" -printf "n\nn\n" | BOOTROOT_LANG=en cargo run --bin bootroot -- init \ +# --- Zero-config Init (decline saving the unseal keys, no show-secrets) --- +# Every prompt but one is answered by its own flag, so the run cannot +# depend on which of password.txt, ca.json and state.json a previous run +# left behind. The single piped `n` answers the save-unseal-keys +# prompt, and it stays an answer on purpose: only the declined branch of +# that prompt echoes the keys in cleartext, which is what the assertion +# below reads. `--no-save-unseal-keys` suppresses that echo, so it must +# not replace this answer. `init` fails on EOF rather than reading an +# unanswered prompt as "no". +echo "[test-core] zero-config init (decline saving the unseal keys, no show-secrets)" +printf "n\n" | BOOTROOT_LANG=en cargo run --bin bootroot -- init \ --enable auto-generate \ --http-hmac "dev-hmac" \ --secrets-dir "$BOOTROOT_SECRETS_DIR" \ + --no-eab \ + --overwrite-password \ + --overwrite-ca-json \ + --overwrite-state \ --responder-url "http://localhost:8080" \ --skip responder-check 2>&1 | tee zero-config-init.log @@ -50,14 +58,21 @@ cargo run --bin bootroot -- clean -y cargo run --bin bootroot -- infra install # --- CLI Init --- -# `clean -y` above removed password.txt, ca.json and state.json, so no -# overwrite confirmation fires here: the two prompts this run reaches -# are EAB registration and saving the unseal keys, both declined. +# The overwrite prompts are answered by their own flags rather than by +# relying on `clean -y` above having removed password.txt, ca.json and +# state.json, and `--no-eab` answers the EAB prompt. The piped `n` +# answers the save-unseal-keys prompt: declining it needs no flag here +# because `--no-save-unseal-keys` requires `--summary-json`, which this +# smoke run does not write. echo "[test-core] CLI init (smoke)" -printf "n\nn\n" | BOOTROOT_LANG=en cargo run --bin bootroot -- init \ +printf "n\n" | BOOTROOT_LANG=en cargo run --bin bootroot -- init \ --enable auto-generate,show-secrets \ --http-hmac "dev-hmac" \ --secrets-dir "$BOOTROOT_SECRETS_DIR" \ + --no-eab \ + --overwrite-password \ + --overwrite-ca-json \ + --overwrite-state \ --responder-url "http://localhost:8080" \ --skip responder-check | tee cli-init.log diff --git a/scripts/preflight/extra/cli-scenarios.sh b/scripts/preflight/extra/cli-scenarios.sh index f0a1f201..fdcb5c64 100755 --- a/scripts/preflight/extra/cli-scenarios.sh +++ b/scripts/preflight/extra/cli-scenarios.sh @@ -85,19 +85,27 @@ run_init_scenario() { wait_for_postgres_admin log "Running bootroot init" - # One answer per prompt this run reaches. `secrets/` and `state.json` - # were removed above, so no overwrite confirmation fires: `y` confirms - # the `db-provision` feature, then EAB registration and saving the - # unseal keys are both declined. An answer short of the last prompt - # aborts the run — init fails on EOF rather than answering itself. - printf "y\nn\nn\n" | BOOTROOT_LANG=en cargo run --bin bootroot -- init \ + # Answer every prompt with its own flag and run with stdin closed, + # rather than relying on the cleanup above having removed `secrets/` + # and `state.json` so that a piped answer sequence lines up: an + # overwrite flag whose file is absent is a silent no-op. + # `--no-save-unseal-keys` declines persistence and suppresses the + # cleartext echo, which nothing here asserts on; the keys stay + # available in the 0600 summary JSON this run already writes. + BOOTROOT_LANG=en cargo run --bin bootroot -- init \ --enable auto-generate,show-secrets,db-provision \ --summary-json "$INIT_SUMMARY_JSON" \ --http-hmac "$responder_hmac" \ + --no-eab \ + --no-save-unseal-keys \ + --overwrite-password \ + --overwrite-ca-json \ + --overwrite-state \ + --confirm-db-provision \ --db-user "step" \ --db-name "stepca" \ --responder-url "http://localhost:8080" \ - --skip responder-check | tee "$ROOT_DIR/tmp/cli-init.log" + --skip responder-check