#!/usr/bin/env bash # Bootstrap root SSH when `su` needs a password (no sudo on host). # Usage: BOOTSTRAP_SU_PASSWORD='...' ./scripts/bootstrap-root-ssh-su-password.sh HOST set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" HOST="${1:-}" BOOTSTRAP_USER="${BOOTSTRAP_USER:-ladmin}" PUBKEY_FILE="${SSH_PUBLIC_KEY:-${HOME}/.ssh/id_ed25519.pub}" SU_PASSWORD="${BOOTSTRAP_SU_PASSWORD:-}" [[ -n "${HOST}" ]] || { echo "Usage: $0 HOST" >&2; exit 1; } [[ -n "${SU_PASSWORD}" ]] || { echo "Set BOOTSTRAP_SU_PASSWORD" >&2; exit 1; } [[ -f "${PUBKEY_FILE}" ]] || { echo "Missing ${PUBKEY_FILE}" >&2; exit 1; } IP="$(awk -v h="${HOST}" '$1==h {for(i=2;i<=NF;i++) if($i~/^ansible_host=/) {sub(/ansible_host=/,"",$i); print $i; exit}}' \ "${REPO_ROOT}/inventories/production/hosts")" [[ -n "${IP}" ]] || { echo "No ansible_host for ${HOST}" >&2; exit 1; } PUBKEY="$(cat "${PUBKEY_FILE}")" export IP BOOTSTRAP_USER SU_PASSWORD PUBKEY /usr/bin/expect <<'EXPECT' set timeout 60 spawn ssh -o StrictHostKeyChecking=accept-new $env(BOOTSTRAP_USER)@$env(IP) expect { -re {[$#] $} { } timeout { exit 1 } } send "su -\r" expect { "Password:" { send "$env(SU_PASSWORD)\r" } timeout { exit 1 } } expect { -re {root@caddy|#|❯|[$#] $} { } timeout { exit 1 } } send "bash --noprofile --norc\r" expect { -re {# $} { } timeout { exit 1 } } send "mkdir -p /root/.ssh && chmod 700 /root/.ssh && touch /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys\r" expect -re {# $} send "grep -qF '$env(PUBKEY)' /root/.ssh/authorized_keys || echo '$env(PUBKEY)' >> /root/.ssh/authorized_keys\r" expect -re {# $} send "sed -i 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config 2>/dev/null || echo PermitRootLogin prohibit-password >> /etc/ssh/sshd_config\r" expect -re {# $} send "systemctl restart ssh 2>/dev/null || systemctl restart sshd 2>/dev/null || true\r" expect -re {# $} send "exit\r" expect eof EXPECT ssh -o BatchMode=yes -i "${PUBKEY_FILE}" -o ConnectTimeout=10 \ "root@${IP}" "echo OK: root@${IP}" echo "Done: root key on ${HOST}"