Fix: Complete SSH bootstrap hardening

This commit is contained in:
Gabriel Brown
2026-08-27 05:19:58 -04:00
parent 98e29eb8f3
commit fc8f226747
9 changed files with 852 additions and 186 deletions
@@ -33,8 +33,11 @@ real firewall, install a real package, start a service, or apply changes to a se
- Safe target SSH state means a non-root account, an absolute home, a real `.ssh`
directory owned by the target UID with mode `0700`, and a nonempty regular
`authorized_keys` file owned by the target UID with mode `0600`. Symlinks are
refused. Panama may create and normalize files it copied from root, but it does
not take ownership of an unsafe pre-existing target path.
refused. Every nonblank, non-comment key line must parse with OpenSSH tooling.
Panama may create and normalize files it copied from root, but final destination
creation and writing run as the target UID. It does not take ownership of an
unsafe pre-existing target path or assume that the user's primary group has the
same name as the user.
- Failed SSH reload rollback includes restored-config validation and a reload of the
restored configuration, because a command can apply state and still return
nonzero.
@@ -92,16 +95,21 @@ Before offering hardening, Panama verifies:
absolute and nonempty;
- neither the home-relative `.ssh` path nor `authorized_keys` is a symlink;
- `.ssh` and `authorized_keys` have the exact ownership and modes in Decisions;
- `authorized_keys` contains at least one nonblank, non-comment line;
- exactly one installed SSH unit is detected, preferring `sshd.service` and falling
back to `ssh.service` only when the first unit is absent.
- `authorized_keys` contains at least one nonblank, non-comment line, and OpenSSH
parses every such line as a public key;
- an installed SSH unit is detected, preferring `sshd.service` and falling back to
`ssh.service` only when the first unit is absent.
If the target has no key and root has a safe regular key file, Panama copies only that
file, creates `.ssh`, applies `0700/0600`, and changes ownership only on those two
paths. It does not recursively take ownership of an existing directory tree.
file. The target UID creates or normalizes `.ssh` at `0700` and writes
`authorized_keys` at `0600` through an already-open root-key input. Panama then
revalidates exact UID ownership, modes, and OpenSSH key parsing. It does not chown a
target-controlled path or assume a same-named primary group.
If the preconditions fail, Panama prints why hardening is unavailable, keeps
root/password authentication unchanged, and continues the clone/install handoff.
If the key preconditions fail, no SSH unit is installed, or the Panama drop-in path
already names a symlink or non-regular object, Panama prints why hardening is
unavailable, keeps root/password authentication unchanged, and continues the
clone/install handoff. Declining the prompt has the same unchanged-state outcome.
### Transaction
@@ -110,22 +118,32 @@ The desired drop-in is exactly:
```text
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
```
Panama creates the candidate with `umask 077` and `mktemp` in
`/etc/ssh/sshd_config.d`. Its temporary name does not end in `.conf`, so the normal
include glob cannot activate it early. It preserves an existing
`90-panama.conf` in the same directory, arms EXIT/INT/TERM rollback, and atomically
renames the candidate over the final path.
include glob cannot activate it early. It refuses a pre-existing Panama path unless
it is a non-symlink regular file. It preserves an existing `00-panama.conf`, including
its ownership, mode, timestamps, ACLs, and extended attributes, in the same directory.
State-aware EXIT/INT/TERM cleanup is armed before the first candidate or backup
artifact, and the candidate is atomically renamed over the final path.
It then runs `sshd -t` against the complete active configuration and reloads only the
detected unit. Success disarms rollback and removes the backup.
It then runs `sshd -t` against the complete active configuration. Before reload,
`sshd -T -C` must report `permitrootlogin no`, `passwordauthentication no`, and
`kbdinteractiveauthentication no` for the root context. The target-user context must
report both authentication directives as `no`. This fails closed when an earlier
main-config directive wins despite the precedence-safe filename. Panama reloads only
the detected unit after every check passes. Success disarms rollback and removes the
backup.
On validation failure, Panama restores or removes the new drop-in, validates the
restored configuration, and returns nonzero without reloading the rejected candidate.
On reload failure, Panama restores the previous drop-in, validates it, reloads the
restored unit, and returns nonzero. A rollback validation/reload failure preserves the
backup and prints its path plus exact recovery commands.
backup and prints its path plus exact recovery commands. When no prior file existed,
recovery instead instructs the operator to remove `00-panama.conf`, run `sshd -t`,
and reload the detected unit.
Existing drop-ins go through the same desired-content, validation, and reload path;
mere existence is not treated as proof of hardening.
@@ -143,12 +161,18 @@ and `systemctl`, and provides temporary account and filesystem state.
Required cases are:
- missing, empty, comment-only, symlinked, wrong-owner, and wrong-mode target keys;
- missing, empty, comment-only, malformed, mixed valid/malformed, symlinked,
wrong-owner, and wrong-mode target keys;
- safe root-key copy and safe existing target key;
- target-UID copy normalization with a primary group whose name differs from the user;
- declined hardening and no installed SSH unit;
- pre-existing symlink, directory, and FIFO Panama drop-ins;
- successful initial install and replacement of an existing drop-in;
- invalid candidate rollback;
- invalid syntax and conflicting effective-policy rollback;
- failed reload rollback, including restored validation and reload;
- rollback failure retaining its recovery artifact;
- rollback failure retaining its recovery artifact or printing no-prior-file removal;
- INT/TERM during candidate and backup preparation as well as after activation;
- actual-root rejection of `PANAMA_BOOT_FIXTURE_ROOT` in a user namespace;
- exact command ordering and no install handoff after a transactional failure.
## Verified bootstrap and installer inputs