根据文件名从 ssh-agent 中选择身份

根据文件名从 ssh-agent 中选择身份

问题:我有 20-30 个ssh-agent身份。大多数服务器拒绝使用 进行身份验证Too many failed authentications,因为 SSH 通常不允许我尝试 20 个不同的密钥来登录。

目前,我正在使用IdentityFile和指令手动为每个主机指定身份文件IdentitiesOnly,以便 SSH 只会尝试一个有效的密钥文件。

不幸的是,一旦原始密钥不再可用,它就会停止工作。ssh-add -l向我显示了每个密钥文件的正确路径,它们与中的路径匹配.ssh/config,但它不起作用。显然,SSH 通过公钥签名而不是文件名来选择身份,这意味着原始文件必须可用,以便 SSH 可以提取公钥。

这有两个问题:

  • 一旦我拔掉装有钥匙的闪存盘,它就会停止工作
  • 由于密钥文件在远程主机上不可用,因此代理转发变得毫无用处

当然,我可以从我的身份文件中提取公钥,并将其存储在我的电脑上,以及我经常登录的每台远程电脑上。但这看起来不是一个理想的解决方案。

我需要的是能够通过文件名从 ssh-agent 中选择身份,这样我就可以通过.ssh/config或轻松选择正确的密钥-i /path/to/original/key,即使在我通过 SSH 进入的远程主机上也是如此。如果我可以给密钥起“昵称”,那就更好了,这样我甚至不必指定完整路径。

答案1

我想我必须回答我自己的问题,因为似乎没有任何方法可以通过文件名请求身份。

我编写了一个快速而简单的 Python 脚本,它.ssh/fingerprints为代理持有的每个密钥创建一个公钥文件。然后我可以指定这个不包含密钥的文件,使用IdentityFileSSH 将从 SSH 代理中选择正确的身份。运行良好,并允许我将代理用于任意数量的私钥。

#!/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()

答案2

跑步

ssh-add -L | gawk ' { print $2 > $3 ".pub" } '

在远程计算机上自动生成所有公钥文件(假设您的公钥.ssh/config已命名privateKeyFileName.pub且不涉及不一致的路径)。致电咨询chown $USER .ssh/*您的sudo案例。

答案3

从已接受的解决方案中挑选,并假设您只想重用用于访问初始服务器的身份,然后类似:

Host github.com
    IdentitiesOnly yes
    IdentityFile ~/.ssh/authorized_keys

就足够了。

答案4

python3版本的代码在leoluks优秀回答中:

#!/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 subprocess

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 = subprocess.check_output(['ssh-add','-L']).decode().strip('\n').split('\n')

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

    for key in keylist:
        crypto, ckey, name = key.split(' ')
        print('name', name)
        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)

        subprocess.call(['chmod', '600', filename])
        subprocess.call(['chown',USERNAME, filename])


if __name__ == '__main__':
    main()

相关内容