Выберите личность из 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'd в. Было бы еще лучше, если бы я мог "прозвать" ключи, чтобы мне даже не пришлось указывать полный путь.

5 ответов

Решение

Думаю, мне придется ответить на мой собственный вопрос, поскольку, похоже, нет способа запросить личность по имени файла.

Я написал быстрые и грязные скрипты Python, которые создают файл открытого ключа в .ssh/fingerprints за каждый ключ, который держит агент. Затем я могу указать этот файл, который не содержит секретного ключа, используя IdentityFile и SSH выберет правильную личность у агента 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()

Бегать

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

на удаленной машине, чтобы автоматически генерировать все файлы открытых ключей (при условии, что открытые ключи в вашем .ssh/config названы privateKeyFileName.pub и никакие нерешительные пути не участвуют). Вызов chown $USER .ssh/* для тебя sudo кейс.

Выбирая из принятого решения и предполагая, что вы просто хотите повторно использовать идентификационные данные, используемые для получения доступа к исходному серверу, тогда что-то вроде:

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

достаточно.

Один из вариантов — создать небольшой скрипт-оболочку для SSH, который сможет пересылать правильный открытый ключ от агента SSH. Вот рабочее доказательство концепции:

      #!/usr/bin/env bash

# SSH with specific identity that has been previously added to the ssh-agent.
# Public key file need not exist.
# Usage: First argument is the ssh key fingerprint of the identity you want to
#     use. All further arguments will be forwarded to ssh directly.
#     
# Ex: ssh-with-key SHA256:VrzNKaV7VcU7jT7hLZJMxxAo6whPc+VVXKaH0exqXiM user@host

set -euo pipefail

fingerprint="$1"
shift

# List ssh-agent fingerprints and public keys, get public key that corresponds
# to the line number of the fingerprint.
pub_key="$(awk 'line == null && "'"$fingerprint"'" {line=FNR; nextfile}; line == FNR' <(ssh-add -l) <(ssh-add -L))"

# Save public key to temporary file.
# NB: Can't use process substitution here. Bash passes substituted argument as
# a /dev/fd file, and ssh closes all /dev/fd files other than stdin, stdout,
# and stderr before processing CLI options.
pub_key_file=$(mktemp)
echo $pub_key > $pub_key_file

cleanup() {
    rm -f $pub_key_file
}
trap cleanup EXIT

ssh -o IdentitiesOnly=yes -i $pub_key_file "$@"

Вы можете создать отдельный файл конфигурации, чтобы связать отпечатки ключей SSH с одним или несколькими именами хостов или псевдонимами для простоты использования.

Преимущество этого метода заключается в том, что вам не нужно выполнять какую-либо предварительную или периодическую обработку открытых ключей, кроме добавления их в агент SSH. Файлы открытых ключей записываются по мере необходимости и удаляются после завершения сеанса.

версия кода 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()
Другие вопросы по тегам