我有一些任意数量的服务器具有相同的用户/传递组合。 我想写一个脚本(我打一次),所以
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
,如果它已经存在,则忽略警告
$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
,则可以使用其他选项。
其中一个并行SSH工具(clusterssh,mssh,pssh)可能适合您。
例如,使用csshlogin到所有机器并自己追encryption钥。
一些可能适合这个法案的东西:
正如其他答案中提到的,sshpass可能是最简单的解决scheme。
我想强调一个想法有多糟糕:
这是一个更安全一点的实施…
#!/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