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


30

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

ssh-copy-id user@myserver

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

Як я можу написати сценарій, який автоматично заповнює поле пароля, коли його ssh-copy-idзапитують?


чому ви використовуєте ідентифікацію user / pass замість ідентифікації користувача / publickey?
kagali-san

16
тому що я використовую цей скрипт для налаштування користувача / publickey.
devin

Відповіді:


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.
devin

Версія Bash не має значення. Я тестував з очікуванням 5.44.1.15, але я використовував подібні з більш старими версіями очікування. У вас виникли проблеми з використанням сценарію?
MonkeeSage

spawn: command not found
devin

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

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

4

Відповідь кванту досить хороша, але вона вимагає, щоб ви ввели свій пароль у текстовий файл.

Зі сторінки чоловіка "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; він також додає ключ щоразу, коли ви запускаєте його. Якщо ви автоматизуєте процес, ваш файл авторизованих ключів швидко забивається дублюючими ключами. Ось програма 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 pub до файлу віддаленого облікового запису ~/.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із шляхом до файлу санкціонованих_ ключів
  • cat - >> $afile - взяти вхід зі STDIN та додати до файла дозволених ключів
  • sort -u $afile -o $afile - однозначно сортує файл санкціонованого_кейса та зберігає його

ПРИМІТКА. Останнім бітом є обробка випадку, коли ви запускаєте вищезазначені кілька разів на одних і тих же серверах. Це дозволить уникнути додавання вашого віскі кілька разів.

Помітьте одиночні кліщі!

Також зверніть особливу увагу на те, що всі ці команди вкладені всередині одиничних лапок. Це важливо, оскільки ми не хочемо $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 додати ssh-ключ для нової машини, будь-яка ідея?
Мітрил

@mithril - це схоже на помилку, я б запитав на форумах Ansible про це.
slm

1

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

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


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

Саме… тому використовуйте цей інструмент для виконання однієї задачі, якої не вистачає. Хоча якщо це буде продовжуватись, сценарій, розміщений 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.