Сегодня я хотел бы поведать вам о том, как можно работать с SSH в Python. SSH — Secure SHell — сетевой протокол, который позволяет производить удаленное управление операционной системой компьютера по зашифрованному соединению. При помощи SSH можно осуществлять передачу файлов. SSH применяется для соединения с удаленной операционной системой по SFTP.
В Python для работы с ssh используется модуль paramiko. Документацию по модулю вы можете почитать тут, ну а я покажу вам пару основных моментов, от которых вы оттолкнетесь и дальше уже будете применять то, что вам необходимо.
Paramiko присутствует в стандартных репозиториях Ubuntu и Debian, и установить его можно очень даже легко:
# apt-get install python-paramiko
Теперь, когда модуль установлен, приступим к программированию)). Для работы с SSH в модуле предусмотрен класс — SSHClient. С него все и начинается:
1
2
3
4
#!/usr/bin/env python
import paramiko
ssh=paramiko.SSHClient()
Я создал объект ssh класса SSHClient, и теперь могу устанавливать соединение. Модуль позволяет авторизоваться как при помощи пары “имя пользователя - пароль”, так и при помощи ключа. Во втором случае, конечно же, нужно иметь “при себе” этот самый ключ.
Разумеется, на разных серверах SSH настроен по разному. Важно знать, что существует 3 варианта авторизации - по имени пользователя и паролю, ключом и гибридный. По умолчанию, скажем, на Debian-based дистрибутивах включен последний вариант. Осмелюсь предположить, что это наиболее популярный вариант настроек. Что он из себя представляет? Когда вы подключаетесь к серверу по SSH, он, перед запросом логина и пароля, предлагает вам принять ключ, чтобы, позже, вы, выгрузив в директорию с ключами на сервере свой ключ, могли авторизоваться именно по ключу. Что получает пользователь? Он получает удобный диалог, на утвердительный ответ которого SSH-клиент скопирует к себе ключики сервера сам. Что получает программист? Весьма неудобную ситуацию - дело в том, что по умолчанию SSHClient настроен так, чтобы не соединяться с сервером, если у клиента нет ключа. Даже если логин и пароль правильны. Это приводит к тому, что SSHClient сбрасывает соединение, и авторизоваться не получается. К счастью, на такие случаи в Paramiko предусмотрено удобное решение:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
Ежели вам охота вернуть все обратно, можете просто не прописывать этот метод. Явно политика по умолчанию устанавливается так:
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
Объект создан и настроен, теперь можно и подключиться:
ssh.connect("<сервер>",username="<имя пользователя>",password="<пароль>")
Необходимо указать свои сервер, имя пользователя и пароль. В качестве сервера можно указать IP-адрес или доменное имя, по которому резолвится ваш сервер.
После успешного соединения есть два варианта работы с сервером - продолжать работать по SSH и выполнять консольные команды сервера или переключиться в режим SFTP и использовать приемы SFTP для работы с файлами. Какой метод предпочтительнее - дело ваше. Посмотрим оба варианта.
Вариант номер один - SFTP. Для переключения в этот режим используем такую конструкцию:
ftp=ssh.open_sftp()
И далее работа с сервером осуществляется по SFTP. В качестве примера приведу пару команд по навигации по директориям сервера и управлению файлами:
1
2
3
4
5
6
7
8
fileList=ftp.listdir()
ftp.chdir("/var/log")
currentDir = ftp.getcwd()
ftp.get("sql.log","mysql.log")
ftp.put("sql.log","mysql.log")
ftp.unlink("mysql.log")
ftp.mkdir("test")
ftp.rmdir("test")
В первой строке я получил список файлов. Метод listdir()
возвращает кортеж, с которым я могу работать как обычно.
Во второй строке я осуществил переход в директорию /var/log
. Таким образом можно перемещаться по директориям на сервере.
В третьей строке мне захотелось узнать, где я нахожусь. В результате выполнения метода getcwd
я получил строку, содержащую путь к моей текущей директории.
Для скачивания файла использется метод get()
— строка четвертая. В качестве первого параметра задается имя локального файла (куда скачивать), в качестве второго — что скачивать наоборот. Можно указывать абсолютные пути.
Для выгрузки файла на сервер используется метод put()
— параметры аналогично get()
. Стоит отметить, что ни get()
, ни put()
не принимают в качестве входных параметров маски (например, *.php
).
Удалить файл можно при помощи метода unlink()
. Параметр задается также, как и у get()
и put()
.
Для создания директории можно использовать метод mkdir()
, а для удаления — rmdir()
.
Вообще, если вы имели ранее дело с sftp/ftp, то, думаю, вы заметили похожесть в названиях методов и команд протоколов FTP/SFTP. Так что трудно не будет, а нюансы всегда можно найти в документации к paramiko.
Вариант номер два — работа с сервером по SSH. Работа с сервером не через SFTP, а через SSH отличается прежде всего тем, что можно выполнять консольные команды. Для выполнения команд по SSH служит метод exec_command()
. Ниже пример (я продублировал соединение с сервером для наглядности):
1
2
3
4
5
6
7
#!/usr/bin/env python
import paramiko
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("<сервер>",username="<имя пользователя>",password="<пароль>")
stdin, stdout, stderr = ssh.exec_command("cat /var/log/syslog")
Данный метод примечателен тем, что при выполнении команды необходимо обрабатывать три возвращаемых канала — это содержимое стандартного ввода, стандартного вывода и стандартного вывода ошибок. Например, результат выполнения вышеописанной команды, если он будет успешен, будет содержаться в stdout. Ко всем трём возвращаемым каналам можно обратиться как к файлу и, например, считать оттуда всё, что необходимо. Для получения результата выполнения всё той же команды выше (результатом будет содержимое файла), я могу просто считать его построчно из stdout:
result = stdout.read().splitlines()
Таким образом я получил кортеж строк системного лога удаленного сервера.
Усложним немного. Допустим, мне понадобилось перезагрузить сервер. Команда перезагрузки доступна только для root (/sbin/reboot
). Для выполнения команд, доступных только root, от обычного пользователя, можно воспользоваться утилитой sudo
. Я исхожу из того, что на сервере установлена и настроена sudo
, и моя учетная запись может выполнять эту утилиту (является т.н. sudoer’ом):
1
2
3
stdin, stdout, stderr = ssh.exec_command("sudo -S reboot")<br />
stdin.write("<пароль пользователя>\n")<br />
stdin.flush()
Мои действия:
- выполняю команду “sudo reboot”
- в результате выполнения мне нужно ввести пароль пользователя (настройки по умолчанию для sudo — пароль вашего пользователя). Для этого я пишу в stdin, как в обычный файл, свой пароль вместе с переносом строки, и отправляю содержимое stdin серверу.
Результатом выполнения будет перезагрузка сервера :). Так что для большей наглядности можете повыполнять другие команды через sudo :).
Это основные моменты работы с ssh в Python, для более подробной информации вы всегда можете обратиться к документации, ссылку на которую я дал в начале статьи. Удачи! ;)
Понимаю что уже много времени прошло, но не подскажите ли.
попытался выполнить следующее
мне выдает следующию ошибку:
>
Как исправить?
“paramiko.ChannelFile from “paramiko.Channel 0 (closed) -“ “paramiko.Transport at 0x5af25b38 (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))””” - почему-то в первом сообщении обрезал ошибку.
Парсер лох. По поводу ошибки: не было ли это связано с фильтрацией соединения или с тем, что вход по паролю на сервере был запрещен?
Спасибо, полезная статья!
Пожалуйста, рад, что оказался полезен :)
Олег, привет! А как сделать что бы вывод выполненной команды записывался в лог файл? Спасибо!
Привет! Если ты выполняешь команду таким образом
…то, после выполнения команды, ее вывод будет содержаться либо в stdout, либо в stderr, в зависимости от результата ее выполнения. Соответственно, ты можешь просто перенаправлять полученные данные в свой логгер (import logging, например). Если вывод команды достаточно длинный, то придется поковыряться, например, сделать так, как тут советуют.
Hi,
I think result = stdout.read.splitlines() should be result = stdout.read().splitlines()
Спасибо, поправил.
Thanks, I’ve updated the article
“Для скачивания файла использется метод get() — строка четвертая. В качестве первого параметра задается имя локального файла (куда скачивать), в качестве второго — что скачивать.”
Наоборот.
Спасибо, поправил
Олег - большое спасибо за статью, все толково и подробно разжевано буду пользоваться :)
Пожалуйста :)
Работа с SSH. Часть 1: делаем это в Python | Заметки одного программиста…
Сегодня я хотел бы поведать вам о том, как можно работать с SSH в Python. SSH — Secure SHell — сетевой протокол, который позволяет производить удаленное управление операционной системой компьютера по зашифрованному соединению. При помощи SSH можно осущ…
sudo в примере поправьте
“sudo -S reboot” … иначе авторизация не пройдёт
Спасибо, поправил