diff --git a/modules/applications/openssh.nix b/modules/applications/openssh.nix index f45a857..53cc0dc 100644 --- a/modules/applications/openssh.nix +++ b/modules/applications/openssh.nix @@ -16,6 +16,7 @@ in PermitRootLogin = "no"; PasswordAuthentication = false; KbdInteractiveAuthentication = false; + AllowAgentForwarding = true; PubkeyAuthentication = "yes"; }; }; diff --git a/modules/applications/ssh/default.nix b/modules/applications/ssh/default.nix index 0a5c0fd..1b6bfa6 100644 --- a/modules/applications/ssh/default.nix +++ b/modules/applications/ssh/default.nix @@ -3,6 +3,7 @@ config, ... }: + let cfg = config.my.applications.ssh; in @@ -13,33 +14,71 @@ in ]; options.my.applications.ssh = { - enable = lib.mkEnableOption "OpenSSH client"; + enable = lib.mkEnableOption "OpenSSH client and agent configuration"; - defaultIdentityFile = lib.mkOption { + defaultIdentityFiles = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ + "~/.ssh/id_ed25519" + ]; + description = '' + Default local SSH identity files. + + These are used as the normal fallback identities when no agent key + is accepted, or when no forwarded agent is available. + ''; + }; + + fidoIdentityFile = lib.mkOption { type = lib.types.str; - default = "~/.ssh/id_ed25519"; - description = "Default SSH identity file"; + default = "~/.ssh/id_ed25519_sk_rk"; + description = '' + Local FIDO2 resident-key SSH identity handle. + + This file is only added to SSH identity candidates when a FIDO2 + device is actually visible. Do not use file existence to decide + whether this key is usable. + ''; + }; + + githubIdentityFiles = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Extra GitHub-specific SSH identity files. + + Leave this empty if GitHub should use the normal agent, FIDO key, + and default identity fallback order. + ''; }; addKeysToAgent = lib.mkOption { type = lib.types.str; default = "no"; - description = "Add keys to SSH agent"; + example = "1h"; + description = '' + Value for OpenSSH AddKeysToAgent. + + Recommended default is "no" for this setup, because FIDO resident-key + handle files should not be added to the agent accidentally. + ''; }; matchBlocks = lib.mkOption { - type = lib.types.attrs; + type = lib.types.attrsOf lib.types.anything; default = { }; - description = "SSH match blocks"; - }; + description = '' + Additional Home Manager OpenSSH settings blocks. - githubIdentityFiles = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ - "~/.ssh/id_ed25519_sk_rk" - "~/.ssh/id_ed25519" - ]; - description = "SSH identity files for GitHub (tried in order)"; + Use this for host-specific options such as ForwardAgent = true. + ''; + example = lib.literalExpression '' + { + "proxmox-* *.home.arpa *.internal" = { + ForwardAgent = true; + }; + } + ''; }; }; diff --git a/modules/applications/ssh/home.nix b/modules/applications/ssh/home.nix index 3b074ee..1981967 100644 --- a/modules/applications/ssh/home.nix +++ b/modules/applications/ssh/home.nix @@ -4,9 +4,40 @@ config, ... }: + let cfg = config.my.applications.ssh; hmCfg = config.my.applications.ssh.homeManager; + + hasFidoDevice = pkgs.writeShellScript "ssh-has-fido-device" '' + ${pkgs.libfido2}/bin/fido2-token -L 2>/dev/null \ + | ${pkgs.gnugrep}/bin/grep -q . + ''; + + userMatchBlockNames = lib.attrNames (cfg.matchBlocks or { }); + + userMatchBlocks = lib.mapAttrs ( + _name: value: lib.hm.dag.entryBefore [ "my-github" "my-default" ] value + ) (cfg.matchBlocks or { }); + + githubBlock = { + header = "Host github.com"; + + HostName = "github.com"; + User = "git"; + + IdentityAgent = "SSH_AUTH_SOCK"; + + IdentitiesOnly = false; + + ForwardAgent = false; + + AddKeysToAgent = cfg.addKeysToAgent; + } + // lib.optionalAttrs (cfg.githubIdentityFiles != [ ]) { + IdentityFile = cfg.githubIdentityFiles; + }; + in { options.my.applications.ssh.homeManager = { @@ -14,40 +45,61 @@ in }; config.home-manager.sharedModules = [ - { - config = lib.mkIf hmCfg.enable { - home.packages = [ - pkgs.openssh - ]; + ( + { lib, ... }: + { + config = lib.mkIf hmCfg.enable { - systemd.user.sockets.gcr-ssh-agent.Install.WantedBy = lib.mkForce [ ]; + programs.ssh = { + enable = true; + enableDefaultConfig = false; - services.ssh-agent.enable = true; + settings = userMatchBlocks // { + "my-local-fido-sk-rk" = + lib.hm.dag.entryBefore + ( + [ + "my-github" + "my-default" + ] + ++ userMatchBlockNames + ) + { + header = ''Match exec "${hasFidoDevice}"''; + IdentityFile = cfg.fidoIdentityFile; + }; - home.sessionVariables = { - SSH_AUTH_SOCK = "\${XDG_RUNTIME_DIR}/ssh-agent"; - }; + "my-github" = lib.hm.dag.entryBefore [ "my-default" ] githubBlock; - programs.ssh = { - enable = true; - enableDefaultConfig = false; + "my-default" = + lib.hm.dag.entryAfter + ( + [ + "my-local-fido-sk-rk" + "my-github" + ] + ++ userMatchBlockNames + ) + { + header = "Host *"; - settings = cfg.matchBlocks // { - "github.com" = { - IdentityFile = cfg.githubIdentityFiles; - AddKeysToAgent = cfg.addKeysToAgent; - }; + IdentityAgent = "SSH_AUTH_SOCK"; + IdentitiesOnly = false; - "*" = { - IdentityFile = cfg.defaultIdentityFile; - AddKeysToAgent = cfg.addKeysToAgent; - SetEnv = { - TERM = "xterm"; - }; + ForwardAgent = false; + + IdentityFile = cfg.defaultIdentityFiles; + + AddKeysToAgent = cfg.addKeysToAgent; + + SetEnv = { + TERM = "xterm"; + }; + }; }; }; }; - }; - } + } + ) ]; } diff --git a/modules/applications/ssh/system.nix b/modules/applications/ssh/system.nix index 9b34d19..bc1ecb5 100644 --- a/modules/applications/ssh/system.nix +++ b/modules/applications/ssh/system.nix @@ -15,9 +15,14 @@ in config = lib.mkIf cfg.enable { environment.systemPackages = with pkgs; [ openssh # OpenSSH client and server + libfido2 # FIDO2 support for SSH ]; - programs.ssh.startAgent = false; - services.gnome.gcr-ssh-agent.enable = false; + programs.ssh = { + startAgent = true; + agentTimeout = "24h"; + }; + programs.gnupg.agent.enableSSHSupport = lib.mkForce false; + services.gnome.gcr-ssh-agent.enable = lib.mkForce false; }; }