Automatización de ssh-copy-id

30

Tengo un número arbitrario de servidores con la misma combinación de usuario / pase. Quiero escribir un guión (que llamo una vez) para que

ssh-copy-id user@myserver

se llama para cada servidor. Dado que todos tienen el mismo usuario / pase, esto debería ser fácil, pero ssh-copy-idquiere que escriba la contraseña por separado cada vez, lo que anula el propósito de mi script. No hay opción para ingresar una contraseña, es decir ssh-copy-id -p mypassword user@myserver.

¿Cómo puedo escribir un script que complete automáticamente el campo de contraseña cuando lo ssh-copy-idsolicite?

devin
fuente
¿Por qué utiliza la identificación de usuario / contraseña en lugar de la identificación de usuario / clave pública?
kagali-san
16
porque estoy usando este script para configurar el usuario / publickey.
devin

Respuestas:

28

Echa un vistazo a sshpass . Coloque su contraseña en un archivo de texto y haga algo como esto:

$ sshpass -f password.txt ssh-copy-id user@yourserver
quanta
fuente
no funciona en Centos7 solo se ejecuta sin error y no hay clave en el servidor remoto
ImranRazaKhan
19

Puede usar esperar escuchar el mensaje de contraseña y enviar su contraseña:

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

Guarde el script, hágalo ejecutable y llámelo como: ./login.expect user@myserver

MonkeeSage
fuente
¿Necesitas una versión más nueva de bash para usar spawn? Por razones que no puedo controlar, estoy atascado con bash v3.2.
devin
La versión Bash no debería importar. Probé con wait 5.44.1.15, pero he usado similar con versiones anteriores de expect. ¿Tienes problemas para usar el script?
MonkeeSage
spawn: command not found
devin
spawnes una palabra clave de esperar (consulte el manual de esperar (1)). Parece que el script se interpreta como shell en lugar de esperar. ¿Tienes esperar instalado? ¿Qué ocurre si se ejecuta directamente esperar:expect -f login.expect user@myserver
MonkeeSage
1
@Envek Solo iba a agregar esto, pero es bueno ver que el último comentario es una pregunta directa sobre lo que iba a escribir. Utilice esta línea en su lugar:spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Steven Lu
4

La respuesta de quanta es bastante buena, pero requiere que coloque su contraseña en un archivo de texto.

Desde la página del comando man "sshpass":

Si no se da ninguna opción, sshpass lee la contraseña de la entrada estándar.

Por lo tanto, lo que puede hacer es capturar la contraseña una vez durante la secuencia de comandos, almacenarla en una variable, hacer eco de la contraseña y canalizar eso para pasar sshpass como entrada.

Hago esto todo el tiempo y funciona bien. Ejemplo: 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
fuente
2

Este es un problema con ssh-copy-id; También agrega una clave cada vez que la ejecutas. Si está automatizando el proceso, su archivo Author_keys se llena rápidamente con claves duplicadas. Aquí hay un programa de Python que evita ambos problemas. Se ejecuta desde el servidor de control y coloca las claves de un servidor remoto en otro 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
fuente
mi ssh-copy-id ya lo hace: ADVERTENCIA: se omitieron todas las claves porque ya existen en el sistema remoto. ¿Es este tu intento de robar llaves? :)
Mihai Stanescu
2

En lugar de escribir su contraseña varias veces, puede utilizar psshy su -Ainterruptor para solicitarla una vez, y luego alimentar la contraseña a todos los servidores en una lista.

NOTA:ssh-copy-id Sin embargo, el uso de este método no le permite usarlo , por lo que deberá utilizar su propio método para agregar su archivo de clave de publicación SSH al archivo de su cuenta remota ~/.ssh/authorized_keys.

Ejemplo

Aquí hay un ejemplo que hace el trabajo:

$ 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

El script anterior generalmente está estructurado de la siguiente manera:

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

psshDetalles de alto nivel

  • cat <pubkey> saca el archivo de clave pública a pssh
  • psshusa el -Iinterruptor para ingerir datos a través de STDIN
  • -l <remote user> es la cuenta del servidor remoto (asumimos que tiene el mismo nombre de usuario en todos los servidores en el archivo IP)
  • -Ale dice psshque solicite su contraseña y luego la reutilice para todos los servidores a los que se conecta
  • -ile dice psshque envíe cualquier salida a STDOUT en lugar de almacenarla en archivos (su comportamiento predeterminado)
  • '...cmds to add pubkey...'- esta es la parte más complicada de lo que está sucediendo, así que lo desglosaré solo (ver más abajo)

Comandos que se ejecutan en servidores remotos

Estos son los comandos que psshse ejecutarán en cada servidor:

'                                         \
  umask 077;                              \
  mkdir -p ~/.ssh;                        \
  afile=~/.ssh/authorized_keys;           \
  cat - >> $afile;                        \
  sort -u $afile -o $afile                \
'
En orden:
  • configure la umask del usuario remoto en 077, esto es para que cualquier directorio o archivo que creemos tenga sus permisos configurados de la siguiente manera:

    $ 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
    
  • crea el directorio ~/.sshe ignora avisándonos si ya está allí

  • establecer una variable, $afilecon la ruta al archivo autorizado_claves
  • cat - >> $afile - tome la entrada de STDIN y anexe al archivo autorizado_claves
  • sort -u $afile -o $afile - clasifica de forma única el archivo autorizado_claves y lo guarda

NOTA: El último bit es manejar el caso en el que ejecuta lo anterior varias veces contra los mismos servidores. Esto evitará que su pubkey se agregue varias veces.

¡Observe las marcas individuales!

También preste especial atención al hecho de que todos estos comandos están anidados dentro de comillas simples. Eso es importante, ya que no queremos $afileser evaluados hasta después de que se esté ejecutando en el servidor remoto.

'               \
   ..cmds...    \
'

He expandido lo anterior para que sea más fácil de leer aquí, pero generalmente lo ejecuto todo en una sola línea así:

$ 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 de bonificación

Mediante el uso psshse puede renunciar a tener que construir archivos y, o bien proporcionar contenido dinámico utilizando -h <(...some command...)o puede crear una lista de direcciones IP utilizando otro de los psshinterruptores 's, -H "ip1 ip2 ip3".

Por ejemplo:

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

Lo anterior podría usarse para extraer una lista de IP de mi ~/.ssh/configarchivo. Por supuesto, también puede usar printfpara generar contenido dinámico:

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

Por ejemplo:

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

¡También puedes usar seqpara generar secuencias de números con formato!

Referencias y herramientas similares a pssh

Si no desea usar psshcomo lo he hecho anteriormente, hay algunas otras opciones disponibles.

slm
fuente
Ansible's authorized_key_moduleParece que no funciona para una nueva máquina. Primero tengo que ssh-copy-id xxx, así que estoy buscando una forma de usar ansible add ssh-key para una nueva máquina, ¿alguna idea?
Mithril
@mithril: suena como un error, lo preguntaría en los foros de Ansible al respecto.
slm
1

Una de las herramientas paralelas de SSH (clusterssh, mssh, pssh) puede ser adecuada para usted.

Por ejemplo, use cssh para iniciar sesión en todas las máquinas y agregar la clave usted mismo.

MikeyB
fuente
1
Ya tengo un conjunto de herramientas personalizadas para hacer todo lo que necesito, excepto para copiar la clave que es.
devin
Exactamente ... así que usa esta herramienta para hacer la tarea que falta. Aunque si esto va a ser algo continuo, el script que MonkeeSage publicó (adaptado para leer la contraseña de stdin y trabajar en múltiples servidores) probablemente sea su mejor opción.
MikeyB
0

Quiero enfatizar cuán mala idea es:

  1. Use una contraseña codificada en sus scripts
  2. Use la misma contraseña en TODOS sus servidores ... como ... ¿por qué?
  3. NO use SSH public_key + contraseña de autenticación si insiste en esto
  4. Guarde la contraseña en un archivo de texto

Aquí hay una implementación que es un poco más 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 
Verdadero demonio
fuente