ansible/scripts/bootstrap-root-ssh.sh
ilia de49b34cdc
Some checks failed
CI / skip-ci-check (pull_request) Successful in 6s
CI / lint-and-test (pull_request) Failing after 9s
CI / ansible-validation (pull_request) Failing after 6s
CI / secret-scanning (pull_request) Successful in 5s
CI / dependency-scan (pull_request) Successful in 8s
CI / sast-scan (pull_request) Failing after 5s
CI / license-check (pull_request) Successful in 11s
CI / vault-check (pull_request) Failing after 6s
CI / playbook-test (pull_request) Failing after 6s
CI / container-scan (pull_request) Failing after 6s
CI / sonar-analysis (pull_request) Failing after 2s
CI / workflow-summary (pull_request) Successful in 4s
Add homelab monitoring, portfolio site, and vault tooling.
Document pve10 static IPs, monitoring stack, and site LXCs; add portfolio
to inventory; Mailcow mailbox automation; vault import/export scripts;
security audit guides and UniFi DHCP reference.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 16:25:07 -04:00

104 lines
3.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Bootstrap root SSH key access via a normal user (default: ladmin).
# Usage: ./scripts/bootstrap-root-ssh.sh HOSTNAME
# BOOTSTRAP_USER=ladmin TARGET_USER=root SSH_PUBLIC_KEY=~/.ssh/id_ed25519.pub
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
INVENTORY_HOSTS="${INVENTORY_HOSTS:-${REPO_ROOT}/inventories/production/hosts}"
PUBKEY_FILE="${SSH_PUBLIC_KEY:-${HOME}/.ssh/id_ed25519.pub}"
BOOTSTRAP_USER="${BOOTSTRAP_USER:-ladmin}"
TARGET_USER="${TARGET_USER:-root}"
HOST="${1:-}"
if [[ -z "${HOST}" ]]; then
echo "Usage: $0 HOST" >&2
exit 1
fi
if [[ ! -f "${PUBKEY_FILE}" ]]; then
echo "Public key not found: ${PUBKEY_FILE}" >&2
exit 1
fi
resolve_from_inventory() {
awk -v host="${HOST}" '
$1 == host {
for (i = 2; i <= NF; i++) {
if ($i ~ /^ansible_host=/) {
sub(/ansible_host=/, "", $i)
ip = $i
}
if ($i ~ /^ansible_user=/) {
sub(/ansible_user=/, "", $i)
user = $i
}
}
}
END {
print ip
print user
}
' "${INVENTORY_HOSTS}"
}
IP="$(resolve_from_inventory | sed -n '1p')"
INV_USER="$(resolve_from_inventory | sed -n '2p')"
if [[ -z "${IP}" ]]; then
echo "Could not resolve ansible_host for ${HOST} in ${INVENTORY_HOSTS}" >&2
exit 1
fi
echo "==> ${HOST} (${BOOTSTRAP_USER}@${IP} -> ${TARGET_USER})"
echo " Inventory ansible_user: ${INV_USER:-<unset>}"
echo " Public key: ${PUBKEY_FILE}"
echo ""
echo "Step 1/3: Install key for ${BOOTSTRAP_USER} (password: ${BOOTSTRAP_USER})"
ssh-copy-id -i "${PUBKEY_FILE}" -o StrictHostKeyChecking=accept-new \
"${BOOTSTRAP_USER}@${IP}"
echo ""
echo "Step 2/3: Copy key and configure ${TARGET_USER} via su (password: root)"
REMOTE_KEY="/tmp/ansible-bootstrap.pub"
scp -o StrictHostKeyChecking=accept-new "${PUBKEY_FILE}" \
"${BOOTSTRAP_USER}@${IP}:${REMOTE_KEY}"
ssh -t "${BOOTSTRAP_USER}@${IP}" bash -s <<REMOTE_SCRIPT
set -e
REMOTE_KEY="${REMOTE_KEY}"
su - root <<ROOT_SCRIPT
set -e
mkdir -p /root/.ssh
chmod 700 /root/.ssh
touch /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
if ! grep -qF "\$(cat "\${REMOTE_KEY}")" /root/.ssh/authorized_keys 2>/dev/null; then
cat "\${REMOTE_KEY}" >> /root/.ssh/authorized_keys
fi
rm -f "\${REMOTE_KEY}"
if [ -f /etc/ssh/sshd_config ]; then
if grep -q '^PermitRootLogin' /etc/ssh/sshd_config; then
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
else
echo 'PermitRootLogin prohibit-password' >> /etc/ssh/sshd_config
fi
systemctl restart ssh 2>/dev/null \
|| systemctl restart sshd 2>/dev/null \
|| service ssh restart 2>/dev/null \
|| true
fi
echo "OK: root authorized_keys updated; PermitRootLogin prohibit-password"
ROOT_SCRIPT
REMOTE_SCRIPT
echo ""
echo "Step 3/3: Verify ${TARGET_USER} key login"
ssh -o BatchMode=yes -i "${PUBKEY_FILE}" -o StrictHostKeyChecking=accept-new \
"${TARGET_USER}@${IP}" "echo OK: ${TARGET_USER}@${IP} accepts your SSH key"
echo ""
echo "Done: ${HOST} — use: ssh -i ${PUBKEY_FILE} ${TARGET_USER}@${IP}"