add self-healing root SSH key systemd units
Headless Pis lose /root/.ssh/authorized_keys to imaging, first-boot tooling, or config management, which locks out the only SSH account that can fix it. - ensure-root-ssh-key: idempotent POSIX sh installer. Appends managed public keys from /usr/local/share/root-ssh-keys/authorized_keys, never removes or rewrites foreign keys, skips lines whose first field is not a known key algorithm, and enforces 0700 / 0600 root:root on the key dir and file. - ensure-root-ssh-key.service: oneshot, ordered Before=ssh.service so the key lands before the daemon starts; ConditionPathExists skips it when the managed keys file is absent. - ensure-root-ssh-key.path: PathChanged= + PathModified= on authorized_keys so deletes, renames, and in-place edits are healed at runtime, not just at boot. Writes only when a key is missing, so the unit settles after one no-op run. - authorized_keys.example: template with commented placeholder keys only; the real file stays on the host. - root-ssh-key.md: runbook with deploy, verification, revocation steps, and the gotcha that systemd has no PathMissing= key. LLM-Generated: true
This commit is contained in:
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
# ensure-root-ssh-key - idempotently install managed SSH public keys into
|
||||
# /root/.ssh/authorized_keys. Runs at boot (ensure-root-ssh-key.service) and
|
||||
# can be run by hand at any time.
|
||||
#
|
||||
# Source of truth: /usr/local/share/root-ssh-keys/authorized_keys
|
||||
# One key per line, same format as an authorized_keys file. Blank lines and
|
||||
# lines starting with '#' are ignored. Keys already present are left alone;
|
||||
# foreign keys in authorized_keys are never removed.
|
||||
|
||||
set -eu
|
||||
|
||||
KEY_DIR="${KEY_DIR:-/root/.ssh}"
|
||||
AUTH_KEYS="$KEY_DIR/authorized_keys"
|
||||
MANAGED_KEYS="${MANAGED_KEYS:-/usr/local/share/root-ssh-keys/authorized_keys}"
|
||||
|
||||
if [ ! -r "$MANAGED_KEYS" ]; then
|
||||
echo "ensure-root-ssh-key: cannot read $MANAGED_KEYS" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
umask 077
|
||||
install -d -m 0700 "$KEY_DIR"
|
||||
[ -e "$AUTH_KEYS" ] || : > "$AUTH_KEYS"
|
||||
chmod 0600 "$AUTH_KEYS"
|
||||
chown root:root "$KEY_DIR" "$AUTH_KEYS"
|
||||
|
||||
added=0
|
||||
checked=0
|
||||
while IFS= read -r line <&3; do
|
||||
case "$line" in
|
||||
'' | \#*) continue ;;
|
||||
esac
|
||||
|
||||
# Basic shape check: <algo> <base64> [comment]
|
||||
algo=${line%% *}
|
||||
case "$algo" in
|
||||
ssh-ed25519 | ssh-rsa | ssh-dss | ecdsa-sha2-* | sk-ecdsa-sha2-* | sk-ssh-ed25519-* | ssh-ed448) ;;
|
||||
*)
|
||||
echo "ensure-root-ssh-key: skipping unrecognised line: $line" >&2
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
checked=$((checked + 1))
|
||||
if grep -qxF -- "$line" "$AUTH_KEYS" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
printf '%s\n' "$line" >> "$AUTH_KEYS"
|
||||
added=$((added + 1))
|
||||
echo "ensure-root-ssh-key: installed key (${algo}, ${#line} chars)"
|
||||
done 3< "$MANAGED_KEYS"
|
||||
|
||||
if [ "$added" -eq 0 ]; then
|
||||
echo "ensure-root-ssh-key: $checked managed key(s) already present in $AUTH_KEYS"
|
||||
else
|
||||
echo "ensure-root-ssh-key: added $added key(s), $checked managed key(s) total"
|
||||
fi
|
||||
|
||||
chmod 0600 "$AUTH_KEYS"
|
||||
exit 0
|
||||
Reference in New Issue
Block a user