Ssh – Choose identity from ssh-agent by file name

sshssh-agentssh-keys

Problem: I have some 20-30 ssh-agent identities. Most servers refuse authentication with Too many failed authentications, as SSH usually won't let me try 20 different keys to log in.

At the moment, I am specifying the identity file for every host manually, using the IdentityFile and the IdentitiesOnly directive, so that SSH will only try one key file, which works.

Unfortunately, this stops working as soon as the original keys aren't available anymore. ssh-add -l shows me the correct paths for every key file, and they match with the paths in .ssh/config, but it doesn't work. Apparently, SSH selects the indentity by public key signature and not by file name, which means that the original files have to be available so that SSH can extract the public key.

There are two problems with this:

  • it stops working as soon as I unplug the flash drive holding the keys
  • it renders agent forwarding useless as the key files aren't available on the remote host

Of course, I could extract the public keys from my identity files and store them on my computer, and on every remote computer I usually log into. This doesn't looks like a desirable solution, though.

What I need is a possibility to select an identity from ssh-agent by file name, so that I can easily select the right key using .ssh/config or by passing -i /path/to/original/key, even on a remote host I SSH'd into. It would be even better if I could "nickname" the keys so that I don't even have to specify the full path.

Best Answer

Guess I'll have to answer my own question, as there doesn't seem to be any way to request an identity by file name.

I wrote a quick-and-dirty Python scripts which creates a public key file in .ssh/fingerprints for every key the agent holds. I can then specify this file, which contains no secret key, using IdentityFile and SSH will pick the right identity from the SSH agent. Works perfectly fine, and allows me to use the agent for as many private keys I wish.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Dumps all public keys held by ssh-agent and stores them in ~/.ssh/fingerprints/, so that
they can be identified using the IdentityFile directive.

"""

import sys, os
import stat
import re
import envoy

RE_MATCH_FILENAME = re.compile(r'([^\\/:*?"<>|\r\n]+)\.\w{2,}$', re.IGNORECASE)

if os.getuid() == 0:
    USERNAME = os.environ['SUDO_USER']
else:
    USERNAME = os.environ['USER']

def error(message):
    print "Error:", message
    sys.exit(1)

def main():
    keylist = envoy.run('ssh-add -L').std_out.strip('\n').split('\n')

    if len(keylist) < 1:
        error("SSH-Agent holds no indentities")

    for key in keylist:
        crypto, ckey, name = key.split(' ')
        filename = os.path.join(os.environ['HOME'], '.ssh/fingerprints',
                  RE_MATCH_FILENAME.search(name).group(1)+'.pub')

        with open(filename, 'w') as f:
            print "Writing %s ..." % filename
            f.write(key)

        envoy.run('chmod 600 %s' % filename)
        envoy.run('chown %s %s' % (USERNAME, filename))


if __name__ == '__main__':
    main()