Автоматизация 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.