Automatisation de ssh-copy-id

30

J'ai un nombre arbitraire de serveurs avec la même combinaison utilisateur / passe. Je veux écrire un script (que j'appelle une fois) pour que

ssh-copy-id user@myserver

est appelé pour chaque serveur. Comme ils ont tous le même utilisateur / passe, cela devrait être facile, mais ssh-copy-idje veux que je tape le mot de passe séparément à chaque fois, ce qui va à l'encontre de l'objectif de mon script. Il n'y a pas d'option pour saisir un mot de passe, c'est-à-dire ssh-copy-id -p mypassword user@myserver.

Comment puis-je écrire un script qui remplit automatiquement le champ du mot de passe lorsque ssh-copy-idje le demande?

devin
la source
pourquoi utilisez-vous l'identification utilisateur / pass au lieu de l'identification utilisateur / publickey?
kagali-san
16
parce que j'utilise ce script pour configurer l'utilisateur / publickey.
devin

Réponses:

28

Jetez un oeil à sshpass . Placez votre mot de passe dans un fichier texte et faites quelque chose comme ceci:

$ sshpass -f password.txt ssh-copy-id user@yourserver
quanta
la source
cela ne fonctionne pas sur Centos7, exécutez-le sans erreur et sans clé sur le serveur distant
ImranRazaKhan
19

Vous pouvez utiliser attendre d'écouter l'invite de mot de passe et envoyer votre mot de passe:

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

Enregistrez le script, rendez-le exécutable et appelez-le comme: ./login.expect user@myserver

MonkeeSage
la source
Avez-vous besoin d'une version plus récente de bash pour l'utiliser spawn? Pour des raisons que je ne peux pas contrôler, je suis bloqué avec bash v3.2.
devin
La version Bash ne devrait pas avoir d'importance. J'ai testé avec expect 5.44.1.15, mais j'ai utilisé des versions similaires avec expect. Avez-vous des difficultés à utiliser le script?
MonkeeSage
spawn: command not found
devin
spawnest un mot clé expect (voir le manuel expect (1)). On dirait que le script est interprété comme un shell plutôt que comme prévu. Avez-vous prévu d'installer? Que se passe-t-il si vous exécutez attendez directement:expect -f login.expect user@myserver
MonkeeSage
1
@Envek J'allais juste ajouter ceci mais c'est agréable de voir que le dernier commentaire est une question directe pour la chose que j'allais écrire. Utilisez cette ligne à la place:spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Steven Lu
4

La réponse de quanta est assez bonne, mais elle vous oblige à mettre votre mot de passe dans un fichier texte.

Depuis la page de manuel "sshpass":

Si aucune option n'est donnée, sshpass lit le mot de passe à partir de l'entrée standard.

Donc, ce que vous pouvez faire est de capturer le mot de passe une fois pendant le script, de le stocker dans une variable, de faire écho au mot de passe et de le diriger vers sshpass en entrée.

Je fais ça tout le temps et ça marche bien. Exemple: 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

Ésotérique Eric
la source
2

Il s'agit d'un problème avec ssh-copy-id; il ajoute également une clé chaque fois que vous l'exécutez. Si vous automatisez le processus, votre fichier authorized_keys est rapidement encombré de clés en double. Voici un programme Python qui évite les deux problèmes. Il s'exécute à partir du serveur de contrôle et place les clés d'un serveur distant dans un autre serveur distant.

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
la source
mon ssh-copy-id le fait déjà: AVERTISSEMENT: toutes les clés ont été ignorées car elles existent déjà sur le système distant. Est-ce votre tentative de voler des clés? :)
Mihai Stanescu
2

Plutôt que de taper votre mot de passe plusieurs fois, vous pouvez utiliser psshet son -Acommutateur pour le demander une fois, puis envoyer le mot de passe à tous les serveurs d'une liste.

REMARQUE: l' utilisation de cette méthode ne vous permet pas d'utiliser ssh-copy-id, cependant, vous devrez donc rouler votre propre méthode pour ajouter votre fichier de clé de publication SSH au fichier de votre compte distant ~/.ssh/authorized_keys.

Exemple

Voici un exemple qui fait le travail:

$ 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

Le script ci-dessus est généralement structuré comme suit:

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

psshDétails de haut niveau

  • cat <pubkey> sort le fichier de clé publique vers pssh
  • psshutilise le -Icommutateur pour ingérer des données via STDIN
  • -l <remote user> est le compte du serveur distant (nous supposons que vous avez le même nom d'utilisateur sur les serveurs dans le fichier IP)
  • -Aindique psshde demander votre mot de passe, puis de le réutiliser pour tous les serveurs auxquels il se connecte
  • -iindique psshd'envoyer n'importe quelle sortie à STDOUT plutôt que de la stocker dans des fichiers (son comportement par défaut)
  • '...cmds to add pubkey...'- c'est la partie la plus délicate de ce qui se passe, donc je vais le décomposer par lui-même (voir ci-dessous)

Commandes exécutées sur des serveurs distants

Ce sont les commandes qui psshs'exécuteront sur chaque serveur:

'                                         \
  umask 077;                              \
  mkdir -p ~/.ssh;                        \
  afile=~/.ssh/authorized_keys;           \
  cat - >> $afile;                        \
  sort -u $afile -o $afile                \
'
En ordre:
  • définissez umask de l'utilisateur distant sur 077, afin que tous les répertoires ou fichiers que nous allons créer, leurs autorisations soient définies en conséquence, comme suit:

    $ 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
    
  • créer le répertoire ~/.sshet ignorer nous avertir s'il est déjà là

  • définir une variable,, $afileavec le chemin d'accès au fichier authorized_keys
  • cat - >> $afile - prendre l'entrée de STDIN et l'ajouter au fichier authorized_keys
  • sort -u $afile -o $afile - trie de façon unique le fichier authorized_keys et l'enregistre

Remarque: ce dernier bit est de gérer le cas où vous exécutez plusieurs fois ci-dessus sur les mêmes serveurs. Cela évitera que votre clé pub ne soit ajoutée plusieurs fois.

Remarquez les tiques simples!

Portez également une attention particulière au fait que toutes ces commandes sont imbriquées à l'intérieur de guillemets simples. C'est important, car nous ne voulons $afileêtre évalués qu'après son exécution sur le serveur distant.

'               \
   ..cmds...    \
'

J'ai développé ce qui précède, il est donc plus facile à lire ici, mais je lance généralement le tout sur une seule ligne comme ceci:

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

Matériel bonus

En utilisant , psshvous pouvez renoncer à avoir à construire des fichiers et soit fournir un contenu dynamique à l' aide -h <(...some command...)ou vous pouvez créer une liste d'adresses IP en utilisant un autre des psshcommutateurs de », -H "ip1 ip2 ip3".

Par exemple:

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

Ce qui précède pourrait être utilisé pour extraire une liste d'adresses IP de mon ~/.ssh/configfichier. Bien sûr, vous pouvez également utiliser printfpour générer du contenu dynamique:

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

Par exemple:

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

Vous pouvez également utiliser seqpour générer des séquences de nombres formatés aussi!

Références et outils similaires à pssh

Si vous ne souhaitez pas l'utiliser psshcomme je l'ai fait ci-dessus, d'autres options sont disponibles.

slm
la source
Ansible's authorized_key_modulene semble pas fonctionner pour une nouvelle machine. Je dois d'abord ssh-copy-id xxx, donc je cherche un moyen d'utiliser simplement ansible add ssh-key pour une nouvelle machine, une idée?
Mithril
@mithril - sonne comme un bug, je demanderais sur les forums Ansible à ce sujet.
slm
1

L'un des outils SSH parallèles (clusterssh, mssh, pssh) peut vous convenir.

Par exemple, utilisez cssh pour vous connecter à toutes les machines et ajoutez la clé vous-même.

MikeyB
la source
1
J'ai déjà un ensemble d'outils personnalisés pour faire tout ce dont j'ai besoin, sauf pour copier la clé qui est.
devin
Exactement… utilisez donc cet outil pour effectuer la tâche qui manque. Bien que cela continue, le script publié par MonkeeSage (adapté pour lire le mot de passe de stdin et travailler sur plusieurs serveurs) serait probablement votre meilleur pari.
MikeyB
0

Je veux souligner à quel point une idée est mauvaise pour:

  1. Utilisez un mot de passe codé en dur dans vos scripts
  2. Utilisez le même mot de passe sur TOUS vos serveurs ... comme ... pourquoi!?
  3. NE PAS utiliser l'authentification par clé publique SSH + mot de passe si vous insistez
  4. Enregistrez le mot de passe dans un fichier texte

Voici une implémentation un peu plus sécurisée ...

#!/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 
Vrai démon
la source