Refactors SSH config management

- Consolidates SSH config into a unified file
- Replaces deprecated config files and updates file sources
- Ensures proper SSH key permission handling and mapping
This commit is contained in:
Chris Toph 2025-04-22 21:10:28 -04:00
parent ebf083522b
commit 8eba3b905c
7 changed files with 69 additions and 62 deletions

View file

@ -10,6 +10,7 @@
let let
username = config.hostSpec.username; username = config.hostSpec.username;
homeDir = config.hostSpec.home; homeDir = config.hostSpec.home;
shell = config.hostSpec.shell;
in in
{ {
imports = lib.flatten [ imports = lib.flatten [
@ -18,7 +19,7 @@ in
"modules/home" "modules/home"
]) ])
./asdf.nix ./asdf.nix
./bash.nix ./bash.nix # TODO: setup a nicer bash config... or zsh
./bat.nix ./bat.nix
./direnv.nix ./direnv.nix
./fastfetch ./fastfetch
@ -26,7 +27,7 @@ in
./git.nix ./git.nix
./ranger.nix ./ranger.nix
./screen.nix ./screen.nix
./ssh ./ssh.nix
./zoxide.nix ./zoxide.nix
]; ];
@ -45,7 +46,7 @@ in
EDITOR = "micro"; EDITOR = "micro";
FLAKE = "${homeDir}/git/Nix/dot.nix"; FLAKE = "${homeDir}/git/Nix/dot.nix";
MANPAGER = "batman"; # see ./cli/bat.nix MANPAGER = "batman"; # see ./cli/bat.nix
SHELL = "fish"; SHELL = shell;
TERM = "foot"; TERM = "foot";
VISUAL = "micro"; VISUAL = "micro";
}; };

View file

@ -0,0 +1,58 @@
{
pkgs,
config,
lib,
hostSpec,
secretsSpec,
...
}:
let
# Generate local key paths for the config
sshKeysMap = lib.mapAttrs (name: _: "${hostSpec.home}/.ssh/${name}") secretsSpec.ssh.privateKeys;
# Create the SSH config file with local paths
sshConfig = pkgs.writeText "ssh-config" ''
Host git.ryot.foo
IdentityFile ${sshKeysMap.git}
Host *
ForwardAgent no
AddKeysToAgent yes
Compression no
ServerAliveInterval 5
ServerAliveCountMax 3
HashKnownHosts no
UserKnownHostsFile ~/.ssh/known_hosts
ControlMaster no
ControlPath ~/.ssh/master-%r@%n:%p
ControlPersist no
IdentityFile ${sshKeysMap.pve}
UpdateHostKeys ask
'';
in
{
home.file =
{
# SSH config file
".ssh/config_source" = {
source = sshConfig;
onChange = ''
cp $HOME/.ssh/config_source $HOME/.ssh/config
chmod 400 $HOME/.ssh/config
'';
};
}
# Dynamically add all SSH private keys using the existing store paths
# Ensures the keys have correct permissions and are not symlinks
// lib.mapAttrs' (name: path: {
name = ".ssh/${name}_source";
value = {
source = path;
onChange = ''
cp $HOME/.ssh/${name}_source $HOME/.ssh/${name}
chmod 600 $HOME/.ssh/${name}
'';
};
}) secretsSpec.ssh.privateKeys;
}

View file

@ -1,17 +0,0 @@
Host git.ryot.foo
IdentityFile ~/git/.ssh/git
Host *
ForwardAgent no
AddKeysToAgent yes
Compression no
ServerAliveInterval 5
ServerAliveCountMax 3
HashKnownHosts no
UserKnownHostsFile ~/.ssh/known_hosts
ControlMaster no
ControlPath ~/.ssh/master-%r@%n:%p
ControlPersist no
IdentityFile ~/.ssh/pve
UpdateHostKeys ask

View file

@ -1,32 +0,0 @@
{
config,
inputs,
lib,
...
}:
{
# programs.ssh = {
# enable = true;
# # Avoids infinite hang if control socket connection interrupted. ex: vpn goes down/up
# serverAliveCountMax = 3;
# serverAliveInterval = 5;
# addKeysToAgent = "yes";
# extraConfig = ''
# IdentityFile ~/.ssh/pve
# UpdateHostKeys ask
# '';
# matchBlocks = {
# "git.ryot.foo" = {
# identityFile = "~/git/.ssh/git";
# };
# };
# };
home.file.".ssh/config" = {
source = ./config;
target = ".ssh/config_source";
onChange = ''cat .ssh/config_source > .ssh/config && chmod 400 .ssh/config'';
};
}

View file

@ -1,8 +1,3 @@
{
lib,
config,
...
}:
{ {
programs.ssh.startAgent = true; programs.ssh.startAgent = true;

View file

@ -2,6 +2,7 @@
let let
username = config.hostSpec.username; username = config.hostSpec.username;
homeDir = config.hostSpec.home; homeDir = config.hostSpec.home;
pve-key = config.secretsSpec.ssh.privateKeys.pve;
in in
{ {
# For less permission issues with SSHFS # For less permission issues with SSHFS
@ -23,7 +24,7 @@ in
"reconnect" "reconnect"
"_netdev" "_netdev"
"allow_other" "allow_other"
"identityfile=${homeDir}/.ssh/pve" "identityfile=${pve-key}"
]; ];
}; };

View file

@ -9,15 +9,16 @@ let
hostSpec = config.hostSpec; hostSpec = config.hostSpec;
username = hostSpec.username; username = hostSpec.username;
homeDir = hostSpec.home; homeDir = hostSpec.home;
pubKeys = lib.filesystem.listFilesRecursive ./keys; _shell = hostSpec.shell;
pubKeys = builtins.attrValues config.secretsSpec.ssh.publicKeys;
in in
{ {
users.users.${username} = { users.users.${username} = {
name = hostSpec.username; name = hostSpec.username;
shell = pkgs.fish; # default shell shell = _shell;
# These get placed into /etc/ssh/authorized_keys.d/<name> on nixos # These get placed into /etc/ssh/authorized_keys.d/<name> on nixos
openssh.authorizedKeys.keys = lib.lists.forEach pubKeys (key: builtins.readFile key); openssh.authorizedKeys.keys = pubKeys;
}; };
# Create ssh sockets directory for controlpaths when homemanager not loaded (i.e. isMinimal) # Create ssh sockets directory for controlpaths when homemanager not loaded (i.e. isMinimal)
@ -38,7 +39,7 @@ in
home-manager = { home-manager = {
extraSpecialArgs = { extraSpecialArgs = {
inherit pkgs inputs; inherit pkgs inputs;
hostSpec = config.hostSpec; inherit (config) secretsSpec hostSpec;
}; };
users.${username}.imports = lib.flatten ( users.${username}.imports = lib.flatten (
lib.optional (!hostSpec.isMinimal) [ lib.optional (!hostSpec.isMinimal) [