2018年10月26日 星期五

Python 學習筆記 : SSH 模組 paramiko 測試

前幾天上面交辦了一件自動化任務, 希望我寫個程式來解決. 此任務主要是透過 ssh 連線遠端主機後向其發送自動產生的指令. 我想用正在學習的 Python tkinter 來撰寫 UI, 而 SSH 連線部分原本想用 Putty, 但我覺得 Python 應該有自己的解決方案, 便上網找到 Paramiko 這個模組.

Paramiko 是以純 Python 實作 SSH v2 加密協定的第三方模組, 底層以 C 擴充函式庫 cryptography 達成低階加密驗證功能, 提供遠端安全連線服務. 最早是由 Jeff Forcier 所創建, 實作並高階封裝了 SSH 協定, 後來開放原始碼成為 Python 底下知名的 SSH 專案, 其首頁, API, 與 GitHub 網址如下 :

http://www.paramiko.org
http://docs.paramiko.org (API)
https://github.com/paramiko/paramiko

Paramiko 需先用 pip 安裝才能匯入使用, 安裝過程如下 :

D:\Python\pip3 install paramiko
Collecting paramiko
  Downloading https://files.pythonhosted.org/packages/cf/ae/94e70d49044ccc234bfdba20114fa947d7ba6eb68a2e452d89b920e62227/paramiko-2.4.2-py2.py3-none-any.whl (193kB)
Collecting pyasn1>=0.1.7 (from paramiko)
  Downloading https://files.pythonhosted.org/packages/d1/a1/7790cc85db38daa874f6a2e6308131b9953feb1367f2ae2d1123bb93a9f5/pyasn1-0.4.4-py2.py3-none-any.whl (72kB)
Collecting pynacl>=1.0.1 (from paramiko)
  Downloading https://files.pythonhosted.org/packages/b5/0f/5f21bd04c8f45685c7b72013fc8efaf0e25baf3b77e62b3b915b5b43a7b6/PyNaCl-1.3.0-cp36-cp36m-win_amd64.whl (188kB)
Requirement already satisfied: cryptography>=1.5 in c:\python36\lib\site-packages (from paramiko) (2.3.1)
Collecting bcrypt>=3.1.3 (from paramiko)
  Downloading https://files.pythonhosted.org/packages/4b/c0/c0550e4b98e0536d3a8b8753b68aa0d3c03af654c43a58328d0bf2c06747/bcrypt-3.1.4-cp36-cp36m-win_amd64.whl
Requirement already satisfied: cffi>=1.4.1 in c:\python36\lib\site-packages (from pynacl>=1.0.1->paramiko) (1.11.5)
Requirement already satisfied: six in c:\python36\lib\site-packages (from pynacl>=1.0.1->paramiko) (1.11.0)
Requirement already satisfied: asn1crypto>=0.21.0 in c:\python36\lib\site-packages (from cryptography>=1.5->paramiko) (0.24.0)
Requirement already satisfied: idna>=2.1 in c:\python36\lib\site-packages (from cryptography>=1.5->paramiko) (2.6)
Requirement already satisfied: pycparser in c:\python36\lib\site-packages (from cffi>=1.4.1->pynacl>=1.0.1->paramiko) (2.19)
Installing collected packages: pyasn1, pynacl, bcrypt, paramiko
Successfully installed bcrypt-3.1.4 paramiko-2.4.2 pyasn1-0.4.4 pynacl-1.3.0

可見 paramiko 有 pyasnl, PyNaCl, bcrypt, asn1crypto, pycparser 等相依模組, 如果是本地安裝, 需先下載安裝這些相依模組後才能順利安裝 paramiko. 我在 Python 3.6.0 (32 位元) 上離線安裝結果如下 :

 D:\>pip3 install pyasn1-0.4.4-py2.py3-none-any.whl
 Processing d:\pyasn1-0.4.4-py2.py3-none-any.whl
 Installing collected packages: pyasn1
 Successfully installed pyasn1-0.4.4

 D:\>pip3 install bcrypt-3.1.4-cp27-cp27m-win32.whl
 bcrypt-3.1.4-cp27-cp27m-win32.whl is not a supported wheel on this platform.

 D:\>pip3 install idna-2.7-py2.py3-none-any.whl
 Processing d:\idna-2.7-py2.py3-none-any.whl
 Installing collected packages: idna
 Successfully installed idna-2.7

 D:\>pip3 install pycparser-2.19.tar.gz
 Processing d:\pycparser-2.19.tar.gz
 Installing collected packages: pycparser
   Running setup.py install for pycparser ... done
 Successfully installed pycparser-2.19

D:\>pip3 install six-1.11.0-py2.py3-none-any.whl
 Processing d:\six-1.11.0-py2.py3-none-any.whl
 Installing collected packages: six
 Successfully installed six-1.11.0

 D:\>pip3 install bcrypt-3.1.4-cp36-cp36m-win32.whl
 Processing d:\bcrypt-3.1.4-cp36-cp36m-win32.whl
 Requirement already satisfied: cffi>=1.1 in c:\python3\lib\site-packages (from b
 crypt==3.1.4)
 Requirement already satisfied: six>=1.4.1 in c:\python3\lib\site-packages (from
 bcrypt==3.1.4)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi>=1.1->bcrypt==3.1.4)
 Installing collected packages: bcrypt
 Successfully installed bcrypt-3.1.4

 D:\>pip3 install PyNaCl-1.3.0-cp36-cp36m-win32.whl
 Processing d:\pynacl-1.3.0-cp36-cp36m-win32.whl
 Requirement already satisfied: six in c:\python3\lib\site-packages (from PyNaCl=
 =1.3.0)
 Requirement already satisfied: cffi>=1.4.1 in c:\python3\lib\site-packages (from
  PyNaCl==1.3.0)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi>=1.4.1->PyNaCl==1.3.0)
 Installing collected packages: PyNaCl
 Successfully installed PyNaCl-1.3.0

D:\>pip3 install asn1crypto-0.24.0-py2.py3-none-any.whl
 Processing d:\asn1crypto-0.24.0-py2.py3-none-any.whl
 Installing collected packages: asn1crypto
 Successfully installed asn1crypto-0.24.0

 D:\>pip3 install cryptography-2.3.1-cp36-cp36m-win32.whl
 Processing d:\cryptography-2.3.1-cp36-cp36m-win32.whl
 Requirement already satisfied: six>=1.4.1 in c:\python3\lib\site-packages (from
 cryptography==2.3.1)
 Requirement already satisfied: idna>=2.1 in c:\python3\lib\site-packages (from c
 ryptography==2.3.1)
 Requirement already satisfied: asn1crypto>=0.21.0 in c:\python3\lib\site-package
 s (from cryptography==2.3.1)
 Requirement already satisfied: cffi!=1.11.3,>=1.7 in c:\python3\lib\site-package
 s (from cryptography==2.3.1)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi!=1.11.3,>=1.7->cryptography==2.3.1)
 Installing collected packages: cryptography
 Successfully installed cryptography-2.3.1

 D:\>pip3 install paramiko-2.4.2-py2.py3-none-any.whl
 Processing d:\paramiko-2.4.2-py2.py3-none-any.whl
 Requirement already satisfied: pynacl>=1.0.1 in c:\python3\lib\site-packages (fr
 om paramiko==2.4.2)
 Requirement already satisfied: cryptography>=1.5 in c:\python3\lib\site-packages
  (from paramiko==2.4.2)
 Requirement already satisfied: bcrypt>=3.1.3 in c:\python3\lib\site-packages (fr
 om paramiko==2.4.2)
 Requirement already satisfied: pyasn1>=0.1.7 in c:\python3\lib\site-packages (fr
 om paramiko==2.4.2)
 Requirement already satisfied: cffi>=1.4.1 in c:\python3\lib\site-packages (from
  pynacl>=1.0.1->paramiko==2.4.2)
 Requirement already satisfied: six in c:\python3\lib\site-packages (from pynacl>
 =1.0.1->paramiko==2.4.2)
 Requirement already satisfied: asn1crypto>=0.21.0 in c:\python3\lib\site-package
 s (from cryptography>=1.5->paramiko==2.4.2)
 Requirement already satisfied: idna>=2.1 in c:\python3\lib\site-packages (from c
 ryptography>=1.5->paramiko==2.4.2)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi>=1.4.1->pynacl>=1.0.1->paramiko==2.4.2)
 Installing collected packages: paramiko
 Successfully installed paramiko-2.4.2

連線 ssh 主機首先要呼叫 SSHClient() 方法建立 SSHClient 物件, 然後呼叫 load_system_host_keys() 載入 ssh 系統主機鍵以便驗證, 再呼叫 connect() 傳入帳號密碼連線遠端主機, 最後呼叫 exec_command() 指令對主機下命令, 它會傳回 3 個元素的 tuple : (stdin, stdout, stderr), 前兩者都是 ChannelFile 物件; 而 stderr 則是 ChannelStderrFile 物件, 利用其 redlines() 方法可讀取命令之回應.

下面範例是以鄉下那台 Pi 3 為遠端主機, 本地則使用 paramiko 來建立 ssh 連線, 並向主機傳送 "ls -al" 指令, 例如 :

Python 3.6.4 (C:\Python36\python.exe)
>>> import paramiko 
>>> ssh=paramiko.SSHClient() 
>>> ssh.load_system_host_keys()   
>>> ssh.connect(hostname="114.27.111.273", username="pi", password="pi") 
>>> ssh_stdin, ssh_stdout, ssh_stderr=ssh.exec_command('ls -al') 
>>> print(''.join(ssh_stdout.readlines())) 
total 64040
drwxr-xr-x 31 pi   pi       4096 Oct 23 07:34 .
drwxr-xr-x  3 root root     4096 Mar  3  2017 ..
-rw-r--r--  1 pi   pi     741773 Dec 24  2017 2017-12-24-204625_1280x1024_scrot.png
-rw-r--r--  1 pi   pi    1139579 Jan 21  2018 2018-01-21-161652_1280x1024_scrot.png
-rw-r--r--  1 pi   pi    1126447 Jan 21  2018 2018-01-21-161711_1280x1024_scrot.png
-rw-r--r--  1 pi   pi     137558 Jan 21  2018 2018-01-21-162138_1280x1024_scrot.png
-rw-------  1 pi   pi       7796 Oct 16 15:59 .bash_history
-rw-r--r--  1 pi   pi        220 Mar  3  2017 .bash_logout
-rw-r--r--  1 pi   pi       3512 Mar  3  2017 .bashrc
drwxr-xr-x  8 pi   pi       4096 Jun 23 20:43 .cache
-rw-r--r--  1 pi   pi         70 Dec 31  2017 checkwifi.sh
drwx------  2 pi   pi       4096 Sep  5 16:17 .chewing
drwx------ 20 pi   pi       4096 Jun 30 23:33 .config
-rw-r--r--  1 pi   pi       1786 Feb 22  2017 crontab.txt
-rw-r--r--  1 pi   pi          0 Apr 29 07:34 db.sqlite
drwx------  3 pi   pi       4096 Mar  4  2017 .dbus
-rw-r--r--  1 pi   pi    4924423 Oct 24 22:40 dead.letter
drwxr-xr-x  2 pi   pi       4096 Sep 17  2017 Desktop
drwxr-xr-x  5 pi   pi       4096 Mar  3  2017 Documents
drwxr-xr-x  2 pi   pi       4096 Feb 16  2018 Downloads
drwx------  2 pi   pi       4096 Jul  1 14:56 .gconf
drwx------  3 pi   pi       4096 Sep 16  2017 .gnome
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 .gstreamer-0.10
-rw-r--r--  1 pi   pi         26 Jun 30 22:09 .gtkrc-2.0
drwxr-xr-x  2 pi   pi       4096 Apr  1  2017 .idlerc
-rw-r--r--  1 root root      523 Mar 31  2017 interfaces
-rw-r--r--  1 pi   pi        375 Sep 24  2017 interfaces_fix
-rw-r--r--  1 pi   pi        430 Jan 23  2017 interfaces_kao
-rw-r--r--  1 pi   pi        428 Sep 24  2017 interfaces_mei
-rw-r--r--  1 pi   pi        428 Jan 24  2017 interfaces_mob
drwxr-xr-x  3 pi   pi       4096 Apr 14  2018 .keras
-rw-r--r--  1 pi   pi         28 Oct 22 14:30 lastip.txt
drwxr-xr-x  3 pi   pi       4096 Mar  3  2017 .local
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Music
-rw-------  1 pi   pi         34 Mar 24  2018 .node_repl_history
drwxr-xr-x  6 pi   pi       4096 Dec 12  2017 node-v9.3.0-linux-armv6l
-rw-r--r--  1 pi   pi   16930799 Dec 12  2017 node-v9.3.0-linux-armv6l.tar.gz
-rw-r--r--  1 pi   pi         21 Jan 26  2017 phpinfo.php
-rw-r--r--  1 pi   pi    8151264 Jan 24  2017 phpMyAdmin-4.6.6-all-languages.tar.bz2
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Pictures
drwxr-xr-x  2 pi   pi       4096 Jan  7  2018 .pip
drwx------  3 pi   pi       4096 Aug  6  2017 .pki
-rw-r--r--  1 pi   pi        675 Mar  3  2017 .profile
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Public
drwxr-xr-x  2 pi   pi       4096 Mar  3  2017 python_games
-rw-------  1 pi   pi          0 Oct 23 07:34 .python_history
-rw-------  1 pi   pi         17 Jun 23 20:42 .python_history-
-rw-r--r--  1 pi   pi       2960 Dec 10  2017 reportip2.py
-rw-r--r--  1 pi   pi       2866 Dec  8  2017 reportip3.py
-rw-r--r--  1 pi   pi       2928 Dec  3  2017 reportip.py
-rw-r--r--  1 pi   pi       2173 Apr 22  2017 rpi-benchmark.sh
drwx------  2 pi   pi       4096 Oct 22  2017 .scim
-rw-r--r--  1 pi   pi         66 Dec  9  2017 .selected_editor
-rw-r--r--  1 pi   pi        671 Feb 26  2017 ssmtp.conf
drwxr-xr-x  5 pi   pi       4096 May 13 16:03 ta-lib
-rw-r--r--  1 pi   pi    1330299 May 13 15:04 ta-lib-0.4.0-src.tar.gz
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Templates
-rw-r--r--  1 pi   pi   28584139 May 26  2017 tensorflow-1.1.0-cp34-cp34m-linux_armv7l.whl
-rw-r--r--  1 pi   pi        281 Feb 25  2018 tensorflow.txt
drwxr-xr-x  4 pi   pi       4096 Feb 19  2018 test
drwxr-xr-x  3 pi   pi       4096 Mar  4  2017 .themes
drwx------  5 pi   pi       4096 Jun 30 23:38 .thonny
drwx------  4 pi   pi       4096 Aug  6  2017 .thumbnails
-rw-r--r--  1 pi   pi    2235652 Feb 22  2017 tony_20170222.zip
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Videos
drwx------  4 pi   pi       4096 Dec 11  2017 .vnc
-rw-------  1 root root      153 Mar 31  2017 wpa_supplicant.conf
-rw-------  1 pi   pi        266 Sep  5 16:17 .Xauthority
-rw-------  1 pi   pi       1187 Sep  5 16:17 .xsession-errors
-rw-------  1 pi   pi       1187 Aug 12 17:17 .xsession-errors.old

exec_command() 的傳回值 ssh_stdout 與 ssh_stdin 均為 ChannelFile 物件; 而 ssh_stderr 則是 ChannelStderrFile 物件 :

>>> type(ssh_stdout) 
<class 'paramiko.channel.ChannelFile'>
>>> type(ssh_stdin) 
<class 'paramiko.channel.ChannelFile'>
>>> type(ssh_stderr) 
<class 'paramiko.channel.ChannelStderrFile'> 

參考 :

Python Paramiko基本使用
Paramiko在Python執行 SSH 小記
系統運維工程師的法寶:python paramiko

沒有留言 :