Python
August 2012 27

Работа с ssh в Python

From Sandbox
Всем добрый день.

Хочу рассказать про paramiko — модуль для работы с ssh в python.
С его помощью можно написать скрипт, который получит доступ к удаленному серверу (или многим) и что-то на нем сделает.

Кому интересно — прошу под кат.

Достаточно часто на работе требовалось выполнить очень однотипные действия на серверах клиентов. Действия пустяковые, наподобие «исправить строчку №12 в конфигурационном файле» или «заменить файл „version_017“ на „version_018“. Это было легко, пока серверов не накопилось *надцать штук. Также подобные задачи надоедали очень быстро, поэтому подобную работу старались поручить новичкам с формулировкой „для приобретения навыков работы с ssh“.

Поначалу самые простые задачи можно было решить стандартными средствами ssh — копированием файла и удаленным исполнением кода. Также пробовали использовать утилиту empty .

Я в то время как раз начинал учить python, решил посмотреть, что в нем есть для этих целей. Гугл услужливо подсказал про paramiko.

Paramiko (комбинация слов языка есперанто „параноик“ и „друг“ — »paranoja" + «amiko») — это модуль для python версии 2.3 и выше, который реализует ssh2 протокол для защищенного (с шифрованием и аутентификацией) соединения с удаленным компьютером. При подключении предоставляется высокоуровневое API для работы с ssh — создается обьект SSHClient. Для большего контроля можно передать сокет (или подобный обьект) классу Transport и работать с удаленным хостом в качестве сервера или клиента. Клиенту для аутентификации можно использовать пароль или приватный ключ и проверку ключа сервера.

Небольшой пример использования этого модуля. Комментарии будут ниже.

import paramiko 

host = '192.168.0.8'
user = 'login'
secret = 'password'
port = 22

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname=host, username=user, password=secret, port=port)
stdin, stdout, stderr = client.exec_command('ls -l')
data = stdout.read() + stderr.read()
client.close()



Получить данные для доступа к удаленному серверу можно любым удобным способом — прописать напрямую в коде, вынести в конфиг, базу данных и т.д. Очевидно, что если хостов несколько, то для их обхода нужно делать цикл.

Основным классом для подключения и удаленной работы является SSHClient. Он предоставляет нам «сессию» с которой мы можем работать далее.

В строчке client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) мы добавляем ключ сервера в список известных хостов — файл .ssh/known_hosts. Если при соединении с сервером ключа в нем не найдено, то по умолчанию ключ «отбивается» и вызввается SSHException.

Для соединения с сервером используем client.connect(). Авторизироваться можно как по комбинации логин-пароль так и по ключам. При соединении можно задать хост, имя пользователя, пароль, порт, ключ, и прочие параметры.

Строка client.exec_command('ls -l') — выполняет команду на удаленном сервере. Потоки ввода-вывода программы возврашщаются в файлообразные обьекты — stdin, stdout, stderr.

Возможно, я недоразобрался, но у меня не получилось получить права рута на уделенном сервере. Буду благодарен, если мне подскажут, как это сделать. Для выполнения действий с правами суперпользователя делал так:

stdin, stdout, stderr = ssh.exec_command('sudo -S rm *')
stdin.write('password' + '\n')
stdin.flush()


UPD: meph1st0 в комментариях предложил для этих целей воспользоваться классом Channel

import paramiko

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(...)

channel = сlient.get_transport().open_session()
channel.get_pty()
channel.settimeout(5)
channel.exec_command('sudo ls')
channel.send(password+'\n')
print channel.recv(1024)

channel.close()
client.close()


Для передачи файлов по sftp можно использовать класс Transport. Для непосредственной передачи файлов используются команды put и get.

host = "example.com"
port = 22
transport = paramiko.Transport((host, port))
transport.connect(username='login', password='password')
sftp = paramiko.SFTPClient.from_transport(transport)

remotepath = '/path/to/remote/file.py'
localpath = '/path/to/local/file.py'

sftp.get(remotepath, localpath)
sftp.put(localpath, remotepath)

sftp.close()
transport.close()


Вкратце всё. Сайт проекта и документация — http://www.lag.net/paramiko/

UPD. В комметариях подсказали, что есть ряд других библиотек инструментов, которые также можно использовать для работы по ssh решения подобных задач. Из перечисленного: fabric, chef, puppet, libbsh2 (pylibbsh2), exscript и net-ssh-telnet на руби. Всем комментаторам спасибо.

+23
92.2k 258
Comments 15
Top of the day