Автоматизация ssh-copy-id


30

У меня есть произвольное количество серверов с одинаковой комбинацией пользователь / пароль. Я хочу написать сценарий (который я называю один раз), чтобы

ssh-copy-id user@myserver

вызывается для каждого сервера. Поскольку все они имеют одного и того же пользователя / пароль, это должно быть легко, но ssh-copy-idя хочу, чтобы я каждый раз вводил пароль отдельно, что противоречит цели моего сценария. Там нет возможности для размещения в пароле, то есть ssh-copy-id -p mypassword user@myserver.

Как я могу написать скрипт, который автоматически заполняет поле пароля при ssh-copy-idзапросе?


почему вы используете идентификацию пользователя / пароля вместо идентификации пользователя / публичного ключа?
Кагали-сан

16
потому что я использую этот скрипт для настройки user / publickey.
Девин

Ответы:


28

Посмотрите на sshpass . Поместите ваш пароль в текстовый файл и сделайте что-то вроде этого:

$ sshpass -f password.txt ssh-copy-id user@yourserver

он не работает на Centos7, просто запустите без ошибок и без ключа на удаленном сервере
ImranRazaKhan

19

Вы можете использовать функцию «ожидать прослушивания пароля» и отправить свой пароль:

#!/usr/bin/expect -f
spawn ssh-copy-id $argv
expect "password:"
send "YOUR_PASSWORD\n"
expect eof

Сохраните скрипт, сделайте его исполняемым и назовите его так: ./login.expect user@myserver


Вам нужна более новая версия Bash для использования spawn? По причинам, которые я не могу контролировать, я застрял с Bash v3.2.
Девин

Bash версия не должна иметь значения. Я тестировал с ожидаемым 5.44.1.15, но я использовал подобное с более старыми версиями ожидаемого. У вас проблемы с использованием сценария?
MonkeeSage

spawn: command not found
Девин

spawnявляется ключевым словом ожидаемого (см. руководство ожидаем (1)). Похоже, что скрипт интерпретируется как оболочка, а не как ожидалось. Вы ожидаете установить? Что произойдет, если вы запустите, ожидайте напрямую:expect -f login.expect user@myserver
MonkeeSage

1
@ Envek Я только собирался добавить это, но приятно видеть, что последний комментарий - это прямой вопрос для того, что я собирался написать. Используйте эту строку вместо:spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Стивен Лу

4

Ответ Quanta довольно хороший, но он требует, чтобы вы поместили свой пароль в текстовый файл.

Из справочной страницы "sshpass":

Если опция не указана, sshpass считывает пароль из стандартного ввода.

Итак, что вы можете сделать, это захватить пароль один раз во время сценария, сохранить его в переменной, отобразить пароль и направить его в sshpass в качестве входных данных.

Я делаю это все время, и он отлично работает. Пример: echo "Please insert the password used for ssh login on remote machine:" read -r USERPASS for TARGETIP in $@; do echo "$USERPASS" | sshpass ssh-copy-id -f -i $KEYLOCATION "$USER"@"$TARGETIP" done


2

Это проблема с ssh-copy-id; он также добавляет ключ каждый раз, когда вы запускаете его. Если вы автоматизируете процесс, ваш файл author_keys быстро загромождается дубликатами ключей. Вот программа на Python, которая позволяет избежать обеих проблем. Он запускается с управляющего сервера и помещает ключи с одного удаленного сервера на другой удаленный сервер.

import subprocess
def Remote(cmd,IP):
    cmd = '''ssh root@%s '''%(IP)+cmd
    lines = subprocess.check_output(cmd.split())
    return '\n'.join(lines)
source = '123.456.78.90'
target = '239.234.654.123'
getkey = 'cat /root/.ssh/id_rsa.pub'
getauth = 'cat /root/.ssh/authorized_keys'
sourcekey = Remote(getkey, source).replace('\n','').strip()
authkeys = Remote(getauth, target).replace('\n','').strip()
if sourcekey not in authkeys: 
    keycmd=''' echo "%s" >>/root/.ssh/authorized_keys; 
    chmod 600 /root/.ssh/authorized_keys '''%(sourcekey) # A compound shell statement
    print 'Installed key', Remote(keycmd,target)
else: print 'Does not need key'

Мой ssh-copy-id уже делает это: ПРЕДУПРЕЖДЕНИЕ: Все ключи были пропущены, поскольку они уже существуют в удаленной системе. Это ваша попытка украсть ключи? :)
Михай

2

Вместо того, чтобы вводить пароль несколько раз, вы можете использовать psshего и его -Aпереключатель, чтобы запросить его один раз, а затем передать пароль всем серверам в списке.

ПРИМЕЧАНИЕ. Использование этого метода не позволяет использовать его ssh-copy-id, поэтому вам нужно будет применить собственный метод для добавления файла ключа публикации SSH в файл удаленной учетной записи ~/.ssh/authorized_keys.

пример

Вот пример, который делает работу:

$ cat ~/.ssh/my_id_rsa.pub                    \
    | pssh -h ips.txt -l remoteuser -A -I -i  \
    '                                         \
      umask 077;                              \
      mkdir -p ~/.ssh;                        \
      afile=~/.ssh/authorized_keys;           \
      cat - >> $afile;                        \
      sort -u $afile -o $afile                \
    '
Warning: do not enter your password if anyone else has superuser
privileges or access to your account.
Password:
[1] 23:03:58 [SUCCESS] 10.252.1.1
[2] 23:03:58 [SUCCESS] 10.252.1.2
[3] 23:03:58 [SUCCESS] 10.252.1.3
[4] 23:03:58 [SUCCESS] 10.252.1.10
[5] 23:03:58 [SUCCESS] 10.252.1.5
[6] 23:03:58 [SUCCESS] 10.252.1.6
[7] 23:03:58 [SUCCESS] 10.252.1.9
[8] 23:03:59 [SUCCESS] 10.252.1.8
[9] 23:03:59 [SUCCESS] 10.252.1.7

Приведенный выше скрипт обычно имеет такую ​​структуру:

$ cat <pubkey> | pssh -h <ip file> -l <remote user> -A -I -i '...cmds to add pubkey...'

psshДетали высокого уровня

  • cat <pubkey> выводит файл открытого ключа в pssh
  • psshиспользует -Iпереключатель для приема данных через STDIN
  • -l <remote user> учетная запись удаленного сервера (мы предполагаем, что у вас одинаковое имя пользователя на серверах в файле IP)
  • -Aговорит psshпопросить ваш пароль и затем повторно использовать его для всех серверов, к которым он подключается
  • -iприказывает psshотправлять любой вывод в STDOUT, а не сохранять его в файлах (поведение по умолчанию)
  • '...cmds to add pubkey...'- это самая сложная часть того, что происходит, поэтому я разобью это сам (см. ниже)

Команды, выполняемые на удаленных серверах

Это команды, которые psshбудут выполняться на каждом сервере:

'                                         \
  umask 077;                              \
  mkdir -p ~/.ssh;                        \
  afile=~/.ssh/authorized_keys;           \
  cat - >> $afile;                        \
  sort -u $afile -o $afile                \
'
Для того, чтобы:
  • установите umask удаленного пользователя на 077, так что любые каталоги или файлы, которые мы собираемся создать, будут иметь соответствующие права доступа следующим образом:

    $ ls -ld ~/.ssh ~/.ssh/authorized_keys
    drwx------ 2 remoteuser remoteuser 4096 May 21 22:58 /home/remoteuser/.ssh
    -rw------- 1 remoteuser remoteuser  771 May 21 23:03 /home/remoteuser/.ssh/authorized_keys
    
  • создать каталог ~/.sshи игнорировать предупреждение, если оно уже есть

  • установить переменную, $afileс путем к файлу author_keys
  • cat - >> $afile - принять входные данные из STDIN и добавить в файл author_keys
  • sort -u $afile -o $afile - уникально сортирует файл author_keys и сохраняет его

ПРИМЕЧАНИЕ. Этот последний бит предназначен для обработки случая, когда вы запускаете вышеуказанное несколько раз на одних и тех же серверах. Это исключит возможность добавления вашего pubkey несколько раз.

Обратите внимание на одиночные галочки!

Также обратите особое внимание на то, что все эти команды вложены в одинарные кавычки. Это важно, поскольку мы не хотим $afileполучать оценку до тех пор, пока она не будет выполнена на удаленном сервере.

'               \
   ..cmds...    \
'

Я расширил вышеописанное, чтобы его было легче читать, но я обычно запускаю все это в одну строку, например:

$ cat ~/.ssh/my_id_rsa.pub | pssh -h ips.txt -l remoteuser -A -I -i 'umask 077; mkdir -p ~/.ssh; afile=~/.ssh/authorized_keys; cat - >> $afile; sort -u $afile -o $afile'

Бонусный материал

С помощью psshвы можете отказаться от того , чтобы построить файлы и либо обеспечить динамическое содержимое , используя -h <(...some command...)или вы можете создать список IP - адреса с помощью других psshпереключателей «s, -H "ip1 ip2 ip3".

Например:

$ cat .... | pssh -h <(grep -A1 dp15 ~/.ssh/config | grep -vE -- '#|--') ...

Выше можно использовать для извлечения списка IP-адресов из моего ~/.ssh/configфайла. Конечно, вы также можете использовать printfдля создания динамического контента:

$ cat .... | pssh -h <(printf "%s\n" srv0{0..9}) ....

Например:

$ printf "%s\n" srv0{0..9}
srv00
srv01
srv02
srv03
srv04
srv05
srv06
srv07
srv08
srv09

Вы также можете использовать seqдля генерации последовательности чисел тоже!

Ссылки и аналогичные инструменты для pssh

Если вы не хотите использовать, psshкак я сделал выше, есть несколько других доступных вариантов.


Ansible's authorized_key_moduleКажется, не работа для новой машины. Сначала я должен использовать ssh-copy-id xxx, поэтому я ищу способ просто использовать ansible add ssh-key для новой машины, понимаете?
Мифрил

@mithril - звучит как ошибка, я бы спросил об этом на форумах Ansible.
СЛМ

1

Вам может подойти один из инструментов параллельного SSH (clusterssh, mssh, pssh).

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


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

Точно ... так что используйте этот инструмент для выполнения одной задачи, которая отсутствует. Хотя, если это будет происходить постоянно, сценарий, который выложил MonkeeSage (адаптированный для чтения пароля из stdin и работы на нескольких серверах), вероятно, будет лучшим выбором.
MikeyB

0

Пара вещей, которые могут соответствовать всем требованиям:

Как упоминалось в других ответах, sshpass, вероятно, самое простое решение.


0

Я хочу подчеркнуть, насколько плоха идея:

  1. Используйте жестко запрограммированный пароль в ваших скриптах
  2. Используйте один и тот же пароль на ВСЕХ ваших серверах ... как ... почему !?
  3. НЕ используйте SSH public_key + пароль, если вы настаиваете на этом
  4. Сохранить пароль в текстовый файл

Вот реализация, которая немного более безопасна ...

#!/usr/bin/python3
import os
import getpass
import argparse

parser = argparse.argument_parser()
parser.add_argument('-l','--login', action='store', help='username')
parser.add_argument('-p','--port', action='store', default='22', help='port')
parser.add_argument('-L','--list', action='store', help='file list of IPs')
parser.add_argument('-i','--ip-address', action='store', nargs='+', metavar='host' help='ip or list of ips')

args = parser.parse_args()
if not args.login:
    print("You need a login, broski!")
    return 0

if args.list:
    ips = [i for i in open(args.list, 'r').readlines()]
    passwd = getpass.getpass('Password: ')

    for ip in ips:
        cmd = 'ssh-id-copy {0}@{1} -p {2}'.format(ip,args.port,passwd)            
        os.system('sshpass -p ' + passwd + ' ' + cmd)
        print("Key added: ", ip)   # prints if successful
        # ex: sshpass -p passwd ssh-id-copy login@1.1.1.1

elif args.host:
    ip = args.host
    cmd = 'ssh-id-copy {0}@{1} -p {2}'.format(ip,args.port,passwd)
    os.system('sshpass -p ' + passwd + ' ' + cmd)
    print("Key added: ", ip)   # prints if successful
else:
    print("No IP addresses were given to run script...")
    return 0 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.