pexpect和ssh:如何格式化su - root -c后的命令字符串
我正在尝试遍历一组服务器和密码,以便更改这些服务器上的sshd配置,这样我就可以通过无密码的SSH密钥以root身份登录和运行命令。
我在bash中可以很容易做到这一点,但我想学习Python,并且(显然)希望避免手动输入密码。
这是我想在bash中做的事情:
scp ~/.ssh/id_rsa.pub /etc/ssh/sshd_config USER@IP:/tmp/
ssh -o StrictHostKeyChecking=no -t USER@IP "su - root -c \"chown root:root /tmp/id_rsa.pub; chmod 600 /tmp/id_rsa.pub; chown root:root /tmp/sshd_config; mkdir /root/.ssh; chown root:root /root/.ssh; chmod 700 /root/.ssh; mv /tmp/id_rsa.pub /root/.ssh/authorized_keys; mv /tmp/sshd_config /etc/ssh/; service sshd reload\""
我在Python中用pexpect接近实现这个目标:
import pexpect
USER="user"
HOST="192.168.1.1"
USERPASS="userpass"
ROOTPASS="rootpass"
COMMAND1="scp /Users/user/.ssh/id_rsa.pub /Users/user/github/ssh-pexpect/sshd_config %s@%s:/tmp/" % (USER, HOST)
COMMAND2="ssh -o StrictHostKeyChecking=no -t %s@%s \"su - root -c \"chown root:root /tmp/id_rsa.pub; chmod 600 /tmp/id_rsa.pub; chown root:root /tmp/sshd_config; mkdir /root/.ssh; chown root:root /root/.ssh; chmod 700 /root/.ssh; mv /tmp/id_rsa.pub /root/.ssh/authorized_keys; mv /tmp/sshd_config /etc/ssh/; service sshd reload\"\"" % (USER, HOST)
child = pexpect.spawn(COMMAND1)
child.expect('password:')
child.sendline(USERPASS)
child.expect(pexpect.EOF)
print child.before
child = pexpect.spawn(COMMAND2)
child.expect('password:')
child.sendline(USERPASS)
child.expect('Password:')
child.sendline(ROOTPASS)
child.expect(pexpect.EOF)
print child.before
当我运行那个COMMAND1(scp命令)时,一切正常。 但是COMMAND2却失败了:
server1:ssh-pexpect user$ python test4.py
id_rsa.pub 100% 410 0.4KB/s 00:00
sshd_config 100% 3498 3.4KB/s 00:00
Traceback (most recent call last):
File "test4.py", line 25, in <module>
child.expect(pexpect.EOF)
File "/Library/Python/2.7/site-packages/pexpect.py", line 1316, in expect
return self.expect_list(compiled_pattern_list, timeout, searchwindowsize)
File "/Library/Python/2.7/site-packages/pexpect.py", line 1330, in expect_list
return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize)
File "/Library/Python/2.7/site-packages/pexpect.py", line 1414, in expect_loop
raise TIMEOUT (str(e) + '\n' + str(self))
pexpect.TIMEOUT: Timeout exceeded in read_nonblocking().
<pexpect.spawn object at 0x102b796d0>
version: 2.4 ($Revision: 516 $)
command: /usr/bin/ssh
args: ['/usr/bin/ssh', '-o', 'StrictHostKeyChecking=no', '-t', 'user@192.168.1.1', 'su - root -c chown', 'root:root', '/tmp/id_rsa.pub;', 'chmod', '600', '/tmp/id_rsa.pub;', 'chown', 'root:root', '/tmp/sshd_config;', 'mkdir', '/root/.ssh;', 'chown', 'root:root', '/root/.ssh;', 'chmod', '700', '/root/.ssh;', 'mv', '/tmp/id_rsa.pub', '/root/.ssh/authorized_keys;', 'mv', '/tmp/sshd_config', '/etc/ssh/;', 'service', 'sshd', 'reload']
searcher: searcher_re:
0: EOF
buffer (last 100 chars): : Permission denied
mv: try to overwrite `/etc/ssh/sshd_config', overriding mode 0600 (rw-------)?
before (last 100 chars): : Permission denied
mv: try to overwrite `/etc/ssh/sshd_config', overriding mode 0600 (rw-------)?
after: <class 'pexpect.TIMEOUT'>
match: None
match_index: None
exitstatus: None
flag_eof: False
pid: 3612
child_fd: 4
closed: False
timeout: 30
delimiter: <class 'pexpect.EOF'>
logfile: None
logfile_read: None
logfile_send: None
maxread: 2000
ignorecase: False
searchwindowsize: None
delaybeforesend: 0.05
delayafterclose: 0.1
delayafterterminate: 0.1
如果我在运行脚本之前删除远程服务器上的/etc/ssh/sshd_config文件,我会得到:
server1:ssh-pexpect user$ python test4.py
id_rsa.pub 100% 410 0.4KB/s 00:00
sshd_config 100% 3498 3.4KB/s 00:00
chown: missing operand
Try `chown --help' for more information.
chown: changing ownership of `/tmp/sshd_config': Operation not permitted
mkdir: cannot create directory `/root/.ssh': Permission denied
chown: cannot access `/root/.ssh': Permission denied
chmod: cannot access `/root/.ssh': Permission denied
mv: accessing `/root/.ssh/authorized_keys': Permission denied
mv: cannot move `/tmp/sshd_config' to `/etc/ssh/sshd_config': Permission denied
bash: service: command not found
Connection to 192.168.1.1 closed.
我甚至不知道该如何调试这个问题,看看哪里出错了。我觉得COMMAND2的解析可能有问题。 我对Python还很陌生,所以任何建议都很感激。 谢谢。
2 个回答
1
我最后选择使用单引号,而不是费劲去弄清楚怎么处理双引号的问题:
COMMAND2="ssh -o StrictHostKeyChecking=no -t %s@%s \"su - root -c 'chown root:root /tmp/id_rsa.pub; chmod 600 /tmp/id_rsa.pub; chown root:root /tmp/sshd_config; mkdir /root/.ssh; chown root:root /root/.ssh; chmod 700 /root/.ssh; mv /tmp/id_rsa.pub /root/.ssh/authorized_keys; mv /tmp/sshd_config /etc/ssh/; service sshd reload'\"" % (USER, HOST)
3
你在使用双引号包裹COMMAND2的时候,已经正确处理了里面的双引号,但你还需要对那些已经被转义的双引号再进行一次转义。换句话说,这其实不是一个Python的问题。不过,你可以考虑使用Python的三重引号来包裹外层的内容,这样会更容易阅读。
编辑:其实,只要正确区分引号就可以了。因为命令行也支持单引号,所以你用单引号的解决方案也是可以的。Python允许你使用单引号或者其他多种引号方式,如果你还没解决这个问题的话,我会推荐你使用这些方式(这样你可以选择不需要对字符串本身进行修改的引号,减少出错的机会)。
所以,下面这些方式都可以:
COMMAND2='ssh -o StrictHostKeyChecking=no -t %s@%s "su - root -c \"chown root:root /tmp/id_rsa.pub; chmod 600 /tmp/id_rsa.pub; chown root:root /tmp/sshd_config; mkdir /root/.ssh; chown root:root /root/.ssh; chmod 700 /root/.ssh; mv /tmp/id_rsa.pub /root/.ssh/authorized_keys; mv /tmp/sshd_config /etc/ssh/; service sshd reload\""' % (USER, HOST)
COMMAND2="""ssh -o StrictHostKeyChecking=no -t %s@%s "su - root -c \"chown root:root /tmp/id_rsa.pub; chmod 600 /tmp/id_rsa.pub; chown root:root /tmp/sshd_config; mkdir /root/.ssh; chown root:root /root/.ssh; chmod 700 /root/.ssh; mv /tmp/id_rsa.pub /root/.ssh/authorized_keys; mv /tmp/sshd_config /etc/ssh/; service sshd reload\"" """ % (USER, HOST)
COMMAND2="ssh -o StrictHostKeyChecking=no -t %s@%s 'su - root -c \"chown root:root /tmp/id_rsa.pub; chmod 600 /tmp/id_rsa.pub; chown root:root /tmp/sshd_config; mkdir /root/.ssh; chown root:root /root/.ssh; chmod 700 /root/.ssh; mv /tmp/id_rsa.pub /root/.ssh/authorized_keys; mv /tmp/sshd_config /etc/ssh/; service sshd reload\"'" % (USER, HOST)
我需要在三重引号中添加一个空格,以便区分旁边的双引号。不过你也可以使用三重单引号。此外,三重引号(无论是单引号还是双引号)允许你嵌入换行符,这样会大大提高可读性:
COMMAND2='''ssh -o StrictHostKeyChecking=no -t %s@%s "su - root -c '
chown root:root /tmp/id_rsa.pub
chmod 600 /tmp/id_rsa.pub
chown root:root /tmp/sshd_config
mkdir /root/.ssh
chown root:root /root/.ssh
chmod 700 /root/.ssh
mv /tmp/id_rsa.pub /root/.ssh/authorized_keys
mv /tmp/sshd_config /etc/ssh/
service sshd reload'"''' % (USER, HOST)