自动化ssh-copy-id

我有一些任意数量的服务器具有相同的用户/传递组合。 我想写一个脚本(我打一次),所以

ssh-copy-id user@myserver

被称为每个服务器。 由于它们都具有相同的用户/通行证,所以这应该很容易,但是ssh-copy-id要求我每次单独input密码,这就破坏了我的脚本的目的。 没有selectinput密码,即ssh-copy-id -p mypassword user@myserver

我怎么能写一个脚本,当ssh-copy-id要求时自动填写密码字段?

看看sshpass 。 把你的密码放在一个文本文件中,做这样的事情:

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

您可以使用expect来听取密码提示并发送密码:

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

保存脚本,使其可执行,并调用它: ./login.expect user@myserver

这是ssh-copy-id的问题; 每次运行时都会添加一个密钥。 如果您正在自动执行该过程,那么您的authorized_keys文件会很快被重复的密钥混淆。 这是一个避免这两个问题的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' 

您可以使用pssh-A开关提示input一次,然后将密码提供给列表中的所有服务器,而不是多次input密码。

注意:然而,使用这种方法不允许你使用ssh-copy-id ,所以你需要把自己的SSH密钥文件附加到远程帐户的~/.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...' 

高级别的细节

  • 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,这样我们要创build的任何目录或文件都将相应地设置权限,如下所示:

     $ 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 
  • 创build目录~/.ssh ,如果它已经存在,则忽略警告

  • 设置一个variables$afile ,其path为authorized_keys文件
  • cat - >> $afile – 从STDINinput并附加到authorized_keys文件
  • sort -u $afile -o $afile – 唯一地对authorized_keys文件进行sorting并保存

注意:最后一点是处理在同一服务器上运行上述多次的情况。 这将消除你的pubkey多次被附加。

注意单蜱!

还要特别注意所有这些命令嵌套在单引号内的事实。 这很重要,因为我们不希望$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您可以放弃构build文件,并使用-h <(...some command...)提供dynamic内容,也可以使用pssh的另一个交换机创buildIP列表, -H "ip1 ip2 ip3"

例如:

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

以上可以用来从我的~/.ssh/config文件中提取一个IP列表。 您当然也可以使用printf来生成dynamic内容:

 $ 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 ,则可以使用其他选项。

  • sshpt
  • Ansible的authorized_key_module

其中一个并行SSH工具(clusterssh,mssh,pssh)可能适合您。

例如,使用csshlogin到所有机器并自己追encryption钥。

一些可能适合这个法案的东西:

  • 在function上同样的问题也被问到如何将我的SSH pub密钥传播到服务器列表中,而不必一遍又一遍地input我的密码? 。
  • 有一个perl模块,但似乎没有太多维护: https : //github.com/Ruyk/pssh-copy-id

正如其他答案中提到的,sshpass可能是最简单的解决scheme。

我想强调一个想法有多糟糕:

  1. 在脚本中使用硬编码的密码
  2. 在所有服务器上使用相同的密码…就像…为什么?
  3. 如果你坚持这样做,不要使用SSH public_key +密码authentication
  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 [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