diff --git a/docs/workflow.md b/docs/workflow.md index c499a63..8a92203 100644 --- a/docs/workflow.md +++ b/docs/workflow.md @@ -8,8 +8,10 @@ This documents the *current* workflow and the *target* workflow once profiles + - Nginx + Pi‑Kit dashboard - DietPi dashboard 3) Update the system if needed. -4) Run the prep scrub + verify: +4) Run the prep scrub + verify (prep now prompts to shut down): - `sudo ./pikit-prep.sh` + - (optional) `sudo ./pikit-prep.sh --no-shutdown` + - (optional) `sudo ./pikit-prep.sh --shutdown-now` - `./pikit-smoke-test.sh` - (optional) `sudo ./pikit-prep.sh --check-only` 5) Image the SD card with DietPi Imager. @@ -25,8 +27,10 @@ This documents the *current* workflow and the *target* workflow once profiles + 4) Add dashboard services using the UI (Add Service modal). 5) Open any needed ports in ufw (done as part of testing/config): - `sudo ufw allow from to any port ` -6) Run the prep scrub + verify: +6) Run the prep scrub + verify (prep now prompts to shut down): - `sudo ./pikit-prep.sh` + - (optional) `sudo ./pikit-prep.sh --no-shutdown` + - (optional) `sudo ./pikit-prep.sh --shutdown-now` - `./pikit-smoke-test.sh` - (optional) `sudo ./pikit-prep.sh --check-only` 7) Image the SD card via the QEMU DietPi VM: @@ -54,8 +58,10 @@ This documents the *current* workflow and the *target* workflow once profiles + - Merges services into `/etc/pikit/services.json` (idempotent). 5) Run the drift check (planned script): - Confirms services + ports match the profile + base. -6) Run the prep scrub + verify: +6) Run the prep scrub + verify (prep now prompts to shut down): - `sudo ./pikit-prep.sh` + - (optional) `sudo ./pikit-prep.sh --no-shutdown` + - (optional) `sudo ./pikit-prep.sh --shutdown-now` - `./pikit-smoke-test.sh` - (optional) `sudo ./pikit-prep.sh --check-only` 7) Image the SD card with DietPi Imager. @@ -84,8 +90,10 @@ Use the helper: - dashboard loads - first‑boot completes 4) Apply any required profile/services. -5) Run prep + verify: +5) Run prep + verify (prep now prompts to shut down): - `sudo ./pikit-prep.sh` + - (optional) `sudo ./pikit-prep.sh --no-shutdown` + - (optional) `sudo ./pikit-prep.sh --shutdown-now` - `./pikit-smoke-test.sh` 6) Power down cleanly. 7) Image the SD card (DietPi Imager via QEMU or on‑device). diff --git a/pikit-prep.sh b/pikit-prep.sh index 7af88cc..d0837a6 100755 --- a/pikit-prep.sh +++ b/pikit-prep.sh @@ -14,9 +14,12 @@ PIKIT_SSH_OPTS="${PIKIT_SSH_OPTS:-}" PIKIT_REMOTE_TMP="${PIKIT_REMOTE_TMP:-/tmp/pikit-prep.sh}" PIKIT_SELF_DELETE="${PIKIT_SELF_DELETE:-0}" PIKIT_FORCE_PASSWORD_CHANGE="${PIKIT_FORCE_PASSWORD_CHANGE:-1}" +PIKIT_SHUTDOWN_AFTER_PREP="${PIKIT_SHUTDOWN_AFTER_PREP:-1}" +PIKIT_SHUTDOWN_PROMPT="${PIKIT_SHUTDOWN_PROMPT:-1}" MODE="both" LOCAL_ONLY=0 +DID_PREP=0 ERRORS=0 WARNINGS=0 @@ -32,10 +35,14 @@ Options: --prep-only Run prep only (no check) --check-only Run checks only (no prep) --local Force local execution (no SSH copy) + --shutdown-now Shutdown after prep completes without prompting + --no-shutdown Skip shutdown prompt after prep --help Show this help Env: PIKIT_FORCE_PASSWORD_CHANGE=0 Skip forcing a password change (default is on) + PIKIT_SHUTDOWN_AFTER_PREP=0 Skip shutdown prompt after prep (default on) + PIKIT_SHUTDOWN_PROMPT=0 Skip shutdown prompt (default on) USAGE } @@ -70,6 +77,8 @@ parse_args() { --prep-only) MODE="prep" ;; --check-only) MODE="check" ;; --local) LOCAL_ONLY=1 ;; + --shutdown-now) PIKIT_SHUTDOWN_AFTER_PREP=1; PIKIT_SHUTDOWN_PROMPT=0 ;; + --no-shutdown) PIKIT_SHUTDOWN_AFTER_PREP=0 ;; --help|-h) usage; exit 0 ;; *) echo "[FAIL] Unknown argument: $arg" >&2 @@ -86,12 +95,16 @@ run_remote() { [ "$arg" = "--local" ] && continue forward+=("$arg") done + local ssh_tty=() + if [ "$PIKIT_SHUTDOWN_AFTER_PREP" -eq 1 ] && [ "$PIKIT_SHUTDOWN_PROMPT" -eq 1 ] && [ -t 0 ]; then + ssh_tty=(-t) + fi if ! command -v scp >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then echo "[FAIL] ssh/scp not available for remote prep" >&2 exit 1 fi scp -i "$PIKIT_SSH_KEY" $PIKIT_SSH_OPTS "$SCRIPT_PATH" "${PIKIT_USER}@${PIKIT_HOST}:${PIKIT_REMOTE_TMP}" - ssh -i "$PIKIT_SSH_KEY" $PIKIT_SSH_OPTS "${PIKIT_USER}@${PIKIT_HOST}" \ + ssh "${ssh_tty[@]}" -i "$PIKIT_SSH_KEY" $PIKIT_SSH_OPTS "${PIKIT_USER}@${PIKIT_HOST}" \ "sudo PIKIT_SELF_DELETE=1 bash ${PIKIT_REMOTE_TMP} --local ${forward[*]}; rc=\$?; rm -f ${PIKIT_REMOTE_TMP}; exit \$rc" exit $? } @@ -607,6 +620,33 @@ finalize() { echo "[OK] Prep/check completed." } +maybe_shutdown() { + if [ "$PIKIT_SHUTDOWN_AFTER_PREP" -ne 1 ] || [ "$DID_PREP" -ne 1 ]; then + return + fi + local do_shutdown=1 + if [ "$PIKIT_SHUTDOWN_PROMPT" -eq 1 ]; then + if [ -t 0 ]; then + local reply="" + printf '\nShutdown now? [y/N] ' + read -r reply || reply="" + case "${reply,,}" in + y|yes) do_shutdown=1 ;; + *) do_shutdown=0 ;; + esac + else + status WARN "no TTY; skipping shutdown (use --shutdown-now to force)" + do_shutdown=0 + fi + fi + if [ "$do_shutdown" -eq 1 ]; then + status OK "Shutting down" + shutdown -f now || status FAIL "shutdown" + else + status OK "Shutdown skipped" + fi +} + maybe_self_delete() { if [ "$PIKIT_SELF_DELETE" -eq 1 ] && [[ "$SCRIPT_PATH" == /tmp/* ]]; then rm -f "$SCRIPT_PATH" || true @@ -623,15 +663,17 @@ main() { require_root case "$MODE" in - prep) prep_image ;; + prep) prep_image; DID_PREP=1 ;; check) check_image ;; both) prep_image + DID_PREP=1 check_image ;; esac finalize + maybe_shutdown maybe_self_delete } diff --git a/pikit-web/data/version.json b/pikit-web/data/version.json index 0f0a0d9..949ca86 100644 --- a/pikit-web/data/version.json +++ b/pikit-web/data/version.json @@ -1,3 +1,3 @@ { - "version": "0.1.3" + "version": "0.1.4" } diff --git a/systemd/pikit-first-login.sh b/systemd/pikit-first-login.sh index 5c22d2b..c87f3e9 100644 --- a/systemd/pikit-first-login.sh +++ b/systemd/pikit-first-login.sh @@ -3,17 +3,24 @@ # Prints a one-time SSH hardening tip after the forced password change. FLAG="/var/lib/pikit/first-login.notice" +DONE_FILE=".pikit-first-login.done" case "$-" in *i*) interactive=1 ;; *) interactive=0 ;; esac -if [ "$interactive" -eq 1 ] && [ -f "$FLAG" ]; then - echo "" - echo "Pi-Kit: For better security, set up an SSH key and disable password auth once working." - echo " Example: ssh-keygen -t ed25519" - echo " ssh-copy-id dietpi@pikit.local" - echo "" - rm -f "$FLAG" 2>/dev/null || true +USER_NAME="$(id -un 2>/dev/null || echo "")" +DONE_PATH="${HOME:-}/$DONE_FILE" + +if [ "$interactive" -eq 1 ] && [ -f "$FLAG" ] && [ "$USER_NAME" = "dietpi" ]; then + if [ -n "${HOME:-}" ] && [ -d "${HOME:-}" ] && [ ! -f "$DONE_PATH" ]; then + echo "" + echo "Pi-Kit: For better security, set up an SSH key and disable password auth once working." + echo " Run these from your computer (not the Pi):" + echo " ssh-keygen -t ed25519" + echo " ssh-copy-id dietpi@pikit.local" + echo "" + :> "$DONE_PATH" 2>/dev/null || true + fi fi