Automatizando ssh-copy-id

30

Eu tenho um número arbitrário de servidores com a mesma combinação de usuário / passe. Quero escrever um script (que chamo uma vez) para que

ssh-copy-id user@myserver

é chamado para cada servidor. Como todos eles têm o mesmo usuário / senha, isso deve ser fácil, mas ssh-copy-idquer que eu digite a senha separadamente cada vez, o que anula o objetivo do meu script. Não há opção para inserir uma senha, ou seja ssh-copy-id -p mypassword user@myserver.

Como posso escrever um script que preenche automaticamente o campo de senha quando ssh-copy-idsolicitado?

devin
fonte
por que você usa a identificação de usuário / senha em vez da identificação de usuário / chave pública?
Kagali-san
16
porque estou usando esse script para configurar o usuário / chave pública.
devin

Respostas:

28

Dê uma olhada no sshpass . Coloque sua senha em um arquivo de texto e faça algo assim:

$ sshpass -f password.txt ssh-copy-id user@yourserver
quanta
fonte
não está funcionando no Centos7, apenas é executado sem erros e sem chave no servidor remoto
ImranRazaKhan
19

Você pode usar o esperar para ouvir o prompt de senha e enviar sua senha:

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

Salve o script, torne-o executável e chame-o como: ./login.expect user@myserver

MonkeeSage
fonte
Você precisa de uma versão mais recente do bash para usar spawn? Por razões que não consigo controlar, estou preso ao bash v3.2.
devin
A versão do Bash não deve importar. Testei com o expect 5.44.1.15, mas usei similar em versões mais antigas do expect. Você está tendo problemas para usar o script?
MonkeeSage
spawn: command not found
Devin
spawné uma palavra-chave expect (consulte o manual expect (1)). Parece que o script está sendo interpretado como shell, e não como esperado. Você espera instalado? O que acontece se você executar esperar diretamente:expect -f login.expect user@myserver
MonkeeSage
1
@ Envek Eu estava indo apenas adicionar isso, mas é bom ver que o último comentário é uma pergunta direta para a coisa que eu ia escrever. Use esta linha:spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Steven Lu
4

A resposta do quanta é muito boa, mas requer que você coloque sua senha em um arquivo de texto.

Na página do manual "sshpass":

Se nenhuma opção for dada, o sshpass lê a senha da entrada padrão.

Portanto, o que você pode fazer é capturar a senha uma vez durante o script, armazená-la em uma variável, repetir a senha e canalizar para sshpass como entrada.

Eu faço isso o tempo todo e funciona bem. Exemplo: 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

Eric esotérico
fonte
2

Este é um problema com o ssh-copy-id; Ele também adiciona uma chave toda vez que você a executa. Se você estiver automatizando o processo, seu arquivo allowed_keys ficará rapidamente cheio de chaves duplicadas. Aqui está um programa Python que evita os dois problemas. Ele é executado no servidor de controle e coloca as chaves de um servidor remoto em outro servidor remoto.

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'
mfs
fonte
minha ssh-copy-id já faz isso: AVISO: Todas as chaves foram ignoradas porque já existem no sistema remoto. Esta é sua tentativa de roubar chaves? :)
Mihai Stanescu
2

Em vez de digitar sua senha várias vezes, você pode usar psshe sua -Aopção para solicitá-la uma vez e depois alimentar a senha para todos os servidores em uma lista.

OBSERVAÇÃO:ssh-copy-id no entanto, o uso desse método não permite o uso , portanto, será necessário rolar seu próprio método para anexar o arquivo de chave de publicação SSH ao arquivo da sua conta remota ~/.ssh/authorized_keys.

Exemplo

Aqui está um exemplo que faz o trabalho:

$ 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

O script acima é geralmente estruturado da seguinte maneira:

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

psshDetalhes de alto nível

  • cat <pubkey> gera o arquivo de chave pública para pssh
  • psshusa o -Iswitch para ingerir dados via STDIN
  • -l <remote user> é a conta do servidor remoto (presumimos que você tenha o mesmo nome de usuário nos servidores do arquivo IP)
  • -Apede psshpara solicitar sua senha e reutilizá-la para todos os servidores aos quais se conecta
  • -idiz psshpara enviar qualquer saída para STDOUT em vez de armazená-la em arquivos (seu comportamento padrão)
  • '...cmds to add pubkey...'- esta é a parte mais complicada do que está acontecendo, então eu vou explicar isso sozinha (veja abaixo)

Comandos sendo Executados em Servidores Remotos

Estes são os comandos que psshserão executados em cada servidor:

'                                         \
  umask 077;                              \
  mkdir -p ~/.ssh;                        \
  afile=~/.ssh/authorized_keys;           \
  cat - >> $afile;                        \
  sort -u $afile -o $afile                \
'
Em ordem:
  • defina o umask do usuário remoto como 077, para que todos os diretórios ou arquivos que criaremos tenham suas permissões definidas da seguinte maneira:

    $ 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
    
  • crie o diretório ~/.sshe ignore o aviso se já estiver lá

  • defina uma variável,, $afilecom o caminho para o arquivo allowed_keys
  • cat - >> $afile - pegue a entrada do STDIN e adicione ao arquivo allowed_keys
  • sort -u $afile -o $afile - classifica exclusivamente o arquivo allowed_keys e o salva

NOTA: Esse último bit é para lidar com o caso em que você executa as opções acima várias vezes nos mesmos servidores. Isso evitará que seu pubkey seja anexado várias vezes.

Observe os carrapatos únicos!

Também preste atenção especial ao fato de que todos esses comandos estão aninhados dentro de aspas simples. Isso é importante, pois não queremos $afileser avaliados até depois da execução no servidor remoto.

'               \
   ..cmds...    \
'

Eu expandi o que foi exposto acima para facilitar a leitura aqui, mas geralmente executo tudo em uma única linha, da seguinte forma:

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

Material bônus

Usando psshvocê pode abdicar de construir arquivos e quer fornecer conteúdo dinâmico usando -h <(...some command...)ou você pode criar uma lista de IPs usando outro dos psshinterruptores 's, -H "ip1 ip2 ip3".

Por exemplo:

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

O exemplo acima pode ser usado para extrair uma lista de IPs do meu ~/.ssh/configarquivo. Obviamente, você também pode usar printfpara gerar conteúdo dinâmico:

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

Por exemplo:

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

Você também pode usar seqpara gerar seqüências de números formatados também!

Referências e ferramentas similares a pssh

Se você não quiser usar psshcomo eu fiz acima, existem outras opções disponíveis.

slm
fonte
Ansible's authorized_key_moduleparece não funcionar para nova máquina. Eu tenho que ssh-copy-id xxx primeiro, então estou procurando uma maneira de usar ansible add ssh-key para nova máquina, alguma idéia?
Mithril
@mithril - soa como um bug, eu perguntaria nos fóruns da Ansible sobre isso.
SLM
1

Uma das ferramentas SSH paralelas (clusterssh, mssh, pssh) pode ser apropriada para você.

Por exemplo, use cssh para efetuar login em todas as máquinas e acrescentar a chave você mesmo.

MikeyB
fonte
1
Eu já tenho um conjunto de ferramentas personalizadas para fazer tudo o que preciso, exceto para copiar a chave que é.
devin
Exatamente ... então use essa ferramenta para fazer a única tarefa que está faltando. Embora, se isso for algo contínuo, o script publicado pelo MonkeeSage (adaptado para ler a senha do stdin e trabalhar em vários servidores) provavelmente seja sua melhor aposta.
precisa saber é o seguinte
0

Quero enfatizar o quão ruim é uma idéia:

  1. Use uma senha embutida em seus scripts
  2. Use a mesma senha em TODOS os seus servidores ... como ... por quê !?
  3. NÃO use autenticação SSH public_key + senha se você insistir nisso
  4. Salve a senha em um arquivo de texto

Aqui está uma implementação que é um pouco mais segura ...

#!/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 [email protected]

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 
True Demon
fonte