I’ve been using KeePass Professional Edition for a few months now, and I’m always discovering new things to do with it. For example, I’ve got HQ photos of my driver’s license so that I can go to the gym without carrying my full wallet (if that’s illegal I totally don’t do that). I’ve got a couple of shared databases that sync off my main personal database that I can share with family and friends, which means I change update my accounts without the old hassle of texting everyone the new credentials. I keep all of my work identities in KeePass, both for personal and employer accounts (with different databases, of course).
Today I was messing around in my gaming environment (which, until I take the time to set up another SSD with a real OS, is also my dev environment) and finally got sick of re-entering my ssh
credentials on reloading bash
. I’ve begun taking my online identity a bit more seriously and I’m building a collection of keys for everything. It’s safe, but it’s insanely annoying to have to re-enter all those passphrases more than once, say, a month.
Background
Typically on Linux, ssh-agent
persists with the session. Also typically on Linux, bouncing a machine is pretty rare. I can’t remember the last time I logged out to log out, because I usually just lock the machine.
On Windows, however, closing all active bash
shells kills off the processes. Microsoft says this a feature, while the internet hates it. For someone like me running ssh-add
more than once in my .zpreztorc
, it’s horrible and I hate it.
This is where KeePass steps in…
- as an
ssh-agent
: KeeAgent is a fantastic crossplatform tool that functions as anssh-agent
capable of reading keys directly from your database. - as a typist: For situations where you aren’t able to foward the agent or aren’t starting from a configured instance of KeePass, Auto-Type has you covered.
By always forwarding your agent (e.g. ssh -A ...
), you can handle all of the keys via KeePass on your machine, instead of managing different keys on each machine.
Software
- KeePass 2 (
~2
)- Windows
- Mac OS X
cmd+f
“Contributed/Unofficial KeePass Packages” - Some Linux package managers
ctrl+f
“Contributed/Unofficial KeePass Packages” - Everything else
- KeeAgent
- (optional) KPEntryTemplates (makes it easier to manage templates)
Adding keys
The official docs for KeeAgent are awesome and have way more screenshots than I want to take. Basically, make your key like normal, use KeePass to generate the passphrase, and attach both the private and public files.
If, like me, you plan on building as many keys as you have passwords, you should uncheck the “Add key to agent when database is opened/unlocked” and instead manage it through KeeAgent. The agent works by forwarding as many keys as possible, so you can lock yourself out of a server if you load too many keys at once.
Running KeePass as an ssh-agent
The basics
Set up KeeAgent as the Agent and export a socket:
I use ssh-keeagent.sock
instead of ssh-agent.sock
to keep origin explicit.
You’ll need to add the socket to your .{whatever}rc
file:
export SSH_AUTH_SOCK=/tmp/user/ssh-keeagent.sock
Finally, add keys and test the connection with some server:
ssh -T [email protected]
systemd
(Note: I’m lazy and prefer to just run KeeAgent as the ssh-agent
, so I didn’t test this)
This article by Fabian Schmengler provides an excellent systemd
setup. I’m pulling out the steps just in case:
Add SSH_AGENT_SOCK
to your .{whatever}rc
file (source):
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"
Add a user service, ~/.config/systemd/user/ssh-agent.service
(source):
[Unit]
Description=SSH key agent
Wants=environment.target
Before=environment.target
IgnoreOnIsolate=true
[Service]
Type=forking
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
ExecStart=/usr/bin/ssh-agent -a $SSH_AUTH_SOCK
ExecStartPost=/bin/sh -c "/usr/bin/ps --no-headers -o pid -C ssh-agent | sed 's/^ /export SSH_AGENT_PID=/' > ~/ssh-agent.properties"
ExecStartPost=/bin/sh -c "echo export SSH_AUTH_SOCK=$SSH_AUTH_SOCK >> ~/ssh-agent.properties"
ExecStartPost=/usr/bin/systemctl --user set-environment SSH_AUTH_SOCK=${SSH_AUTH_SOCK}
ExecStopPost=/bin/rm ${SSH_AUTH_SOCK}
[Install]
WantedBy=default.target
This step is really smart. Like the author, I’ve had trouble with SSH_AGENT_PID
in the past. The service throws the generated config into a local file, removing the headache of exporting the variables.
Start and enable the service (source):
systemctl --user enable ~/.config/systemd/user/ssh-agent.service
systemctl --user start ssh-agent
Guard keepass
with ~/ssh-agent.properties
(source):
source ~/ssh-agent.properties && keepass
You’ll have to change the command wherever it’s being called (e.g. update the path in your menu editor)
Windows
mendhak wrote a wonderful article about connecting KeeAgent to Cygwin/MsysGit. However, with Windows 10, I’d like to integrate with WSL. At the moment, full integration doesn’t exist, but there are several solutions to get around it.
Using msys
/cygwin
You can read from a msysgit
(or possibly cygwin
) socket via socat
. Find or install socat
,
which socat || sudo apt-get -y install socat
then update your .{whatever}rc
file (source):
# If MSYSGIT socket in keeagent is set as c:\Users/foo/Documents/ssh_auth_msysgit
SSH_AUTH_KEEAGENT_SOCK=/mnt/c/Users/foo/Documents/ssh_auth_msysgit
SSH_AUTH_KEEAGENT_PORT=`sed -r 's/!<socket >([0-9]*\b).*/\1/' ${SSH_AUTH_KEEAGENT_SOCK}`
# use socket filename structure similar to ssh-agent
ssh_auth_tmpdir=`mktemp --tmpdir --directory keeagent-ssh.XXXXXXXXXX`
SSH_AUTH_SOCK="${ssh_auth_tmpdir}/agent.$$"
socat UNIX-LISTEN:${SSH_AUTH_SOCK},mode=0600,fork,shut-down TCP:127.0.0.1:${SSH_AUTH_KEEAGENT_PORT},connect-timeout=2 2>&1 > /dev/null &
I had to modify this a bit to get it to work with my prezto
setup. Placing it in my .zshrc
meant it was running in every new bash.exe
, so I ps | awk
to find the proper SSH_AUTH_SOCK
if it’s already running.
# Attempt to find the current process
SSH_AUTH_SOCK=$(ps -C socat -o command | awk '{ match($2, /\/tmp.*keeagent[^,]*/, a) }END{ print a[0] }')
# Check if variable is empty
if [[ -z $SSH_AUTH_SOCK ]]; then
# Clean all old tmp files
rm -rf /tmp/user/keeagent*
# If msys doesn't work, try cyg
SSH_AUTH_KEEAGENT_SOCK=/mnt/c/Users/path/to/msyslock
SSH_AUTH_KEEAGENT_PORT=`sed -r 's/!<socket >([0-9]*\b).*/\1/' ${SSH_AUTH_KEEAGENT_SOCK}`
# use socket filename structure similar to ssh-agent
ssh_auth_tmpdir=`mktemp --tmpdir --directory keeagent-ssh.XXXXXXXXXX`
SSH_AUTH_SOCK="${ssh_auth_tmpdir}/agent.$$"
socat UNIX-LISTEN:${SSH_AUTH_SOCK},mode=0600,fork,shut-down TCP:127.0.0.1:${SSH_AUTH_KEEAGENT_PORT},connect-timeout=2 2>&1 > /dev/null &
fi
export SSH_AUTH_SOCK=$SSH_AUTH_SOCK
Breakdown of the initial command:
ps -C socat -o command
-C socat
searches theCOMMAND
column forsocat
-o command
returns only theCOMMAND
column
awk '{ match($2, /\/tmp.*keeagent[^,]*/, a) }END{ print a[0] }'
:match($2, /\/tmp.*keeagent[^,]*/, a)
searches the second column (see the space betweensocat
andUNIX
?) for a/tmp
path containingkeeagent
and stores it ina
.print a[0]
sends off the matched string, if any.
Using python
With python
you can directly convert the socket (gist | fork with additional features) instead of using socat
. I haven’t tried this, but it seems pretty solid. I might eventually run it in a VM at work.
KeePass Auto-Type
If you, for whatever reason, aren’t forwarding/can’t forward your agent, KeePass is still insanely useful. I use for server-specific users whose config I don’t want to leave the server (if it doesn’t leave the server, I know it’s insecure when I see it in the wild). I make a template per user and reference things everywhere. Here’s a breakdown of a sample user:
I’m abusing URL
as hostname
, and the password is actually the key’s passphrase.
I include a string field for each of the CLI options (sure, specifying options and a config file is overkill, but you get the point).
ssh -i {S:SSH Key Path} -F {S:SSH Config File} -p {S:SSH Port} {USERNAME}@{URL}{ENTER}{DELAY 1000}{PASSWORD}{ENTER}
This is where the magic happens. I’ve beefed up the autotype to include all of the variables. I could actually use URL:Port
instead of a custom variable, but I like having a custom variable because I can create a default template with port 22 and not worry about always appending :22
to the URL
.