#!/bin/sh # Clawdie Shell — SSH & System User Password Module # Purpose: Configure SSH keys and system user passwords # Author: Clawdie Project # POSIX-compliant (no bash-isms) set -eu # Configuration (can be overridden for testing) LOG_FILE="${LOG_FILE:-/var/log/clawdie-firstboot.log}" PROGRESS_FILE="${PROGRESS_FILE:-/var/log/clawdie-firstboot.progress}" EMERGENCY_PASSWORD_FILE="${EMERGENCY_PASSWORD_FILE:-/root/.firstboot-emergency-password}" # Derived from wizard/build inputs (caller sets these) # SSH_PUBLIC_KEY - Optional SSH public key (ssh-ed25519 or ssh-rsa) # ROOT_PASSWORD - Optional root password (if empty, auto-generate) # CLAWDIE_USER_PASSWORD - Optional clawdie user password (if empty, auto-generate) # ============================================================================ # MAIN ENTRY POINT # ============================================================================ clawdie_shell_ssh_setup() { # Main orchestrator # 1. Configure SSH keys (if provided) # 2. Set system passwords (if provided or auto-generate) # 3. Configure SSH auth methods (key-only or key+password) # 4. Seed SSH client defaults (~/.ssh/config) log_msg "[ssh] Starting SSH and password setup" # Generate emergency root password if not provided if [ -z "${ROOT_PASSWORD:-}" ]; then ROOT_PASSWORD=$(gen_secret) fi # Generate clawdie user password if not provided if [ -z "${CLAWDIE_USER_PASSWORD:-}" ]; then CLAWDIE_USER_PASSWORD=$(gen_secret) fi # Setup SSH keys if provided if [ -n "${SSH_PUBLIC_KEY:-}" ]; then clawdie_shell_ssh_install_pubkey clawdie_shell_ssh_disable_password_auth clawdie_shell_ssh_seed_client_config log_msg "[ssh] SSH public key installed, password auth disabled, client config seeded" else clawdie_shell_ssh_enable_password_auth log_msg "[ssh] No SSH key provided, password auth enabled (less secure)" fi # Set system user passwords clawdie_shell_ssh_set_passwords "$ROOT_PASSWORD" "$CLAWDIE_USER_PASSWORD" log_msg "[ssh] System user passwords set" # Save emergency root password to file clawdie_shell_ssh_save_emergency_password "$ROOT_PASSWORD" log_msg "[ssh] Emergency root password saved" echo "[SSH] COMPLETE" >> "$PROGRESS_FILE" log_msg "[ssh] SSH setup complete" } # ============================================================================ # SSH PUBLIC KEY INSTALLATION # ============================================================================ clawdie_shell_ssh_install_pubkey() { # Install SSH_PUBLIC_KEY to authorized_keys for both root and clawdie user local root_ssh_dir="/root/.ssh" local clawdie_ssh_dir="/home/clawdie/.ssh" # Setup root authorized_keys if [ ! -d "$root_ssh_dir" ]; then mkdir -p "$root_ssh_dir" chmod 700 "$root_ssh_dir" fi echo "$SSH_PUBLIC_KEY" >> "$root_ssh_dir/authorized_keys" 2>/dev/null || { log_msg "[ssh] ERROR: Failed to install SSH key for root" return 1 } chmod 600 "$root_ssh_dir/authorized_keys" log_msg "[ssh] Installed SSH key for root user" # Setup clawdie authorized_keys if [ ! -d "$clawdie_ssh_dir" ]; then mkdir -p "$clawdie_ssh_dir" chmod 700 "$clawdie_ssh_dir" fi echo "$SSH_PUBLIC_KEY" >> "$clawdie_ssh_dir/authorized_keys" 2>/dev/null || { log_msg "[ssh] ERROR: Failed to install SSH key for clawdie" return 1 } chmod 600 "$clawdie_ssh_dir/authorized_keys" chown -R clawdie:clawdie "$clawdie_ssh_dir" log_msg "[ssh] Installed SSH key for clawdie user" return 0 } # ============================================================================ # SSH CLIENT CONFIGURATION (seed ~/.ssh/config) # ============================================================================ clawdie_shell_ssh_seed_client_config() { # Seed SSH client defaults for the clawdie user. # .profile / .bashrc / .tmux.conf are handled by shell-system.sh # (which runs after this module). # Run after clawdie_shell_ssh_install_pubkey (requires user to exist). local ssh_config="/home/clawdie/.ssh/config" # --- ~/.ssh/config: auto-load key on first use, no agent forwarding --- if [ ! -f "$ssh_config" ]; then cat > "$ssh_config" <<'SSHEOF' Host * AddKeysToAgent yes ForwardAgent no SSHEOF chmod 600 "$ssh_config" chown clawdie:clawdie "$ssh_config" 2>/dev/null || true log_msg "[ssh] Seeded ~/.ssh/config with AddKeysToAgent yes" else if ! grep -q 'AddKeysToAgent' "$ssh_config" 2>/dev/null; then printf '\nHost *\n AddKeysToAgent yes\n ForwardAgent no\n' >> "$ssh_config" log_msg "[ssh] Appended AddKeysToAgent to existing ~/.ssh/config" fi fi return 0 } # ============================================================================ # SSH AUTH METHOD CONFIGURATION # ============================================================================ clawdie_shell_ssh_disable_password_auth() { # Disable password authentication in sshd_config # Keep PubkeyAuthentication enabled local sshd_config="/etc/ssh/sshd_config" if [ ! -f "$sshd_config" ]; then log_msg "[ssh] WARNING: sshd_config not found" return 1 fi # Ensure PubkeyAuthentication is enabled if grep -q "^#*PubkeyAuthentication" "$sshd_config"; then sed -i '' 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' "$sshd_config" else echo "PubkeyAuthentication yes" >> "$sshd_config" fi # Disable PasswordAuthentication if grep -q "^#*PasswordAuthentication" "$sshd_config"; then sed -i '' 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' "$sshd_config" else echo "PasswordAuthentication no" >> "$sshd_config" fi # Disable PermitRootLogin with password (allow pubkey only) if grep -q "^#*PermitRootLogin" "$sshd_config"; then sed -i '' 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' "$sshd_config" else echo "PermitRootLogin prohibit-password" >> "$sshd_config" fi log_msg "[ssh] Disabled password auth in sshd_config" return 0 } clawdie_shell_ssh_enable_password_auth() { # Enable password authentication in sshd_config (fallback) # Less secure but prevents lockout if user loses SSH key local sshd_config="/etc/ssh/sshd_config" if [ ! -f "$sshd_config" ]; then log_msg "[ssh] WARNING: sshd_config not found" return 1 fi # Enable PasswordAuthentication if grep -q "^#*PasswordAuthentication" "$sshd_config"; then sed -i '' 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' "$sshd_config" else echo "PasswordAuthentication yes" >> "$sshd_config" fi # Allow root login with password if grep -q "^#*PermitRootLogin" "$sshd_config"; then sed -i '' 's/^#*PermitRootLogin.*/PermitRootLogin yes/' "$sshd_config" else echo "PermitRootLogin yes" >> "$sshd_config" fi log_msg "[ssh] WARNING: Password auth enabled (less secure)" return 0 } # ============================================================================ # PASSWORD MANAGEMENT # ============================================================================ clawdie_shell_ssh_set_passwords() { local root_pwd="$1" local clawdie_pwd="$2" # Set root password using echo | pw usermod echo "$root_pwd" | pw usermod root -h 0 2>/dev/null || { log_msg "[ssh] ERROR: Failed to set root password" return 1 } log_msg "[ssh] Root password set" # Set clawdie user password # Create user if it doesn't exist. Prefer bash when the package is present; # fall back to /bin/sh during partial recovery states. local clawdie_shell="/bin/sh" if [ -x /usr/local/bin/bash ]; then clawdie_shell="/usr/local/bin/bash" if ! grep -qx '/usr/local/bin/bash' /etc/shells 2>/dev/null; then echo '/usr/local/bin/bash' >> /etc/shells 2>/dev/null || true fi fi if [ -x /usr/local/bin/zsh ] && ! grep -qx '/usr/local/bin/zsh' /etc/shells 2>/dev/null; then echo '/usr/local/bin/zsh' >> /etc/shells 2>/dev/null || true fi if ! id clawdie >/dev/null 2>&1; then pw useradd -n clawdie -u 1000 -g wheel -m -s "$clawdie_shell" 2>/dev/null || { log_msg "[ssh] ERROR: Failed to create clawdie user" return 1 } log_msg "[ssh] Created clawdie user" elif [ "$clawdie_shell" = "/usr/local/bin/bash" ]; then pw usermod clawdie -s /usr/local/bin/bash 2>/dev/null || true fi echo "$clawdie_pwd" | pw usermod clawdie -h 0 2>/dev/null || { log_msg "[ssh] ERROR: Failed to set clawdie password" return 1 } log_msg "[ssh] Clawdie user password set" return 0 } clawdie_shell_ssh_save_emergency_password() { local root_pwd="$1" # Save emergency password to file (root-only, readable only by root) { echo "EMERGENCY ROOT PASSWORD" echo "==============================" echo "" echo "Generated at: $(date)" echo "Password: $root_pwd" echo "" echo "Access:" echo " ssh root@" echo "" echo "WARNING: Store this securely." echo "This file should be moved to secure storage and deleted from this system." } > "$EMERGENCY_PASSWORD_FILE" 2>/dev/null || { log_msg "[ssh] WARNING: Could not save emergency password file" return 1 } chmod 600 "$EMERGENCY_PASSWORD_FILE" log_msg "[ssh] Emergency password saved to $EMERGENCY_PASSWORD_FILE" return 0 } # ============================================================================ # UTILITY: Logging & Secret Generation # ============================================================================ log_msg() { local msg="$1" echo "$msg" >> "$LOG_FILE" 2>/dev/null || true } gen_secret() { openssl rand -base64 32 | tr -d '\n/+=' | head -c 32 } # ============================================================================ # Export for use by firstboot.sh # ============================================================================ case "${0##*/}" in shell-ssh.sh) # Direct execution (for testing) clawdie_shell_ssh_setup ;; *) # Sourced from another script — functions available ;; esac