diff --git a/proxmox/scripts/bootstrap.sh b/proxmox/scripts/bootstrap.sh index 9b82a05..49b1ab7 100755 --- a/proxmox/scripts/bootstrap.sh +++ b/proxmox/scripts/bootstrap.sh @@ -65,3 +65,9 @@ fi echo " - ${SCRIPT_DIR}/create-admin-user.sh (PVE web UI account)" echo " - Enable 2FA/TOTP for that user and root@pam via the web UI" echo " - ${SCRIPT_DIR}/audit.sh (verify everything above)" +echo +echo " If this host will be enrolled in FreeIPA:" +echo " ipa-client-install --domain=sweet.home --realm=SWEET.HOME \\" +echo " --server=domain-controller.sweet.home --mkhomedir --ssh-trust-dns --no-ntp" +echo " ${SCRIPT_DIR}/setup-ipa-sudo.sh (NOPASSWD sudo for IPA admins group)" +echo " ${SCRIPT_DIR}/create-local-backdoor.sh (emergency local account)" diff --git a/proxmox/scripts/create-local-backdoor.sh b/proxmox/scripts/create-local-backdoor.sh new file mode 100755 index 0000000..1ba35bf --- /dev/null +++ b/proxmox/scripts/create-local-backdoor.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# Create a local 'pveadmin' account as an emergency backdoor for when +# IPA/SSSD is unavailable. The account authenticates by SSH key only +# (password auth is disabled by harden-ssh.sh); the password set here +# is for physical console access only. +# +# Idempotent - safe to re-run. If the account already exists, the SSH key +# is refreshed but the password and account are left unchanged. Run as root. +# +# Usage: +# ./create-local-backdoor.sh +# ./create-local-backdoor.sh --key-file +# +# Set BACKDOOR_PASS env var to supply the console password non-interactively; +# otherwise you will be prompted. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/common.sh +source "${SCRIPT_DIR}/lib/common.sh" +require_root + +USERNAME="pveadmin" + +SSH_KEY="" +if [ "${1:-}" = "--key-file" ]; then + KEY_FILE="${2:-}" + [ -z "$KEY_FILE" ] && { echo "ERROR: --key-file requires a path." >&2; exit 1; } + [ -f "$KEY_FILE" ] || { echo "ERROR: key file not found: $KEY_FILE" >&2; exit 1; } + SSH_KEY="$(cat "$KEY_FILE")" +else + SSH_KEY="${1:-}" +fi + +if [ -z "$SSH_KEY" ]; then + echo "Usage: $0 " >&2 + echo " $0 --key-file " >&2 + exit 1 +fi + +if ! echo "$SSH_KEY" | grep -qE '^(ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp[0-9]+) [A-Za-z0-9+/=]'; then + echo "ERROR: argument doesn't look like a valid SSH public key." >&2 + exit 1 +fi + +if id "$USERNAME" >/dev/null 2>&1; then + echo "User '${USERNAME}' already exists -- not modifying account or password." +else + useradd --create-home --shell /bin/bash "$USERNAME" + echo "Created Linux user '${USERNAME}'." + + if [ -n "${BACKDOOR_PASS:-}" ]; then + printf '%s:%s\n' "$USERNAME" "$BACKDOOR_PASS" | chpasswd + echo "Console password set." + else + echo "Set a console password for '${USERNAME}' (used for physical console access only):" + passwd "$USERNAME" + fi +fi + +HOME_DIR="$(getent passwd "$USERNAME" | cut -d: -f6)" +SSH_DIR="${HOME_DIR}/.ssh" +AUTH_FILE="${SSH_DIR}/authorized_keys" + +mkdir -p "$SSH_DIR" +chmod 700 "$SSH_DIR" +chown "${USERNAME}:${USERNAME}" "$SSH_DIR" + +if grep -qF "$SSH_KEY" "$AUTH_FILE" 2>/dev/null; then + echo "SSH key already present in ${AUTH_FILE}." +else + printf '%s\n' "$SSH_KEY" >> "$AUTH_FILE" + echo "Installed SSH key in ${AUTH_FILE}." +fi +chmod 600 "$AUTH_FILE" +chown "${USERNAME}:${USERNAME}" "$AUTH_FILE" + +SUDOERS_FILE="/etc/sudoers.d/${USERNAME}-nopasswd" +write_if_changed "$SUDOERS_FILE" "${USERNAME} ALL=(root) NOPASSWD: ALL" +chmod 0440 "$SUDOERS_FILE" +visudo -c >/dev/null +echo "Sudoers rule for '${USERNAME}' is valid." +echo +echo "'${USERNAME}' is ready: SSH key login, NOPASSWD sudo, console password set." diff --git a/proxmox/scripts/setup-ipa-sudo.sh b/proxmox/scripts/setup-ipa-sudo.sh new file mode 100755 index 0000000..561cc35 --- /dev/null +++ b/proxmox/scripts/setup-ipa-sudo.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Grant the IPA 'admins' group passwordless sudo on this host. +# +# Writes two files: +# /etc/sudoers.d/admins-nopasswd -- NOPASSWD: ALL for general shell use +# /etc/sudoers.d/admins-proxmox -- NOPASSWD for pvesh/qm/pct (PVE only; +# skipped silently if those binaries +# aren't present, e.g. on PBS/PDM) +# +# Idempotent - safe to re-run. Run as root on the target host after +# ipa-client-install has been completed and SSSD is active. +# +# Usage: ./setup-ipa-sudo.sh +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/common.sh +source "${SCRIPT_DIR}/lib/common.sh" +require_root + +if [ ! -f /etc/ipa/default.conf ]; then + echo "ERROR: /etc/ipa/default.conf not found -- is this host enrolled in FreeIPA?" >&2 + exit 1 +fi + +write_if_changed /etc/sudoers.d/admins-nopasswd '%admins ALL=(root) NOPASSWD: ALL' +chmod 0440 /etc/sudoers.d/admins-nopasswd + +# PVE-specific tools -- pvesh moved from /usr/sbin to /usr/bin in PVE 8.x; +# resolve at script time so the path in the sudoers rule is always correct. +PVESH="$(command -v pvesh 2>/dev/null || true)" +QM="$(command -v qm 2>/dev/null || true)" +PCT="$(command -v pct 2>/dev/null || true)" + +if [ -n "$PVESH" ] && [ -n "$QM" ] && [ -n "$PCT" ]; then + write_if_changed /etc/sudoers.d/admins-proxmox \ + "%admins ALL=(root) NOPASSWD: ${PVESH}, ${QM}, ${PCT}" + chmod 0440 /etc/sudoers.d/admins-proxmox + echo "Proxmox tools found -- wrote admins-proxmox." +else + echo "pvesh/qm/pct not found -- skipping admins-proxmox (not a PVE host)." +fi + +visudo -c >/dev/null +echo "All sudoers files valid. IPA admins group has sudo on this host."