diff --git a/AGENTS.md b/AGENTS.md index 563f0610..02937c1a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -101,11 +101,31 @@ inspection as final proof that SDDM/XFCE works. **Privilege model:** distinguish build-host administration from live-USB runtime. - On the FreeBSD 15 build host, operator-facing commands may use `sudo`. -- Inside the live USB, `sudo` is intentionally absent. +- Inside the live USB, `sudo` is intentionally absent (deleted via `pkg delete -f sudo`). - Live privileged actions use FreeBSD `mac_do` via `mdo -u root `. +- `~/.bashrc` aliases `sudo` → `mdo -u root` for muscle-memory compatibility. + The shell function wraps `mdo` with a fire-and-forget ZFS snapshot + (`zroot@cli-`) before each privileged invocation, so rollback + is always available without confirmation prompts or warning popups. - Agent runtime code must not shell out to `sudo` for privileged host changes. - Privileged Clawdie-AI host operations go through the hostd RPC layer. +**Why `sudo` as alias, not as pkg:** + +| Approach | Install sudo pkg | Alias to mdo | +|----------|-----------------|--------------| +| Extra package | Yes (sudo + deps) | No | +| Two privilege paths | `sudo` AND `mdo` coexist | Single path (`mdo`) | +| Audit surface | Two tools to audit | One kernel-enforced MAC | +| ZFS safety net | Requires sudoers hooks | Built into bash function | +| Agent confusion | Agents try both paths | Agents use `sudo`, get `mdo` | +| ISO size | Larger | Zero bytes | + +The alias approach keeps the ISO lean, eliminates dual-privilege-path +confusion, and bakes ZFS rollback safety directly into the operator shell. +`mac_do` is FreeBSD base system — no package dependency, kernel-enforced, +auditable via MAC framework. + --- ## Installer Temp Files diff --git a/firstboot/shell-ssh.sh b/firstboot/shell-ssh.sh index b9bf68ed..c6e834c3 100644 --- a/firstboot/shell-ssh.sh +++ b/firstboot/shell-ssh.sh @@ -25,7 +25,7 @@ clawdie_shell_ssh_setup() { # 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 agent persistence (~/.ssh/config + ~/.tmux.conf) + # 4. Seed SSH client defaults (~/.ssh/config) log_msg "[ssh] Starting SSH and password setup" @@ -43,8 +43,8 @@ clawdie_shell_ssh_setup() { if [ -n "${SSH_PUBLIC_KEY:-}" ]; then clawdie_shell_ssh_install_pubkey clawdie_shell_ssh_disable_password_auth - clawdie_shell_ssh_seed_agent_config - log_msg "[ssh] SSH public key installed, password auth disabled, agent config seeded" + 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)" @@ -105,45 +105,18 @@ clawdie_shell_ssh_install_pubkey() { } # ============================================================================ -# SSH AGENT PERSISTENCE (seed ~/.profile, ~/.ssh/config, ~/.tmux.conf) +# SSH CLIENT CONFIGURATION (seed ~/.ssh/config) # ============================================================================ -clawdie_shell_ssh_seed_agent_config() { - # Seed SSH agent launcher, auto-load, and terminal preferences. - # FreeBSD: no systemd — use ~/.profile to start the agent on login. - # tmux's default update-environment already includes SSH_AUTH_SOCK; - # do NOT override it (would drop DISPLAY, XAUTHORITY, etc.). +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 profile="/home/clawdie/.profile" local ssh_config="/home/clawdie/.ssh/config" - local tmux_conf="/home/clawdie/.tmux.conf" - # --- Tier 1: ~/.profile — start agent on every login (FreeBSD, no systemd) --- - if [ ! -f "$profile" ]; then - cat > "$profile" <<'PROFEOF' -# Start SSH agent if not already running -if [ -z "$SSH_AUTH_SOCK" ]; then - eval $(ssh-agent -s) > /dev/null 2>&1 -fi -PROFEOF - chmod 644 "$profile" - chown clawdie:clawdie "$profile" 2>/dev/null || true - log_msg "[ssh] Seeded ~/.profile with SSH agent launcher" - else - if ! grep -q 'ssh-agent -s' "$profile" 2>/dev/null; then - cat >> "$profile" <<'PROFEOF' - -# Start SSH agent if not already running -if [ -z "$SSH_AUTH_SOCK" ]; then - eval $(ssh-agent -s) > /dev/null 2>&1 -fi -PROFEOF - log_msg "[ssh] Appended SSH agent launcher to existing ~/.profile" - fi - fi - - # --- Tier 2: ~/.ssh/config — auto-load key on first use --- + # --- ~/.ssh/config: auto-load key on first use, no agent forwarding --- if [ ! -f "$ssh_config" ]; then cat > "$ssh_config" <<'SSHEOF' Host * @@ -160,20 +133,6 @@ SSHEOF fi fi - # --- Tier 3: ~/.tmux.conf — UI preferences only (agent socket is default) --- - # tmux update-environment already includes SSH_AUTH_SOCK + DISPLAY. - # Seed mouse + indexing; do NOT override update-environment. - if [ ! -f "$tmux_conf" ]; then - cat > "$tmux_conf" <<'TMUXEOF' -set -g base-index 1 -setw -g pane-base-index 1 -set -g mouse on -TMUXEOF - chmod 644 "$tmux_conf" - chown clawdie:clawdie "$tmux_conf" 2>/dev/null || true - log_msg "[ssh] Seeded ~/.tmux.conf (base-index 1, mouse on)" - fi - return 0 } diff --git a/firstboot/shell-system.sh b/firstboot/shell-system.sh index 2c61c9ca..7c12ad49 100755 --- a/firstboot/shell-system.sh +++ b/firstboot/shell-system.sh @@ -354,6 +354,12 @@ EOF cat > /home/clawdie/.profile <<'EOF' # Clawdie operator POSIX shell profile. [ -r /etc/profile.d/clawdie.sh ] && . /etc/profile.d/clawdie.sh + +# Start SSH agent on login (FreeBSD: no systemd, no X11 agent launcher). +# Non-login shells (tmux windows) use ~/.bashrc fallback instead. +if [ -z "$SSH_AUTH_SOCK" ]; then + eval $(ssh-agent -s) > /dev/null 2>&1 +fi EOF cat > /home/clawdie/.bash_profile <<'EOF' # Clawdie operator bash login profile. @@ -365,14 +371,50 @@ EOF } [ -r "${HOME}/.bashrc" ] && . "${HOME}/.bashrc" EOF - cat > /home/clawdie/.bashrc <<'EOF' -# Clawdie operator interactive bash profile. + cat > /home/clawdie/.bashrc <<'BASHRC' +# Clawdie operator interactive bash shell. +# Non-interactive shells stop here. +case $- in *i*) ;; *) return ;; esac + +# ── PATH (FreeBSD: /usr/local first) ────────────────────────── +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" [ -r /etc/profile.d/clawdie.sh ] && . /etc/profile.d/clawdie.sh -if [ -n "${PS1:-}" ]; then - export HISTFILE="${HISTFILE:-/tmp/clawdie/bash_history}" - mkdir -p /tmp/clawdie 2>/dev/null || true + +# ── Privilege escalation ────────────────────────────────────── +# Clawdie ISO: no sudo pkg. Use FreeBSD mac_do instead. +# mac_do rules (gid=0>uid=0) allow wheel→root transitions. +# ZFS auto-snapshot provides safety net — no confirmation prompts. +sudo() { + local pool + pool=$(zpool list -H -o name 2>/dev/null | head -1) + if [ -n "$pool" ]; then + (zfs snapshot "${pool}@cli-$(date +%s)" 2>/dev/null &) + fi + mdo -u root "$@" +} + +# ── SSH agent (fallback for non-login shells) ───────────────── +# .profile starts the agent for login shells; tmux windows +# (non-login) inherit via SSH_AUTH_SOCK if the parent had one, +# but if not, try a stored env file from a prior login. +if [ -z "$SSH_AUTH_SOCK" ] && [ -f ~/.ssh-agent-env ]; then + . ~/.ssh-agent-env 2>/dev/null || true fi -EOF + +# ── Prompt ──────────────────────────────────────────────────── +PS1='\h:\w\$ ' + +# ── History ─────────────────────────────────────────────────── +export HISTFILE="${HISTFILE:-/tmp/clawdie/bash_history}" +export HISTSIZE=5000 +export HISTFILESIZE=10000 +mkdir -p /tmp/clawdie 2>/dev/null || true + +# ── Aliases ─────────────────────────────────────────────────── +alias ll='ls -lah' +alias la='ls -A' +alias lt='ls -laht' +BASHRC cat > /home/clawdie/.zprofile <<'EOF' # Clawdie operator zsh login profile. [ -r /etc/profile ] && . /etc/profile