Automatyzacja ssh-copy-id


30

Mam dowolną liczbę serwerów z tą samą kombinacją użytkownik / przepustka. Chcę napisać skrypt (który wywołuję raz), aby

ssh-copy-id user@myserver

jest wywoływany dla każdego serwera. Ponieważ wszyscy mają tego samego użytkownika / hasło, powinno to być łatwe, ale ssh-copy-idchcę, aby wpisywać hasło osobno za każdym razem, co jest sprzeczne z celem mojego skryptu. Nie ma opcji wprowadzenia hasła, tj ssh-copy-id -p mypassword user@myserver.

Jak napisać skrypt, który automatycznie wypełnia pole hasła, gdy ssh-copy-ido to poprosi?


dlaczego używasz identyfikacji użytkownika / hasła zamiast identyfikacji użytkownika / klucza publicznego?
kagali-san

16
ponieważ używam tego skryptu do konfigurowania użytkownika / klucza publicznego.
devin

Odpowiedzi:


28

Spójrz na sshpass . Umieść swoje hasło w pliku tekstowym i zrób coś takiego:

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

nie działa na Centos7 po prostu działa bez błędów i nie ma klucza na zdalnym serwerze
ImranRazaKhan

19

Możesz użyć funkcji oczekuj, aby nasłuchiwać monitu o hasło i wysłać hasło:

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

Zapisz skrypt, uczyń go wykonywalnym i wywołaj go w następujący sposób: ./login.expect user@myserver


Czy potrzebujesz nowszej wersji bash spawn? Z powodów, których nie mogę kontrolować, utknąłem w bash v3.2.
devin

Wersja Bash nie powinna mieć znaczenia. Testowałem z oczekiwaniem 5.44.1.15, ale użyłem podobnego rozwiązania ze starszymi wersjami expect. Czy masz problemy z używaniem skryptu?
MonkeeSage

spawn: command not found
devin

spawnjest słowem kluczowym expect (patrz instrukcja expect (1)). Wygląda na to, że skrypt jest interpretowany jako powłoka, a nie oczekiwany. Czy spodziewałeś się zainstalowanego? Co się stanie, jeśli biegniesz, oczekuj bezpośrednio:expect -f login.expect user@myserver
MonkeeSage

1
@Envek Właśnie zamierzałem to dodać, ale miło jest widzieć, że ostatni komentarz jest bezpośrednim pytaniem do rzeczy, którą zamierzałem napisać. Zamiast tego użyj tej linii:spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Steven Lu

4

Odpowiedź quanty jest całkiem dobra, ale wymaga wpisania hasła w pliku tekstowym.

Ze strony podręcznika „sshpass”:

Jeśli nie podano żadnej opcji, sshpass odczytuje hasło ze standardowego wejścia.

Więc, co możesz zrobić, to przechwycić hasło raz podczas skryptu, zapisać je w zmiennej, powtórzyć hasło i potokować, aby sshpass jako dane wejściowe.

Robię to cały czas i działa dobrze. Przykład: 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

Jest to problem z ssh-copy-id; dodaje również klucz przy każdym uruchomieniu. Jeśli automatyzujesz ten proces, twój plik autoryzowanych kluczy szybko zapełnia się powielonymi kluczami. Oto program w języku Python, który pozwala uniknąć obu problemów. Działa z serwera kontrolnego i umieszcza klucze z jednego zdalnego serwera na innym zdalnym serwerze.

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'

mój ssh-copy-id już to robi: OSTRZEŻENIE: Wszystkie klucze zostały pominięte, ponieważ już istnieją w systemie zdalnym. Czy to Twoja próba kradzieży kluczy? :)
Mihai Stanescu

2

Zamiast wpisywać hasła wielokrotnie można wykorzystać psshi jego -Aprzejście na zachęty dla niego raz, a następnie karmić hasło do wszystkich serwerów na liście.

UWAGA: Korzystanie z tej metody nie pozwala jednak na jej użycie ssh-copy-id, dlatego konieczne będzie wdrożenie własnej metody dołączania pliku klucza pub SSH do pliku konta zdalnego ~/.ssh/authorized_keys.

Przykład

Oto przykład, który wykonuje zadanie:

$ 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

Powyższy skrypt ma ogólnie taką strukturę:

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

psshSzczegóły na wysokim poziomie

  • cat <pubkey> wysyła plik klucza publicznego do pssh
  • psshużywa -Iprzełącznika do pobierania danych przez STDIN
  • -l <remote user> to konto zdalnego serwera (zakładamy, że masz taką samą nazwę użytkownika na serwerach w pliku IP)
  • -Akaże psshpoprosić o podanie hasła, a następnie użyć go ponownie dla wszystkich serwerów, z którymi się łączy
  • -ikaże psshwysyłać dane wyjściowe do STDOUT zamiast przechowywać je w plikach (zachowanie domyślne)
  • '...cmds to add pubkey...'- to jest najtrudniejsza część tego, co się dzieje, więc sama to rozwiążę (patrz poniżej)

Polecenia uruchamiane na zdalnych serwerach

Oto polecenia, które psshbędą uruchamiane na każdym serwerze:

'                                         \
  umask 077;                              \
  mkdir -p ~/.ssh;                        \
  afile=~/.ssh/authorized_keys;           \
  cat - >> $afile;                        \
  sort -u $afile -o $afile                \
'
W porządku:
  • ustaw umask użytkownika zdalnego na 077, dzięki temu wszelkie katalogi lub pliki, które będziemy tworzyć, będą miały odpowiednio ustawione uprawnienia:

    $ 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
    
  • utwórz katalog ~/.sshi zignoruj ​​ostrzeżenie, jeśli już tam jest

  • ustaw zmienną, $afileze ścieżką do pliku autoryzowanych_kluczy
  • cat - >> $afile - weź dane wejściowe ze STDIN i dołącz do pliku uprawnione klucze
  • sort -u $afile -o $afile - jednoznacznie sortuje plik uprawniony_klucz i zapisuje go

UWAGA: Ten ostatni bit dotyczy obsługi przypadku, w którym powyższe uruchamiane są wielokrotnie na tych samych serwerach. Pozwoli to wyeliminować wielokrotne dołączanie Twojego klucza pub.

Zwróć uwagę na pojedyncze tyknięcia!

Zwróć także szczególną uwagę na fakt, że wszystkie te polecenia są zagnieżdżone w pojedynczych cudzysłowach. To ważne, ponieważ nie chcemy $afilebyć poddawani ocenie, dopóki nie zostanie wykonana na zdalnym serwerze.

'               \
   ..cmds...    \
'

Rozszerzyłem powyższe, aby łatwiej było je tutaj przeczytać, ale generalnie uruchamiam to wszystko w jednym wierszu:

$ 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'

Materiał bonusowy

Dzięki zastosowaniu psshmożna zrezygnować z konieczności konstruowania pliki i albo zapewnienie dynamicznej zawartości przy użyciu -h <(...some command...)lub można utworzyć listę adresów IP, z wykorzystaniem innego pssh„s przełączników -H "ip1 ip2 ip3".

Na przykład:

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

Powyższe można wykorzystać do wyodrębnienia listy adresów IP z mojego ~/.ssh/configpliku. Oczywiście możesz także użyć printfdo generowania dynamicznej treści:

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

Na przykład:

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

Możesz także użyć seqdo generowania sformatowanych sekwencji liczb!

Referencje i podobne narzędzia do pssh

Jeśli nie chcesz używać psshtak jak ja to zrobiłem powyżej, dostępne są inne opcje.


Ansible's authorized_key_modulewydaje się nie działać na nowej maszynie. Najpierw muszę ssh-copy-id xxx, więc szukam sposobu, aby użyć klucza Answer Add Ssh dla nowej maszyny, jakiś pomysł?
Mithril

@mithril - brzmi jak błąd, pytam o tym na forach Ansible.
slm

1

Jedno z równoległych narzędzi SSH (clusterssh, mssh, pssh) może być dla Ciebie odpowiednie.

Na przykład użyj cssh, aby zalogować się na wszystkich komputerach i samodzielnie dodać klucz.


1
Mam już zestaw niestandardowych narzędzi do robienia wszystkiego, czego potrzebuję, z wyjątkiem kopiowania klucza.
devin

Dokładnie… więc użyj tego jednego narzędzia do wykonania jednego brakującego zadania. Chociaż jeśli będzie to trwało, skrypt, który MonkeeSage opublikował (przystosowany do odczytu hasła ze standardowego wejścia i pracy na wielu serwerach) prawdopodobnie byłby najlepszym wyborem.
MikeyB


0

Chcę podkreślić, jak kiepskim pomysłem jest:

  1. Używaj zakodowanego hasła w swoich skryptach
  2. Użyj tego samego hasła na WSZYSTKICH serwerach ... na przykład ... dlaczego !?
  3. NIE używaj SSH public_key + hasło uwierzytelniania, jeśli nalegasz na to
  4. Zapisz hasło w pliku tekstowym

Oto implementacja, która jest nieco bezpieczniejsza ...

#!/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 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.