nixos/home-manager: materialize ~/.ssh/config as user-owned copy

/nix/store is owned by nobody:nogroup on this host, so the
home-manager-generated ~/.ssh/config symlink resolves to a nobody-owned
store file and OpenSSH rejects it with "Bad owner or permissions",
breaking ssh and git-over-ssh. Add a post-writeBoundary activation step
that replaces the symlink with a real 0600 copy owned by the user.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 04:59:35 -07:00
parent 1da7188781
commit 85118f187e

View File

@@ -328,6 +328,24 @@ in {
home.file.".ssh/config".force = true;
# OpenSSH only reads ~/.ssh/config when it is owned by root or the invoking
# user. On this host /nix/store is owned by nobody:nogroup, so the
# home-manager-generated symlink resolves to a nobody-owned store file and
# OpenSSH bails with "Bad owner or permissions on ~/.ssh/config", breaking
# ssh and git-over-ssh. After writeBoundary the symlink has been linked into
# place; replace it with a real 0600 copy owned by the user so the ownership
# check passes. home.file."...".force re-links the symlink on each switch,
# then this re-materializes it.
home.activation.materializeSshConfig = lib.hm.dag.entryAfter ["writeBoundary"] ''
ssh_config="$HOME/.ssh/config"
if [ -L "$ssh_config" ]; then
resolved="$(readlink -f "$ssh_config")"
tmp="$ssh_config.hm-real"
install -m600 "$resolved" "$tmp"
mv -f "$tmp" "$ssh_config"
fi
'';
services.gpg-agent = {
enable = true;
defaultCacheTtl = 8 * 60 * 60;