clawdie-iso/firstboot/shell-ssh.sh
123kupola f1dd03da0a feat: ground-up .bashrc rewrite — sudo→mdo alias, ZFS safety (Sam & Hermes)
- shell-system.sh (runs last, step 6): rewrite .profile with agent launcher,
  full .bashrc with sudo() shell function that fire-and-forget snapshots
  zroot@cli-<ts> before mdo -u root. PATH, prompt, history, aliases (ll/la/lt).
  Agent fallback for non-login shells via ~/.ssh-agent-env.
- shell-ssh.sh (step 4): strip to ~/.ssh/config only. Agent/profile/tmux
  seeding moved to shell-system.sh so it is not overwritten.
- AGENTS.md: document sudo→mdo decision with rationale table (ISO size,
  audit surface, single privilege path, ZFS rollback safety).
2026-06-04 20:04:23 +02:00

307 lines
9.8 KiB
Bash

#!/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@<domain>"
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