我有任意数量的具有相同用户/通行证组合的服务器。我想编写一个脚本(我只调用一次),以便
ssh-copy-id user@myserver
为每个服务器调用。由于他们都具有相同的用户名/密码,这应该很容易,但是ssh-copy-id
希望我每次都分别键入密码,这违反了脚本的目的。没有输入密码的选项,即ssh-copy-id -p mypassword user@myserver
。
如何编写一个脚本,该脚本在需要时自动填写密码字段ssh-copy-id
?
我有任意数量的具有相同用户/通行证组合的服务器。我想编写一个脚本(我只调用一次),以便
ssh-copy-id user@myserver
为每个服务器调用。由于他们都具有相同的用户名/密码,这应该很容易,但是ssh-copy-id
希望我每次都分别键入密码,这违反了脚本的目的。没有输入密码的选项,即ssh-copy-id -p mypassword user@myserver
。
如何编写一个脚本,该脚本在需要时自动填写密码字段ssh-copy-id
?
Answers:
看看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
spawn
吗?由于某些原因,我无法控制,因此无法使用bash v3.2。
spawn: command not found
spawn
是Expect关键字(请参见Expect(1)手册)。听起来好像脚本被解释为外壳程序,而不是期望值。您期望安装吗?如果您直接期望运行会发生什么:expect -f login.expect user@myserver
spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Quanta的回答相当不错,但是它要求您将密码放入文本文件中。
在“ sshpass”手册页中:
如果未提供任何选项,则sshpass从标准输入中读取密码。
因此,您可以做的是在脚本执行期间捕获一次密码,将其存储在变量中,回显密码并将其传递给sshpass作为输入。
我一直都这样做,而且效果很好。例:
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
这是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
开关进行一次提示,然后将密码提供给列表中的所有服务器。
注意:ssh-copy-id
但是,使用此方法不允许您使用,因此您需要滚动自己的方法来将SSH pub密钥文件附加到远程帐户的~/.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...'
pssh
细节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,这样我们将要创建的任何目录或文件都会相应地设置其权限,如下所示:
$ 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
创建目录~/.ssh
,如果目录已经存在,请忽略警告我们
$afile
其路径为authorized_keys文件cat - >> $afile
-接收来自STDIN的输入并附加到authorized_keys文件中sort -u $afile -o $afile
-对authorized_keys文件进行唯一排序并保存注意:最后一点是要处理对同一台服务器多次运行上述操作的情况。这样可以避免多次添加您的发布密钥。
还应特别注意所有这些命令都嵌套在单引号内的事实。这很重要,因为我们要$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
您可以不必构造文件并使用来提供动态内容-h <(...some command...)
,也可以使用的另一个pssh
开关创建IP列表-H "ip1 ip2 ip3"
。
例如:
$ cat .... | pssh -h <(grep -A1 dp15 ~/.ssh/config | grep -vE -- '#|--') ...
以上内容可用于从我的~/.ssh/config
文件中提取IP列表。当然,您也可以printf
用于生成动态内容:
$ 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
我上面那样使用,可以使用其他一些选项。
Ansible's authorized_key_module
似乎不适用于新机器。我必须先ssh-copy-id xxx,所以我正在寻找一种只在新机器上使用ansible add ssh-key的方法,有什么主意吗?
并行SSH工具之一(clusterssh,mssh,pssh)可能适合您。
例如,使用cssh登录所有计算机并自己添加密钥。
可能符合要求的几件事:
如其他答案所述,sshpass可能是最简单的解决方案。
我想强调的是,一个想法有多糟糕:
这是一个更安全的实现...
#!/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 login@1.1.1.1
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