From f0e18fda453adc88a78997f4a9b086e63e6a0b13 Mon Sep 17 00:00:00 2001 From: Julian Prester Date: Sun, 7 Jun 2026 14:34:25 +1000 Subject: [PATCH] Harden scripts for non-interactive provisioning - config/scripts/bw-load-ssh.sh: add ssh-agent retry loop (graphical session may not be ready when systemd fires); use process substitution instead of pipe to avoid subshell + set -e issues with LOADED counter - stages/05-git.sh: remove interactive SSH key generation prompt (keys come from Bitwarden); pre-accept GitHub host key via ssh-keyscan to avoid first-connect prompt during git clone - stages/04-shell.sh: add sudo chsh fallback (chsh may fail in non-interactive provisioning without PAM auth) --- config/scripts/bw-load-ssh.sh | 24 +++++++++++++++----- stages/04-shell.sh | 10 +++++++-- stages/05-git.sh | 42 ++++++++++++----------------------- 3 files changed, 41 insertions(+), 35 deletions(-) mode change 100644 => 100755 config/scripts/bw-load-ssh.sh mode change 100644 => 100755 stages/04-shell.sh mode change 100644 => 100755 stages/05-git.sh diff --git a/config/scripts/bw-load-ssh.sh b/config/scripts/bw-load-ssh.sh old mode 100644 new mode 100755 index 5213c5f..5a145f0 --- a/config/scripts/bw-load-ssh.sh +++ b/config/scripts/bw-load-ssh.sh @@ -24,9 +24,22 @@ if [ ! -f "$SESSION_FILE" ]; then exit 1 fi -# Check ssh-agent is running -if ! ssh-add -l >/dev/null 2>&1; then - echo "ERROR: ssh-agent is not running." +# Check ssh-agent is running (retry in case graphical session hasn't fully started) +# ssh-add -l exits 2 if agent not running, 1 if no identities (which is fine) +_ssh_retries=5 +_ssh_waited=0 +while [ $_ssh_waited -lt $_ssh_retries ]; do + _ssh_exit=0 + ssh-add -l >/dev/null 2>&1 || _ssh_exit=$? + if [ $_ssh_exit -eq 2 ]; then + sleep 2 + _ssh_waited=$((_ssh_waited + 1)) + else + break + fi +done +if [ $_ssh_exit -eq 2 ]; then + echo "ERROR: ssh-agent is not running after ${_ssh_retries} attempts." echo "Start it with: eval \$(ssh-agent)" exit 1 fi @@ -53,7 +66,8 @@ fi LOADED=0 SKIPPED=0 -echo "$ITEMS" | while IFS= read -r item; do +# Use process substitution instead of pipe to avoid subshell and set -e issues +while IFS= read -r item; do NAME=$(echo "$item" | jq -r '.name') PUBLIC_KEY=$(echo "$item" | jq -r '.sshKey.publicKey // ""') PRIVATE_KEY=$(echo "$item" | jq -r '.sshKey.privateKey // ""') @@ -80,6 +94,6 @@ echo "$ITEMS" | while IFS= read -r item; do else echo " FAIL '$NAME'" fi -done +done <<< "$ITEMS" echo "Done. Loaded: $LOADED, Skipped (already loaded): $SKIPPED" diff --git a/stages/04-shell.sh b/stages/04-shell.sh old mode 100644 new mode 100755 index e90ea89..c51134e --- a/stages/04-shell.sh +++ b/stages/04-shell.sh @@ -92,8 +92,14 @@ fi # ---- Change default shell to zsh ---- info "Setting zsh as default shell..." if [ "$SHELL" != "$(which zsh)" ]; then - chsh -s "$(which zsh)" 2>/dev/null || warn "Could not change shell (chsh)." - ok "Default shell set to zsh. Log out and back in to activate." + # Try chsh directly first, fallback to sudo chsh (for non-interactive provisioning) + if chsh -s "$(which zsh)" 2>/dev/null; then + ok "Default shell set to zsh. Log out and back in to activate." + elif sudo chsh -s "$(which zsh)" "$USER" 2>/dev/null; then + ok "Default shell set to zsh via sudo. Log out and back in to activate." + else + warn "Could not change shell (chsh). Run manually: chsh -s \"$(which zsh)\"" + fi else ok "zsh is already the default shell." fi diff --git a/stages/05-git.sh b/stages/05-git.sh old mode 100644 new mode 100755 index c668f13..4933390 --- a/stages/05-git.sh +++ b/stages/05-git.sh @@ -67,35 +67,21 @@ EOF ok "Global .gitignore deployed." fi -# ---- Git SSH key ---- -# The Pop machine has a single SSH key: id_ed25519_github -# Loading is handled by bw-load-ssh.sh (stage 07) from Bitwarden. -# This stage can generate a new key if one doesn't exist. -info "Checking SSH keys..." -if [ ! -f "$HOME/.ssh/id_ed25519" ]; then - warn "No SSH key found." - warn "Options:" - warn " 1. Generate a new key (recommended for new machine):" - warn " ssh-keygen -t ed25519 -C 'hi@julianprester.com'" - warn " 2. Restore from backup/Bitwarden (see TODO.md)" - warn " 3. Skip for now (run bw-load-ssh.sh later to load from Bitwarden)" - echo "" - read -r -p "Generate a new SSH key now? [y/N] " response - if [[ "$response" =~ ^[Yy]$ ]]; then - ssh-keygen -t ed25519 -C "hi@julianprester.com" -f "$HOME/.ssh/id_ed25519" -N "" 2>/dev/null || { - warn "Key generation skipped or failed." - } - ok "SSH key generated: ~/.ssh/id_ed25519.pub" - cat "$HOME/.ssh/id_ed25519.pub" - warn "Add this key to GitHub: https://github.com/settings/keys" - fi +# ---- SSH keys ---- +# All SSH keys are stored in Bitwarden and loaded via bw-load-ssh.sh (stage 06). +# No local keys are generated. +info "SSH keys: loaded from Bitwarden via stage 06 (bw-load-ssh.sh)." + +# ---- Pre-accept GitHub host key ---- +# Avoids interactive prompt on first SSH connection to GitHub +if [ ! -f "$HOME/.ssh/known_hosts" ] || ! ssh-keygen -F github.com &>/dev/null; then + info "Adding github.com SSH host key to known_hosts..." + mkdir -p "$HOME/.ssh" + ssh-keyscan github.com >> "$HOME/.ssh/known_hosts" 2>/dev/null && \ + chmod 644 "$HOME/.ssh/known_hosts" && \ + ok "GitHub host key added." || warn "Could not fetch GitHub host key." else - ok "SSH key already exists." + ok "GitHub host key already known." fi -# Ensure proper SSH permissions -chmod 700 "$HOME/.ssh" 2>/dev/null || true -find "$HOME/.ssh" -type f -name "id_*" -exec chmod 600 {} \; 2>/dev/null || true -find "$HOME/.ssh" -type f -name "*.pub" -exec chmod 644 {} \; 2>/dev/null || true - ok "Stage 05 complete: Git configured."