I recently migrated from Arch Linux to NixOS, but I have a problem since my migration I can’t use my Yubikey with ssh. I get an error when I do ssh-add <path of ssh key of type sk> :
Could not add identity “toto”: agent refused operation
Whereas I can use it when I do ssh-keygen to create a key of type sk.
I remember my team mates had an issue using Gnome, I can check with them if it’s still the case, just tell me.
Edit: if not already done you should also give ultimate trust to your key:
# import your GPG public key
gpg --import mykey.gpg
# if needed, find your key id
gpg -k --keyid-format long
# ultimate trust the key
gpg --edit-key 0xFFFFFFFFFFFFFFFF
> trust
> 5
> y
> save
# hopefully your key is there
ssh-add -L
I’ve tested your configuration, but it still doesn’t work, I get the same error
❯ echo $SSH_AUTH_SOCK
/run/user/1000/gnupg/S.gpg-agent.ssh
❯ ssh-add -L
ssh-rsa AA ... == cardno:20_694_419
❯ ssh-add toto
Enter passphrase for toto:
Could not add identity "toto": agent refused operation
For what I understand ssh-add serves to add a ssh private key, but currently the private key is on the yubikey and should not be on your host. If ssh-add -L lists your key you should be able to ssh into hosts using it, did I miss something?
I’ve been banging my head against an issue with yubikey pgp ssh auth as well but only for TTY based pinentry (pinentry-tty and pinentry-curses) and only failing on zsh.
Could it be that you are on a zsh login shell?
I have this setup which works for bash login shell but fails for zsh. I tested on a fresh user.
from my configuration.nix (I added some comments to illustrate):
# maybe not relevant to mention, used for greetd
users.users.greeter = {
isNormalUser = false;
group = "greeter";
shell = pkgs.bash; # could be an issue but more for security not tty-messup? switch to nologin?
};
# defensive but shouldn't hurt
environment.etc."ssh/ssh_config".text = ''
Match host * exec "gpg-connect-agent UPDATESTARTUPTTY /bye"
'';
users.users.myuser = {
isNormalUser = true;
shell = pkgs.zsh;
};
programs.zsh = {
enable = true;
ohMyZsh = {
enable = true;
plugins = [
"git"
"z"
"gpg-agent" # Just for refrence. Tested with and without doesn't change the outcome
];
theme = "gnzh";
};
};
programs.gnupg = {
agent = {
enable = true;
enableSSHSupport = true; # Enable SSH agent support in gpg-agent
enableExtraSocket = true; # Optional, disable extra sockets if not needed
enableBrowserSocket = true; # Optional, disable browser socket
pinentryPackage = pkgs.pinentry-curses; #works on bash, not on zsh
settings = {
default-cache-ttl = 300;
max-cache-ttl = 7200;
#log-level = "/tmp/gpg-agent.log";
#debug-level = "advanced";
};
};
};
environment.variables = {
# Set SSH_AUTH_SOCK to default gpg-agent SSH socket path
# This is usually ~/.gnupg/S.gpg-agent.ssh or /run/user/UID/gnupg/S.gpg-agent.ssh
# You can hardcode the path or infer via environment at runtime.
SSH_AUTH_SOCK = "${builtins.getEnv "XDG_RUNTIME_DIR"}/gnupg/S.gpg-agent.ssh";
};
environment.shellInit = ''
export GPG_TTY="$(tty)"
gpg-connect-agent updatestartuptty /bye
export SSH_AUTH_SOCK="/run/user/$UID/gnupg/S.gpg-agent.ssh"
'';
Again, it works if I change from pinentry-curses to e.g. pinentry-qt OR if I am using pkgs.bash for my user.
the error, which is hard to debug further, is gpg-agent’s call to pinentry-curses. It is as if it isn’t finding the tty but I couldn’t get that level of confirmation. I even went so far as to compile a custom c wrapper that hardcodes the -T argument and feeds it into pinentry-curses. To no avail.
That might explain it — thanks a lot for the detailed write-up!
If I can’t get it to work cleanly, I’ll just switch to a different pinentry (maybe pinentry-qt or pinentry-tty) as a workaround.
It is installed as a package and reacts correctly when you call it manually on your terminal?
Type “GETPIN” into stdin when it launches.
If that works but the gpg-agent still refuses then my wisdom ends here try gtk …
And just for curiosity sake, what happens if you switch your login shell to bash? If that “fixes” it (not suggesting you keep using bash of course) maybe we can edit the title of this help thread to include “with zsh” or such.
Interesting I got this working fine on my setup with KDE and zsh as the default shell a little while ago and didn’t have a huge amount of trouble with it.
If your are using gpg agent and pcscd you need to disable the normal ssh agent as the gpg agent takes over that functionality. They don’t get on so you kind of have to pick one or the other.
programs.ssh.startAgent = false;
gpg agent doesn’t, to my knowledge, work with the ed25519-sk style keys, you need the ordinary ssh agent for that to work. I used to use these and I swapped to gpg so I could use the gpg agent for signing git commits with my yubikey and changed my ssh auth over to gpg so it would all play nice.
Hmm If I drop to a TTY with my desktop session still going I still get the qt pin prompt in the desktop.
Looks like if I change the pinentry package i’ll need to restart for it to have an effect as if I change to ‘curses’ / ‘tty’ / or ‘all’ variants I still just get the qt prompt after a rebuild switch. I’ll report back at some point if any of the tty options work for me after a reboot.