Python paramiko 模組淺談與SSH主要功能模擬解析
疫情還沒結束,小編只能宅在家裡,哪哪也去不了,今天突發奇想給大家分享一篇教程關於Python paramiko 模組淺談與SSH主要功能模擬解析。
大家都知道,通過SSH服務可以遠端連線到Linux伺服器,檢視上面的日誌狀態,批量配置遠端伺服器,檔案上傳,檔案下載等,Python的paramiko模組同樣實現了這一功能。
首先我們需要安裝這一模組,pycharm環境中如下操作
一,安裝paramiko模組
PyCharm→Preferences→Project:專案名→Project Interpreter
點選箭頭所指加號,在搜尋框輸入選中,並安裝
完成後會在Project Interpreter中顯示,如上上圖
二, 基於使用者名稱和密碼的 sshclient 方式登入
import paramiko # 建立SSH物件 ssh = paramiko.SSHClient() # 允許連線不在know_hosts檔案中的主機 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 連線伺服器 ssh.connect(hostname='192.168.199.146',port=22,username='fishman',password='9') # 執行命令 stdin,stdout,stderr = ssh.exec_command('df') # 獲取命令結果 res,err = stdout.read(),stderr.read() result = res if res else err print(result.decode()) # 關閉連線 ssh.close()
解析:首先匯入模組,然後建立一個SSH物件,然後通過命令連線遠端機器,預設埠是22,這裡我連線的是192.168.199.146的機器,密碼為9,然後執行df命令,獲取返回的結果如下
我們可以比較一下通過SSH得到的結果
三,基於使用者名稱和密碼的 transport 方式登入
基於SSHClient是傳統的連線伺服器、執行命令、關閉的一個操作,有時候需要登入上伺服器執行多個操作,比如執行命令、上傳/下載檔案,上面方法則無法實現,可以通過如下方式來操作
#SSHClient 封裝 Transport import paramiko # 例項化一個transport物件 transport = paramiko.Transport(('192.168.199.146',22)) # 建立連線 transport.connect(username='fishman',password='9') # 將sshclient的物件的transport指定為以上的transport ssh = paramiko.SSHClient() ssh._transport = transport # 執行命令,和傳統方法一樣 stdin,stderr = ssh.exec_command('df') print (stdout.read().decode()) # 關閉連線 transport.close()
四,基於公鑰金鑰連線
4.1 SSH配置免密登陸
我們知道通過配置公鑰金鑰,可以實現SSH的免密登陸,比如現在連線fishman機器會提示我們輸入密碼,然後才能登陸
通過SSH原理與運用中介紹,我們知道,要實現免密公鑰登陸,需要將我們自己機器的公鑰儲存在要登陸的遠端主機上。登入的時候,遠端主機會向用戶傳送一段隨機字串,使用者用自己的私鑰加密後,再發回來。遠端主機用事先儲存的公鑰進行解密,如果成功,就證明使用者是可信的,直接允許登入shell,不再要求密碼。
首先我們生成自己的公鑰通過命令 ssh-keygen,這裡會提示這個公鑰儲存的檔案路徑,預設即可然後回車,這裡由於我本地已經有生成的公鑰,所以提示是否要覆蓋,我這裡就不重寫了,如果本地沒有生成過繼續回車,提示要不要對私鑰設定口令(passphrase),如果擔心私鑰的安全,這裡可以設定一個。預設也可以直接回車
得到公鑰後一種方法是通過命令將公鑰直接傳到遠端機器ssh-copy-id user@host,如我們登陸的機器:
ssh-copy-id [email protected]
另一種方法是進入公鑰檔案去複製,進入.ssh/id_rsa.pub通過more或者cat複製公鑰
~ more .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyQ2GtkbKcFAP/kX/+65e91Q8EJ+twpyqjEBzlVmZ2mxy8Zw7b/AfeV60ehloCq7p521IGJaXWbr3bky7Jljc6x7cAZsRW7mZRPFelPYa5iJ4lIshU3RwhbTYW6RyrG+InF3ognJ7bggpPmJbWFxqqpZwdkEilpnv0+8b17frSbF1xaQQh57vDjG78xr0pys2MBzylqrs1RNiTcRz86zJT7SbujYY/bpiUB78w46CmRlwzHOk8Zg0hcegqSMHjboQyhkSX0xcFv2ZLHm5Gpfba3863M+62Q1r2U+QW5Ki1f3nsGwHWQIRuHNWldUzWKYRPsKJHVvwFwkYDG3sZZMWR [email protected] ~ cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyQ2GtkbKcFAP/kX/+65e91Q8EJ+twpyqjEBzlVmZ2mxy8Zw7b/AfeV60ehloCq7p521IGJaXWbr3bky7Jljc6x7cAZsRW7mZRPFelPYa5iJ4lIshU3RwhbTYW6RyrG+InF3ognJ7bggpPmJbWFxqqpZwdkEilpnv0+8b17frSbF1xaQQh57vDjG78xr0pys2MBzylqrs1RNiTcRz86zJT7SbujYY/bpiUB78w46CmRlwzHOk8Zg0hcegqSMHjboQyhkSX0xcFv2ZLHm5Gpfba3863M+62Q1r2U+QW5Ki1f3nsGwHWQIRuHNWldUzWKYRPsKJHVvwFwkYDG3sZZMWR [email protected] ~
複製後,我們登陸遠端機器,在目錄下.ssh資料夾中發現有以下檔案,其中id_ras id_ras.pub儲存該機器的公鑰私鑰,
know_hosts:ssh會把你每個你訪問過計算機的公鑰(public key)都記錄在~/.ssh/known_hosts。當下次訪問相同計算機時,OpenSSH會核對公鑰。如果公鑰不同,OpenSSH會發出警告, 避免你受到DNS Hijack之類的攻擊。
我們需要將剛才複製的我的公鑰儲存到authorized_keys檔案中
通過vi開啟編輯然後貼上退出儲存
此時,我們退出登陸,然後重新登陸,發現就不需要輸入登陸密碼了,可以實現免密登陸,美滋滋。
另外我們要注意,.ssh目錄的許可權為700,其下檔案authorized_keys和私鑰的許可權為600。否則會因為許可權問題導致無法免密碼登入
另外,將公鑰拷貝到伺服器的~/.ssh/authorized_keys檔案中方法有如下幾種:
1、將公鑰通過scp拷貝到伺服器上,然後追加到~/.ssh/authorized_keys檔案中,這種方式比較麻煩。scp -P 22 ~/.ssh/id_rsa.pub user@host:~/。
2、通過ssh-copy-id命令最簡單,也是第一種方法,ssh-copy-id "-p22 user@host"即可,有時候user@host需要加引號,前面寫-p埠號
3、可以通過cat ~/.ssh/id_rsa.pub | ssh -p 22 user@host ‘cat >> ~/.ssh/authorized_keys',這個也是比較常用的方法,因為可以更改埠號。
4.2 基於公鑰金鑰的 SSHClient 方式登入
進入路徑檢視我本地id_rsa檔案路徑
import paramiko private_key = paramiko.RSAKey.from_private_key_file('/Users/ljf/.ssh/id_rsa') # 建立SSH物件 ssh = paramiko.SSHClient() # 允許連線不在know_hosts檔案中的主機 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 連線伺服器 ssh.connect(hostname='192.168.199.146',pkey=private_key) # 執行命令 stdin,stderr.read() result = res if res else err print(result.decode()) # 關閉連線 ssh.close()
同理。可以用transport封裝
import paramiko private_key = paramiko.RSAKey.from_private_key_file('/Users/ljf/.ssh/id_rsa') # 例項化一個transport物件 transport = paramiko.Transport(('192.168.199.146',pkey=private_key) ssh = paramiko.SSHClient() ssh._transport = transport stdin,stderr.read() result = res if res else err print(result.decode()) # 關閉連線 ssh.close()
同理,可以直接根據私鑰字串連線,首先在本地獲取私鑰字串
➜ .ssh cat id_rsa -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAskNhrZGynBQD/5F//uuXvdUPBCfrcKcqoxAc5VZmdpscvGcO 2/wH3letHoZaAqu6edtSBiWl1m6925MuyZY3Ose3AGbEVu5mUTxXpT2GuYieJSLI VN0cIW02FukcqxviJxd6IJye24IKT5iW1hcaqqWcHZBIpaZ79PvG9e360mxdcWkE Iee7w4xu/Ma9KcrNjAc8paq7NUTYk3Ec/OsyU+0m7o2GP26YlAe/MOOgpkZcMxzp PGYNIXHoKkjB426EMoZEl9MXBb9mSx5uRqX22t/OtzPutkNa9lPkFuSotX957BsB 1kCEbhzVpXVM1imET7CiR1b8BcJGAxt7GWTFkQIDAQABAoIBAH54j7Q0yiMxkCSh dc3GF8H2htDMAZ3K+9T0eYu74LYFFj4UX9Zy2KJGUex2JSX/8CzEDU2PKDkaGFjP 80HR8R0i1BLU1jdWrAC2bvgszoiTBKAULU0IEg0lDlryyAQdpDVX0q2QcKQLfoU6 HMmHWsP2+ut+kgv0Mb19Y4rXbDwctmx53BJ0Ykk3RaQe8FiLOAAdFzxbGIUdIwLh N8PEh9HW/wMFvJVEAgNu3oD0bONH+PZKasxqnOQlHrILSPQrNiXBOxCYr9u8cFx7 hl1V0yhQHnCF+JK2se6VXQnJ1pUVtWc3kRTtwas3x7IRPdNJDbj8WT6/n6ocUm+Q OgEY9gECgYEA3CtYVABizz8GsHpged/xDkOKf9gET5m1kX/awdd3m6lkoWboB5OL ym6YwxWpl6Fs0XHRAih3w2434MyTxqdgZf5ixMgq00PBr7itGycokLm1KO1GFjbZ 4tog3qwX6vejHsDl+TMWojxmkEjwQ1uPcQOeY96CvOafHH+kP5zmfAUCgYEAz0Ym 1y07sSfiKSDEO+99r7deex8fssk/xcFI3PgD/4zwu0zGF3QIB124cbZX6Sy6ut0d jzX+RQm5OGUNR1m4Qg1syeNIEF/2mV0rL7+qPGIpYIsuoeuBwedNj7qTIDx4wKtm aJcwdErEQwRJ7UOX9KEdPkKivyf4A+uT5fCsJR0CgYBE7tFF46UMLDiE8pvYLLRF egIYCuM2pPKDLpuoSzToqL2YBycokBqZc80ib1rc3a67WL5OxarRpmWaXZL7BJaa +G2mHOHDqZgv00tnj/gUcAB3Yuqps9y+OPtHnGwUphoNW+nk/wjcHLsj+6I2BKnB gZeKvzUBvdcdTh13yUEknQKBgHBkmNEbPP/+IXutwdrCLYQnyXq30Mdwqzz/ZxQz BHABK9RUeCHlkCj2X/qBJsBQudxz5ABxBbTH5gC3gvDKrMhcYT5EGSKP9rcIt09H /faKP+eS8TFp882CMCOcxwS25b+L8ZcLTIHyvOOeIrweZ/qFlsbY+UjwUmNFzcfk rmPdAoGBAJ9NWhf16aLQxUrPkUvHK9k7ONUadamBxA6NNvHMZxow81/p9VQK71o7 iUdJmC/+VOvGqbA3AbtqkbjBMUWGjEeVKLxMnCZngfu9J6bnWDUaYbQz3gVY63ca KFWjRXO6GtynW0Dec0Nj/q22V5J+2ZCkIvSAQ+cI04d0Ij7RdKPl -----END RSA PRIVATE KEY----- ➜ .ssh
#!/usr/bin/env python # -*- coding: utf-8 -*- # 基於私鑰字串進行連線 import paramiko from io import StringIO key_str = """-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAskNhrZGynBQD/5F//uuXvdUPBCfrcKcqoxAc5VZmdpscvGcO 2/wH3letHoZaAqu6edtSBiWl1m6925MuyZY3Ose3AGbEVu5mUTxXpT2GuYieJSLI VN0cIW02FukcqxviJxd6IJye24IKT5iW1hcaqqWcHZBIpaZ79PvG9e360mxdcWkE Iee7w4xu/Ma9KcrNjAc8paq7NUTYk3Ec/OsyU+0m7o2GP26YlAe/MOOgpkZcMxzp PGYNIXHoKkjB426EMoZEl9MXBb9mSx5uRqX22t/OtzPutkNa9lPkFuSotX957BsB 1kCEbhzVpXVM1imET7CiR1b8BcJGAxt7GWTFkQIDAQABAoIBAH54j7Q0yiMxkCSh dc3GF8H2htDMAZ3K+9T0eYu74LYFFj4UX9Zy2KJGUex2JSX/8CzEDU2PKDkaGFjP 80HR8R0i1BLU1jdWrAC2bvgszoiTBKAULU0IEg0lDlryyAQdpDVX0q2QcKQLfoU6 HMmHWsP2+ut+kgv0Mb19Y4rXbDwctmx53BJ0Ykk3RaQe8FiLOAAdFzxbGIUdIwLh N8PEh9HW/wMFvJVEAgNu3oD0bONH+PZKasxqnOQlHrILSPQrNiXBOxCYr9u8cFx7 hl1V0yhQHnCF+JK2se6VXQnJ1pUVtWc3kRTtwas3x7IRPdNJDbj8WT6/n6ocUm+Q OgEY9gECgYEA3CtYVABizz8GsHpged/xDkOKf9gET5m1kX/awdd3m6lkoWboB5OL ym6YwxWpl6Fs0XHRAih3w2434MyTxqdgZf5ixMgq00PBr7itGycokLm1KO1GFjbZ 4tog3qwX6vejHsDl+TMWojxmkEjwQ1uPcQOeY96CvOafHH+kP5zmfAUCgYEAz0Ym 1y07sSfiKSDEO+99r7deex8fssk/xcFI3PgD/4zwu0zGF3QIB124cbZX6Sy6ut0d jzX+RQm5OGUNR1m4Qg1syeNIEF/2mV0rL7+qPGIpYIsuoeuBwedNj7qTIDx4wKtm aJcwdErEQwRJ7UOX9KEdPkKivyf4A+uT5fCsJR0CgYBE7tFF46UMLDiE8pvYLLRF egIYCuM2pPKDLpuoSzToqL2YBycokBqZc80ib1rc3a67WL5OxarRpmWaXZL7BJaa +G2mHOHDqZgv00tnj/gUcAB3Yuqps9y+OPtHnGwUphoNW+nk/wjcHLsj+6I2BKnB gZeKvzUBvdcdTh13yUEknQKBgHBkmNEbPP/+IXutwdrCLYQnyXq30Mdwqzz/ZxQz BHABK9RUeCHlkCj2X/qBJsBQudxz5ABxBbTH5gC3gvDKrMhcYT5EGSKP9rcIt09H /faKP+eS8TFp882CMCOcxwS25b+L8ZcLTIHyvOOeIrweZ/qFlsbY+UjwUmNFzcfk rmPdAoGBAJ9NWhf16aLQxUrPkUvHK9k7ONUadamBxA6NNvHMZxow81/p9VQK71o7 iUdJmC/+VOvGqbA3AbtqkbjBMUWGjEeVKLxMnCZngfu9J6bnWDUaYbQz3gVY63ca KFWjRXO6GtynW0Dec0Nj/q22V5J+2ZCkIvSAQ+cI04d0Ij7RdKPl -----END RSA PRIVATE KEY-----""" private_key = paramiko.RSAKey(file_obj=StringIO(key_str)) transport = paramiko.Transport(('192.168.199.146',22)) transport.connect(username='fishman',pkey=private_key) ssh = paramiko.SSHClient() ssh._transport = transport stdin,stderr = ssh.exec_command('df') res,stderr.read() result = res if res else err print(result.decode()) # 關閉連線 ssh.close()
以上得到的結果都相同
這裡需要注意,如果你是Linux及OSX系統,那麼自帶SSH,以上操作都沒問題,如果你是Windows,那麼通過Xsheel也可以生成一個,通過Xsheel--》工具--》新建使用者祕鑰生成嚮導,
如果沒有裝Xsheel,可以將Linux的公鑰copy過來,這樣在這裡可以讀取本地存的公鑰資訊
通過命令sz ~/.ssh/id_rsa,將公鑰儲存為一個txt檔案,paramiko.RSAKey.from_private_key_file(公鑰檔案)
五,SFTPClient用於連線遠端伺服器並執行上傳下載
我們計劃將本地LocalFile.txt上傳到fishman機器的test路徑下remote.txt,這是該路徑下原來的檔案,其中remote.txt為空,沒有內容,通過sftp.put('LocalFile.txt','/home/fishman/test/remote.txt')即可上傳
基於使用者名稱密碼上傳下載
import paramiko # 例項化一個trans物件# 例項化一個transport物件 transport = paramiko.Transport(('192.168.199.146',password='9') # 例項化一個 sftp物件,指定連線的通道 sftp = paramiko.SFTPClient.from_transport(transport) # LocalFile.txt 上傳至伺服器 /home/fishman/test/remote.txt # sftp.put('LocalFile.txt','/home/fishman/test/remote.txt') # 將LinuxFile.txt 下載到本地 fromlinux.txt檔案中 sftp.get('/home/fishman/test/LinuxFile.txt','fromlinux.txt') transport.close()
同理,我們可以下載遠端機器上test路徑下的LinuxFile.txt檔案到本地fromLinux.txt
基於公鑰金鑰上傳下載
import paramiko private_key = paramiko.RSAKey.from_private_key_file('/Users/ljf/.ssh/id_rsa') transport = paramiko.Transport(('192.168.199.146',22)) transport.connect(username='fishman',password='9') sftp = paramiko.SFTPClient.from_transport(transport) # LocalFile.txt 上傳至伺服器 /home/fishman/test/remote.txt # sftp.put('LocalFile.txt','fromlinux.txt') transport.close()
5 實現輸入命令立馬返回結果的功能 以上操作都是基本的連線,如果我們想實現一個類似xshell工具的功能,登入以後可以輸入命令回車後就返回結果:
import paramiko import os import select import sys # 建立一個socket trans = paramiko.Transport(('192.168.2.129',22)) # 啟動一個客戶端 trans.start_client() # 如果使用rsa金鑰登入的話 ''' default_key_file = os.path.join(os.environ['HOME'],'.ssh','id_rsa') prikey = paramiko.RSAKey.from_private_key_file(default_key_file) trans.auth_publickey(username='super',key=prikey) ''' # 如果使用使用者名稱和密碼登入 trans.auth_password(username='super',password='super') # 開啟一個通道 channel = trans.open_session() # 獲取終端 channel.get_pty() # 啟用終端,這樣就可以登入到終端了,就和我們用類似於xshell登入系統一樣 channel.invoke_shell() # 下面就可以執行你所有的操作,用select實現 # 對輸入終端sys.stdin和 通道進行監控,# 當用戶在終端輸入命令後,將命令交給channel通道,這個時候sys.stdin就發生變化,select就可以感知 # channel的傳送命令、獲取結果過程其實就是一個socket的傳送和接受資訊的過程 while True: readlist,writelist,errlist = select.select([channel,sys.stdin,],[],[]) # 如果是使用者輸入命令了,sys.stdin發生變化 if sys.stdin in readlist: # 獲取輸入的內容 input_cmd = sys.stdin.read(1) # 將命令傳送給伺服器 channel.sendall(input_cmd) # 伺服器返回了結果,channel通道接受到結果,發生變化 select感知到 if channel in readlist: # 獲取結果 result = channel.recv(1024) # 斷開連線後退出 if len(result) == 0: print("\r\n**** EOF **** \r\n") break # 輸出到螢幕 sys.stdout.write(result.decode()) sys.stdout.flush() # 關閉通道 channel.close() # 關閉連結 trans.close()
6 支援tab自動補全
import paramiko import os import select import sys import tty import termios ''' 實現一個xshell登入系統的效果,登入到系統就不斷輸入命令同時返回結果 支援自動補全,直接呼叫伺服器終端 ''' # 建立一個socket trans = paramiko.Transport(('192.168.2.129',password='super') # 開啟一個通道 channel = trans.open_session() # 獲取終端 channel.get_pty() # 啟用終端,這樣就可以登入到終端了,就和我們用類似於xshell登入系統一樣 channel.invoke_shell() # 獲取原操作終端屬性 oldtty = termios.tcgetattr(sys.stdin) try: # 將現在的操作終端屬性設定為伺服器上的原生終端屬性,可以支援tab了 tty.setraw(sys.stdin) channel.settimeout(0) while True: readlist,sys.stdin發生變化 if sys.stdin in readlist: # 獲取輸入的內容,輸入一個字元傳送1個字元 input_cmd = sys.stdin.read(1) # 將命令傳送給伺服器 channel.sendall(input_cmd) # 伺服器返回了結果,發生變化 select感知到 if channel in readlist: # 獲取結果 result = channel.recv(1024) # 斷開連線後退出 if len(result) == 0: print("\r\n**** EOF **** \r\n") break # 輸出到螢幕 sys.stdout.write(result.decode()) sys.stdout.flush() finally: # 執行完後將現在的終端屬性恢復為原操作終端屬性 termios.tcsetattr(sys.stdin,termios.TCSADRAIN,oldtty) # 關閉通道 channel.close() # 關閉連結 trans.close()
到此這篇關於Python paramiko 模組詳解與SSH主要功能模擬的文章就介紹到這了,更多相關Python paramiko 模組與SSH內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!