How to set up a system-wide ssh-agent that would work on all terminals?

The question is basically this (How do I get ssh-agent to work in all terminals?) but for NixOS.

The only NixOS option I could find was security.pam.services.<name>.sshAgentAuth but this may not be it because it didn’t work (or I didn’t use it correctly; maybe re-boot needed?).

I tried to use keychain as well (with these instructions) but no luck; any new terminal would just ignore it. (Again, this still could be me.)

Is there a straightforward post on how to do this? Thanks in advance!


update-1: Also found /nixos/modules/programs/ssh.nix in Nixpkgs that seems to start ssh-agent as a service, but not sure what I’m looking at to be honest.


update-2: Just realized that this is a NixOS module to configure SSH, and the options attribute set provides the options that can be queried at NixOS options. (Not sure why this wasn’t obvious.)

The relevant option in my case would probably be programs.ssh.startAgent"

Description
Whether to start the OpenSSH agent when you log in. The OpenSSH agent remembers private keys for you so that you don’t have to type in passphrases every time you make an SSH connection. Use ssh-add to add a key to the agent.

Added it to /etc/nixos/configuration.nix, did nixos-rebuild switch, and it does not work but that’s probably because I haven’t logged in and out yet.

So what is security.pam.services.<name>.sshAgentAuth used for then?

3 Likes

I currently use systemd.user.sessionVariables.SSH_AUTH_SOCK = "/run/user/1000/keyring/ssh" with services.gnome.gnome-keyring.enable = true;. Otherwise, SSH_AUTH_SOCK wasn’t set in emacs.

I need to test if it works with sway on my laptop.

3 Likes

The relevant option in my case would probably be programs.ssh.startAgent "
[…]
Added it to /etc/nixos/configuration.nix, did nixos-rebuild switch, and it does not work but that’s probably because I haven’t logged in and out yet.

I also added the programs.ssh.startAgent property and logged out and in … and then rebooted … and it does nothing?

I’m aware it’s been a minute since this thread had any activity, but it still doesn’t seem to do anything. What are we missing here? :upside_down_face:

Is there not an easy way to run ssh agent?

  • Searching the wiki results in no actual hits.
  • The Arch Wiki makes it clear there should be a systemd user service, but I can’t find it in /nix or via tab autocomplete when I try to enable it naively. What’s really strange is that I definitely have found /nix/var/nix/gcroots/booted-system/etc/systemd/user/ssh-agent.service … but it’s not visible anywhere.

I don’t want a gnome-keyring solution, as the ssh-agent should “just work” in server mode or lighter DE settings.

I’ll post back if I fix it, but in the meanwhile, I’d be happy to hear someone else’s thoughts or results on this.

3 Likes

So what is security.pam.services.<name>.sshAgentAuth used for then?

That is for the ssh server, ie if you set that to true on system A then you can use ssh-agent auth to log in to system A from system B (assuming you have agent auth set up on B).

2 Likes

Today I discovered keychain, seems to solve this problem nicely… if configured correctly :wink:

I added keychain to my environment.systemPackages then added the following line to programs.bash.initExtra:

if command -v keychain > /dev/null 2>&1; then eval $(keychain --eval --nogui ${keyFilename} --quiet); fi

replace keyFilename with the filename of the ssh key you want to load. Or omit that arg if you don’t want to load a key right off the bat. When you login after rebooting, the first time you open a terminal you’ll be prompted for your password (via a GUI pop-up by default if --nogui is not set). Then, you enter it and you’re ready to ssh in this and any other terminal windows you open. Seems to support other shells, haven’t tested that out yet. You could start out without the --quiet flag at first, it prints a pretty little summary of what it’s doing at the top of new terminal sessions by default, might help in debugging.

1 Like

That seems lightweight!

Is it included in the NixOS options at all or do you have to write your own (albeit very small!) glue code?

I’m ideally looking for an ssh-agent setting that can go into configuration.nix without adjunct glue-code. I was searching for where this would be implemented already, in any ssh agent or keyring handler, but I still don’t spot it yet. :dotted_line_face:

2 Likes

There is, although for some reason the service file in NixOS doesn’t use the .socket extension for ssh-agent, so you have to leave that part off of the environment variable you set as well.

There is a “FIXME” comment in the code here, which I am guessing is related to that but I may not be understanding the comment correctly:

# FIXME: this should really be socket-activated for über-awesomeness.

To get the service file, I added this to my Nix config:

  programs.ssh.startAgent = true;

Then, you need to set the environment variable somewhere. The ArchWiki article mentions you need to set the environment variable SSH_AUTH_SOCK to $XDG_RUNTIME_DIR/ssh-agent.socket, however like I mentioned the service file in NixOS has ssh-agent without the .socket part. See here:

        serviceConfig =
          { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
            ExecStart =
                "${cfg.package}/bin/ssh-agent " +
                optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") +
                optionalString (cfg.agentPKCS11Whitelist != null) ("-P ${cfg.agentPKCS11Whitelist} ") +
                "-a %t/ssh-agent";

I tried to make a drop-in for the service file to use .socket instead, but I couldn’t get it working so I think there must be more to it than that. :thinking: I’m afraid it’s a little beyond my understanding at the moment.

The way I was able to get it working was to leave off the .socket part in the environment variable as well.

SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/ssh-agent

To make Git and other SSH clients store keys in the agent on first use, add to ~/.ssh/config:

  AddKeysToAgent yes

Instead of “yes” you can put “confirm” or “ask”, if you would rather have one of those behaviors.

Start and enable the service:

systemctl --user enable --now ssh-agent.service

That’s it! You only have to input the key once for each login session, which is handy if you need to do a lot of commits.

2 Likes

This worked for me, and I didn’t set any environment variable and didn’t manually enable the service. Although it required a reboot.

I wonder if this could be set using the option:

programs.ssh.extraConfig

If anyone knows this please share.

Following works for me with the caveat that I use hardware tokens to house the SSH keys. Everything is defined in home manager but it should be portable to NixOS modules:

{ config, pkgs, ... }:
{
  programs.gpg = {
    enable = true;
    homedir = "${config.xdg.dataHome}/gnupg"; # Move gnupg home to clean up ~
    publicKeys = map (token: { source = ./gpg-keys + "/${token}-pub.asc"; trust = 5; }; 
    # separate bit of code symlinks them to ~/.ssh/; servers get users.users.root.openssh_authorizedKeys.keys with contents from the ssh pubkey
  };
  services.gpg-agent = {
    enable = true;
    enableSshSupport = true;
    extraConfig = ''
      pinentry-program ${pkgs.pinentry-qt}/bin/pinentry-qt
    '';
  };
}

and relevant bit from programs.zsh config:

programs.zsh.initExtra = (
      if config.services.gpg-agent.enable then
        ''
          if [[ -z "$SSH_AUTH_SOCK" ]]; then
            export SSH_AUTH_SOCK="$(${config.programs.gpg.package}/bin/gpgconf --list-dirs agent-ssh-socket)"
          fi
        ''
      else
        ""
    );

Had a bit more luck this time.

My missing systemd service was from a bad misconfiguration where I tried to insert my own ssh-agent.service in ~/.config/systemd and so systemd just completely deletes any mention or idea of that service if there’s any error loading it. Not even close to optimal fault-mode behavior on systemd’s behalf, but let’s move on. So that’s back.

And this was my actual problem at the root:

In my perfect world, it should definitely be “fixed” upstream to match the config I use for all of my other distros. I wonder why it is missing that, if it has historical reasons or if it’s just a simple omission.

Thanks for the tips on that, BluishHumility!

1 Like

I’ve got it working using programs.ssh.startAgent and setting and environment variable in user’s environment.d so that the systemd environment.d generator would pick that up.

So, my need is to have keepassxc to manage my ssh keys when using hyprland: I want all the programs to pick that up, not just shells.

So, basically, programs.ssh.startAgent creates a systemd unit in $HOME/.config/systemd/user/ssh-agent.service, which will export $SSH_AUTH_SOCK, but I found that to be available only in shells: when I start keepassxc I will get a warning saying that agent is not active (but it is).

To fix this, I just created $HOME/.config/environment.d/ssh-agent.conf with SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/ssh-agent and everything seems to be smooth so far.

Hope it helps!

1 Like