# SSH hardening transaction implementation plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Make `boot --server` refuse unsafe SSH hardening and atomically roll back failed validation or reloads. **Architecture:** Keep production logic in the standalone `boot` file because it runs before the repository exists. Exercise the public `boot --server` command through a PTY, a temporary filesystem root, and PATH command adapters; never call private functions directly or touch host SSH state. **Tech Stack:** Bash 5, Python 3 standard library for the PTY driver, OpenSSH/systemd command adapters, Panama contract runner. **Spec:** `docs/superpowers/specs/2026-08-27-secure-bootstrap-privileged-installation-design.md` ## Global constraints - Preserve the public `boot --server` command and the non-root desktop bootstrap path. - Do not reload a real SSH service, edit `/etc/ssh`, create a real account, or run a live privileged check. - A missing or unsafe target key keeps root/password access unchanged and continues bootstrap. - Candidate validation or reload failure restores the prior drop-in and stops before clone/install handoff. - Exact safe state is target ownership plus `.ssh` mode `0700` and `authorized_keys` mode `0600`; symlinks, root UID, relative homes, blank/comment-only keys, and foreign ownership are refused. - `PANAMA_BOOT_FIXTURE_ROOT` is accepted only by a real non-root process whose stubbed `id -u` reports root. Real root plus that variable must fail closed. - The new public contract is hermetic and must run under `panama test --safe`. - Preserve the user's unstaged `/home/gib/.local/share/Panama/config/bash/.bashrc` change outside this worktree. ## File map - `boot`: account/key preconditions, test-only filesystem adapter, SSH unit detection, atomic drop-in transaction, rollback. - `tests/setup/root-server-bootstrap-contract`: PTY and stateful command/filesystem fixture for the real public command. - `tests/contracts.manifest`: one `hermetic` entry for the new executable contract. - `tests/setup/boot-contract`: retain the non-root clone/handoff behavior; adjust only if the later verified-bootstrap plan changes it. - `tests/setup/role-contract`: retain role/stage assertions. - `README.md`: describe key-gated transactional hardening without claiming a live reload was tested. --- ### Task 1: Gate hardening on a verified login path **Files:** - Create: `tests/setup/root-server-bootstrap-contract` - Modify: `boot:52-115` - Modify: `tests/contracts.manifest` - Test: `tests/setup/root-server-bootstrap-contract` **Interfaces:** - Consumes: public `boot --server`, `PANAMA_BOOT_FIXTURE_ROOT`, PTY answers `gib`, password already set, and `Y` for hardening. - Produces: `system_path ABSOLUTE_PATH`, `safe_authorized_keys USER HOME`, and `harden_server_ssh USER HOME`; later tasks extend the last function with the transaction. - [ ] **Step 1: Build the public fixture and write the failing precondition table** Create a checked temporary tree with `root/etc/ssh/sshd_config.d`, `root/root/.ssh`, `root/home/gib/.ssh`, `bin`, `calls`, and a stub cloned installer. Use Python's `pty.openpty()` to start: ```python env = { **os.environ, "PATH": f"{stub_dir}:/usr/bin:/bin", "PANAMA_BOOT_FIXTURE_ROOT": fixture_root, "PANAMA_PATH": f"{fixture_root}/home/gib/.local/share/Panama", "HOME": f"{fixture_root}/root", } process = subprocess.Popen( ["bash", boot, "--server"], stdin=slave, stdout=slave, stderr=slave, env=env, start_new_session=True, ) os.write(master, b"gib\nY\n") ``` PATH stubs must log one shell-escaped argv vector per line. `id -u` with no username reports `0`; `id -u root` reports `0`; `id -u gib` reports `1000`; `id -nG gib` prints `gib wheel`; `passwd -S gib` prints `gib PS`; `getent passwd gib` prints the logical absolute home `/home/gib`; `runuser` materializes the clone/install handoff without changing users. `boot` resolves that logical home beneath the fixture root through `system_path`. Stub `stat` for fixture ownership metadata plus `dnf`, `git`, `sshd`, and `systemctl`; any unexpected command exits `97`. Run one table row per unsafe state: ```text missing empty comment-only ssh-directory-symlink authorized-keys-symlink directory-wrong-mode file-wrong-mode directory-wrong-owner file-wrong-owner root-target-account relative-home ``` For every row assert status `0`, a diagnostic containing `SSH hardening unavailable`, no `sshd -t`, no `systemctl reload`, no drop-in change, and a recorded install handoff. Add safe existing-key and safe root-key-copy rows that currently reach the unsafe direct-write path and therefore fail the new expected command ordering. - [ ] **Step 2: Run the contract and confirm the red behavior** Run: ```bash bash -n tests/setup/root-server-bootstrap-contract tests/setup/root-server-bootstrap-contract ``` Expected: nonzero findings showing current `boot` writes/reloads without the required key checks and has no fixture-root support. - [ ] **Step 3: Add the guarded filesystem adapter and key checks** Add these shapes near the root branch, using `stat -Lc` only after rejecting symlinks: ```bash BOOT_ROOT="${PANAMA_BOOT_FIXTURE_ROOT:-}" if [[ -n "$BOOT_ROOT" && "$EUID" -eq 0 ]]; then echo "boot: PANAMA_BOOT_FIXTURE_ROOT is test-only" >&2 exit 1 fi system_path() { local path="$1" [[ "$path" == /* ]] || return 2 printf '%s%s\n' "$BOOT_ROOT" "$path" } safe_authorized_keys() { local username="$1" user_home="$2" uid ssh_dir keys uid="$(id -u "$username")" || return 1 [[ "$uid" =~ ^[0-9]+$ && "$uid" != 0 && "$user_home" == /* ]] || return 1 ssh_dir="$user_home/.ssh" keys="$ssh_dir/authorized_keys" [[ -d "$ssh_dir" && ! -L "$ssh_dir" && -f "$keys" && ! -L "$keys" ]] || return 1 [[ "$(stat -Lc '%u:%a' "$ssh_dir")" == "$uid:700" ]] || return 1 [[ "$(stat -Lc '%u:%a' "$keys")" == "$uid:600" ]] || return 1 grep -qEv '^[[:space:]]*(#|$)' "$keys" } ``` Resolve the target home, `/root/.ssh/authorized_keys`, and `/etc/ssh/sshd_config.d` through `system_path`. A copyable root key must be a non-symlinked regular file owned by UID 0, mode `0600`, with at least one nonblank/non-comment line. Never overwrite an existing `authorized_keys`. When it is absent, either create a missing `.ssh` or require an existing `.ssh` to already be a real directory owned by the target UID with mode `0700`; then copy only the key and apply ownership/mode to those two paths. Never use `chown -R`. If `safe_authorized_keys` still fails, print the unavailable message and skip the prompt/transaction. - [ ] **Step 4: Add the hermetic manifest entry and run focused checks** Insert the sorted manifest line with a directly preceding comment: ```text # Root bootstrap runs entirely against a temporary filesystem and PATH adapters. hermetic tests/setup/root-server-bootstrap-contract ``` Run: ```bash bash -n boot tests/setup/root-server-bootstrap-contract tests/setup/contract-manifest-contract tests/setup/root-server-bootstrap-contract tests/setup/boot-contract tests/setup/role-contract ./bin/panama test --safe root-server-bootstrap git diff --check ``` Expected: all pass; the public fixture proves unsafe keys do not invoke `sshd` or reload while bootstrap still hands off. - [ ] **Step 5: Commit the precondition gate** ```bash git add boot tests/setup/root-server-bootstrap-contract tests/contracts.manifest git commit -m "Fix: Gate SSH hardening on a verified key" ``` --- ### Task 2: Make the drop-in transaction recoverable **Files:** - Modify: `boot:42-137` - Modify: `tests/setup/root-server-bootstrap-contract` - Test: `tests/setup/root-server-bootstrap-contract` **Interfaces:** - Consumes: `safe_authorized_keys`, `system_path`, the fixture's `sshd`/`systemctl` state files, and a detected service name. - Produces: `detect_ssh_unit`, `restore_ssh_dropin`, and a complete `harden_server_ssh USER HOME` transaction returning 0 only after validation and reload. - [ ] **Step 1: Add failing success and rollback scenarios** Extend the command state with `SSHD_RESULTS` and `RELOAD_RESULTS`, consumed one result per call. Add exact cases: ```text success-without-prior-dropin: validate=0 reload=0 success-replaces-prior-dropin: validate=0 reload=0 candidate-invalid: validate=1,0 reload= candidate-reload-fails: validate=0,0 reload=1,0 rollback-validation-fails: validate=0,1 reload=1 rollback-reload-fails: validate=0,0 reload=1,1 ``` Assert the desired two-line content, validation before reload, only the detected unit, byte-for-byte restoration, restored validation/reload ordering, nonzero status and no install handoff on every transactional failure, no `*.tmp`/`*.backup` residue on success, and retained backup plus recovery commands when rollback fails. - [ ] **Step 2: Run the focused contract and confirm it fails on current code** Run: ```bash tests/setup/root-server-bootstrap-contract ``` Expected: nonzero because current code writes the final path directly, never validates, guesses units through reload failure, and reports success after failed reload. - [ ] **Step 3: Implement unit detection, atomic install, and rollback** Use explicit transaction state and traps. The core shape is: ```bash detect_ssh_unit() { local unit for unit in sshd.service ssh.service; do systemctl cat "$unit" >/dev/null 2>&1 && { printf '%s\n' "$unit"; return 0; } done return 1 } restore_ssh_dropin() { if [[ -n "${ssh_backup:-}" && -e "$ssh_backup" ]]; then local restore restore="$(mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.restore)" || return 1 cp -a -- "$ssh_backup" "$restore" mv -f -- "$restore" "$ssh_dropin" else rm -f -- "$ssh_dropin" fi } ``` Create the candidate with `umask 077; mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.tmp`, write exact desired content, and preserve an existing final file in a collision-safe same-directory `mktemp` name ending `.backup`, not `.conf`. Save any prior `EXIT`, `INT`, and `TERM` traps, then arm transaction traps before `mv -f` activates the candidate. The EXIT handler restores only while `ssh_transaction_active=1`; every success or handled failure path restores the prior traps before returning. After activation: ```bash if ! sshd -t; then restore_ssh_dropin sshd -t || rollback_failed=1 return 1 fi if ! systemctl reload "$ssh_unit"; then restore_ssh_dropin sshd -t || rollback_failed=1 systemctl reload "$ssh_unit" || rollback_failed=1 return 1 fi ``` On clean success set `ssh_transaction_active=0`, clear the local traps, and remove the backup. On rollback failure keep the backup and print its absolute path plus `sshd -t` and `systemctl reload UNIT` recovery commands. Do not continue to clone/install after a hardening transaction returns nonzero. - [ ] **Step 4: Verify failure status, cleanup, and old public behavior** Run: ```bash bash -n boot tests/setup/root-server-bootstrap-contract tests/setup/root-server-bootstrap-contract tests/setup/boot-contract tests/setup/role-contract ./bin/panama test --safe root-server-bootstrap git diff --check ``` Expected: all pass; every rollback scenario preserves the previous drop-in and the safe success path validates before one reload. - [ ] **Step 5: Commit the transaction** ```bash git add boot tests/setup/root-server-bootstrap-contract git commit -m "Fix: Roll back failed SSH hardening" ``` --- ### Task 3: Synchronize operator documentation and close the SSH plan **Files:** - Modify: `README.md:40-48` - Modify: `.claude/skills/panama/SKILL.md` - Modify: `skills/panama-desktop/SKILL.md` only if it describes root bootstrap - Test: `tests/setup/readme-contract` **Interfaces:** - Consumes: the landed `boot --server` behavior from Tasks 1-2. - Produces: accurate user-facing preconditions, skip behavior, rollback behavior, and no claim of live-host proof. - [ ] **Step 1: Write the documentation assertions first** Extend `tests/setup/readme-contract` to require nearby root-bootstrap prose containing all of: verified target key, `sshd -t`, atomic drop-in, reload rollback, and hardening unavailable without a key. Reject wording that says Panama merely writes the file or that reload failure is ignored. - [ ] **Step 2: Run the README contract and confirm the old prose fails** Run: ```bash tests/setup/readme-contract ``` Expected: nonzero until README describes the transactional behavior. - [ ] **Step 3: Update the documentation without claiming a live reload** State plainly that Panama copies or verifies the target key, offers hardening only with exact safe ownership/modes, validates the complete config, reloads the detected unit, and restores the previous drop-in on failure. State that fixture contracts test these paths and no real daemon reload runs under `panama test --safe`. - [ ] **Step 4: Run the plan gate** ```bash bash -n boot tests/setup/root-server-bootstrap-contract tests/setup/root-server-bootstrap-contract tests/setup/boot-contract tests/setup/role-contract tests/setup/readme-contract ./bin/panama test --safe git diff --check ``` Expected: 132 hermetic contracts pass after adding the new contract; non-hermetic skip counts remain unchanged. - [ ] **Step 5: Commit the synchronized documentation** ```bash git add README.md .claude/skills/panama/SKILL.md skills/panama-desktop/SKILL.md tests/setup/readme-contract git commit -m "Docs: Explain transactional SSH hardening" ```