pexpect и ssh: как отформатировать строку команд после su - root -c
Я пытаюсь перебрать список серверов и паролей, чтобы изменить конфигурацию sshd на группе серверов, чтобы я мог входить в систему / запускать команды через root с использованием SSH-ключей без пароля.
Я легко могу сделать это в bash, но я пытаюсь изучить Python и (очевидно) хотел бы отказаться от ввода паролей вручную.
Вот что я хочу сделать:
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'ing) работает нормально. Но 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 ответа
У вас есть КОМАНДА2 в двойных кавычках, и вы корректно экранируете все встроенные двойные кавычки, но вам также необходимо дважды экранировать уже экранированные двойные кавычки. Другими словами, это не проблема 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)
Я закончил тем, что пошел на одинарные кавычки вместо того, чтобы пытаться понять, как избежать двойных кавычек
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)