Recently, I switched to KeePassXC for managing my passwords and SSH keys. And of course, I wanted to use the ssh-agent integration with ssh-askpass offered by KeepassXC.
I'm using a Mac for work. I installed ssh-askpass from MacPorts and thought I was done.
Alas, Apple recently prevented system agents (like ssh-agent) from accepting additional
environment variables (in this case: SSH_ASKPASS), probably for security reasons.
This means, that the login item provided by the ssh-askpass package does not work any more
(see theseal/ssh-askpass#54).
Two workarounds are being offered in this issue. The first one
(theseal/ssh-askpass#54 (comment)) involves installing
the XQuartz package, just to get the DISPLAY variable set and then relies implicitly on a
default location, where the system ssh-agent looks for ssh-askpass in the absence of an
SSH_ASKPASS variable. This, to me, seems unwieldy and kind of fragile. From a helpful
comment, I learned, that this is the intended way it should work (and does indeed on Linux
and BSD).
The second approach (theseal/ssh-askpass#54 (comment); theseal/ssh-askpass#54 (comment)) works by masking the system ssh-agent .plist with a user copy and also masking
the system ssh-agent socket with a symbolic link to the socket from the user copy. This approach
appeals to me because it's simple, non-invasive to the system (I was reminded in a comment,
that replacing the system agent in itself is a potential security issue, which is correct, of
course) and trivially reversible. All .plist and script files are located in the users
~/Library/LaunchAgents directory, so no system directories need to be touched.
The com.openssh.ssh-agent-overlay.plist goes to ~/Library/LaunchAgents. It's based on a copy of
the system ssh_agent .plist, as suggested in this comment:
theseal/ssh-askpass#54 (comment).
The executed program has been replaced with the com.openssh.ssh-agent-overlay.sh script, which I put
into ~/Library/LaunchAgents/bin (remember to make it executable with chmod +x). Please adjust the
path in the .plist file accoding to your system.
This service sets up a parallel infrastructure to the system ssh-agent. It requests another socket
from the system and starts our ssh-agent listening to this socket... but only if there is an interaction
with this socket! Which nobody knows about!
The second component to make it work is the symbolic link. Copy the com.openssh.ssh-agent-overlay-link.plist
to ~/Library/LaunchAgents and the com.openssh.ssh-agent-overlay-link.sh to ~/Library/LaunchAgents/bin
(or wherever you like). Again, adjust the program path in the .plist file and make the script file executable.
The .plist and script files work in tandem to run at user login and wait for the other service to provide
the alternative socket (when available, this will inject the SSH_AUTH_SOCK_OVERLAY variable into the
whole launchd user domain). As soon as the alternative socket is available, the symbolic link masking
the system socket and pointing to the alternative socket is created. This works, because the system
ssh-agent, though being provided by the system, is started per user in the user domain. So the user
has write access to the socket. Also, the script kills any previous ssh-agent processes (which could
happen, if another login item had already accessed the system ssh-agent's socket).
Now, any interaction by any program with the system provided socket in the SSH_AUTH_SOCK variable is
forwarded to our alternative socket. The first interaction with it will execute the
com.openssh.ssh-agent-overlay.sh script starting our instance of ssh-agent.
And this user provided login agent DOES pick up any other environment variables injected into the
launchd user domain (before it's started, of course). That means, the ssh-askpass login item
does work with it, too.
Don't forget to reboot after installing all the files and adjusting the paths in the .plist files!
If everything went well, after a reboot, you should see this:
$ launchctl list | grep ssh
- 0 com.openssh.ssh-agent-overlay-link
- 0 com.openssh.ssh-agent
- 0 com.openssh.ssh-agent-overlay
$ launchctl print gui/$UID/com.openssh.ssh-agent-overlay
gui/503/com.openssh.ssh-agent-overlay = {
...
inherited environment = {
SSH_AUTH_SOCK_OVERLAY => /private/tmp/com.apple.launchd.<random chars>/Listeners
SSH_AUTH_SOCK => /private/tmp/com.apple.launchd.<different random chars>/Listeners
}
...
}
$ readlink -f $SSH_AUTH_SOCK
/private/tmp/com.apple.launchd.<random chars>/Listeners // == $SSH_AUTH_SOCK_OVERLAY
$ ssh-add -l
The agent has no identities.
$ launchctl list | grep ssh
- 0 com.openssh.ssh-agent-overlay-link
- 0 com.openssh.ssh-agent
<random PID> 0 com.openssh.ssh-agent-overlayUninstalling this workaround, if it becomes unnecessary or breaks something else (that I'm not aware of),
is trivial. The first and easiest thing to do is deactivating the two login items via the system settings.
If you want to purge them from your system, delete the two .plist files from ~/Library/LaunchAgents and
the two script files from wherever you placed them.
Also, don't forget to reboot to revert to the system ssh_agent.
Thanks for the clarification! I'll update the gist's description accordingly.