fix_ssh_permissions never checked the home directory itself

Asked to look at user provisioning and .ssh dir creation — the recurring pain point
was "the .ssh permissions are never right even when I do everything correctly", and
tracing it: fix_ssh_permissions() only ever touched ~/.ssh and its contents. sshd's
StrictModes (on by default; nothing here turns it off) also refuses authorized_keys
if the HOME DIRECTORY is group- or world-writable — not just .ssh. An account
created by this script's own adduser call gets a safe 0755 home by default, but one
that already existed some other way (cloud-init, a prior useradd, LDAP, or simply an
account that was already there) might not, and the script never checked or reported
on it. From the user's side that looks exactly like the complaint: .ssh is 0700, the
key is present and correctly formatted, and login still fails, with nothing in the
script's own output pointing at why.

Fixed by extending fix_ssh_permissions() — called on every path that adds or
verifies a key — to check the home directory's mode first and clear only the
group/other write bits if set (go-w), reported with a warn rather than done
silently, since it's genuinely surprising when it fires. Deliberately not forced to
a fixed mode: 0755 vs 0750 is a real choice some setups make on purpose, and this
only removes the specific bits sshd refuses to see.

Verified with two simulations: a 775 home directory gets warned about and corrected
to 755 (.ssh and authorized_keys already-wrong modes fixed too, as before); a 750
home directory is left untouched with no warning. The stat call tries GNU's -c
first (this step only runs on Linux) with a BSD -f fallback, same pattern
officer-setup's layout.sh already uses for the same reason — not a live concern
here since MACOS_SKIP keeps this step off macOS entirely, but consistent rather
than a call that would silently do nothing if that ever changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 04:11:16 +01:00
co-authored by Claude Opus 5
parent 2e7fb84454
commit 8a3047b06b
+27 -4
View File
@@ -59,12 +59,35 @@ authorized_key_count() {
has_authorized_key() { (($(authorized_key_count) > 0)); }
# Everything about ~/.ssh that has to be true for sshd to use it at all. sshd
# ignores an authorized_keys file that is group- or world-writable, and does so
# silently from the client's point of view — the login just fails.
# Everything that has to be true for sshd to use ~/.ssh at all. sshd's StrictModes
# (on by default, and nothing here turns it off) checks the HOME DIRECTORY too, not
# just .ssh — this used to check only .ssh, which is the shape of bug that is hardest
# to notice: an account whose home came from somewhere other than THIS script's own
# adduser call (cloud-init, a prior useradd, LDAP, or simply an account that already
# existed) can have a group- or world-writable home directory, and sshd refuses
# authorized_keys under it silently. The .ssh directory and the key both look
# completely correct — because they are — and nothing here used to say why the
# login still failed.
fix_ssh_permissions() {
local dir
local dir home mode
dir="$(user_ssh_dir)"
home="$USER_HOME"
if [[ -d "$home" ]]; then
# GNU stat's -c, tried first since this step only ever runs on Linux (MACOS_SKIP)
# — with a BSD fallback anyway rather than a call that silently returns nothing on
# whatever this is next tested against, the same trap officer-setup's layout.sh
# file_owner() hit for the same reason.
mode="$(stat -c %a "$home" 2>/dev/null || stat -f %Lp "$home" 2>/dev/null)"
# 0022 is the group-write and other-write bits together. Cleared rather than
# forced to a fixed mode — 0755 vs 0750 is a real choice some setups make on
# purpose, and this only removes the bits sshd actually refuses to see.
if [[ -n "$mode" ]] && ((8#$mode & 0022)); then
chmod go-w "$home"
warn "${home} was group/world-writable (${mode}) — sshd's StrictModes refuses authorized_keys under a writable home regardless of .ssh's own permissions; cleared"
fi
fi
[[ -d "$dir" ]] || install -d -m 0700 -o "$USERNAME" -g "$(user_group)" "$dir"
chmod 700 "$dir"
[[ -f "$dir/authorized_keys" ]] && chmod 600 "$dir/authorized_keys"