Python----Paramiko模塊和堡壘機實戰

paramiko模塊

  paramiko是一個用於作遠程控制的模塊,使用該模塊能夠對遠程服務器進行命令或文件操做,值得一說的是,fabric和ansible內部的遠程管理就是使用的paramiko來現實。其實它的底層是對ssh的上層代碼的一個封裝node

1、下載安裝python

1
2
3
#pycrypto,因爲 paramiko 模塊內部依賴pycrypto,因此先下載安裝pycrypto
tomcat@node:~$ pip install pycrypto
tomcat@node:~$ pip install paramiko

2、模塊使用linux

一、SSHClient 用於鏈接遠程服務器並執行基本命令

(1)基於用戶名密碼鏈接兩種方式:ios

-->第一種git

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import paramiko
    
# 建立SSH對象
ssh = paramiko.SSHClient()
# 容許鏈接不在know_hosts文件中的主機
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 鏈接服務器
ssh.connect(hostname='192.168.1.21', port=22, username='root', password='123456')
    
# 執行命令
stdin, stdout, stderr = ssh.exec_command('ls')
# 獲取命令結果
result = stdout.read()
    
# 關閉鏈接
ssh.close()

-->二種:SSHClient 封裝 Transport  github

1
2
3
4
5
6
7
8
9
10
11
12
import paramiko
 
transport = paramiko.Transport(('192.168.1.21', 22))
transport.connect(username='root', password='123456')
 
ssh = paramiko.SSHClient()
ssh._transport = transport
 
stdin, stdout, stderr = ssh.exec_command('df')
print stdout.read()
 
transport.close()

(2)基於公鑰密鑰鏈接的兩種方式:shell

-->第一種  json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import paramiko
   
private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')
   
# 建立SSH對象
ssh = paramiko.SSHClient()
# 容許鏈接不在know_hosts文件中的主機
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 鏈接服務器
ssh.connect(hostname='192.168.1.21', port=22, username='root', key=private_key)
   
# 執行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 獲取命令結果
result = stdout.read()
   
# 關閉鏈接
ssh.close()

-->第二種:SSHClient 封裝 Transport  windows

1
2
3
4
5
6
7
8
9
10
11
12
13
import paramiko
 
private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')
 
transport = paramiko.Transport(('192.168.1.21', 22))
transport.connect(username='root', pkey=private_key)
 
ssh = paramiko.SSHClient()
ssh._transport = transport
 
stdin, stdout, stderr = ssh.exec_command('df')
 
transport.close()

二、SFTPClient用於鏈接遠程服務器並執行上傳下載

(1)基於用戶名密碼上傳下載:tomcat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import paramiko
   
transport = paramiko.Transport(('192.168.1.21',22))
transport.connect(username='root',password='123456')
   
sftp = paramiko.SFTPClient.from_transport(transport)
 
# 將location.py 上傳至服務器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
 
# 將remove_path 下載到本地 local_path
sftp.get('remove_path', 'local_path')
   
transport.close()

(2)基於公鑰密鑰上傳下載:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import paramiko
   
private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')
   
transport = paramiko.Transport(('192.168.1.21', 22))
transport.connect(username='root', pkey=private_key )
   
sftp = paramiko.SFTPClient.from_transport(transport)
 
# 將location.py 上傳至服務器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
 
# 將remove_path 下載到本地 local_path
sftp.get('remove_path', 'local_path')
   
transport.close()

三、實例:經過transport實現遠程執行命令和上傳文件  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import paramiko
 
class SSHConnection(object):
 
    def __init__(self, host='192.168.1.21', port=22, username='root',pwd='123456'):
        self.host = host
        self.port = port
        self.username = username
        self.pwd = pwd
        self.__k = None
 
    def run(self):
        self.connect()
        pass
        self.close()
 
    def connect(self):
        transport = paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.pwd)
        self.__transport = transport
 
    def close(self):
        self.__transport.close()
 
    def cmd(self, command):
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 執行命令
        stdin, stdout, stderr = ssh.exec_command(command)
        # 獲取命令結果
        result = stdout.read()
        return result
 
    def upload(self,local_path, target_path):
        # 鏈接,上傳
        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 將location.py 上傳至服務器 /tmp/test.py
        sftp.put(local_path, target_path)
 
ssh = SSHConnection()
ssh.connect()
r1 = ssh.cmd('df')
print(r1.decode())
#ssh.upload('test.py', "/root/test.py")
ssh.upload('s13_par.py', "/root/s7.py")
ssh.close()
複製代碼
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import paramiko
import uuid

class SSHConnection(object):

    def __init__(self, host='172.16.103.191', port=22, username='wupeiqi',pwd='123'):
        self.host = host
        self.port = port
        self.username = username
        self.pwd = pwd
        self.__k = None

    def create_file(self):
        file_name = str(uuid.uuid4())
        with open(file_name,'w') as f:
            f.write('sb')
        return file_name

    def run(self):
        self.connect()
        self.upload('/home/wupeiqi/tttttttttttt.py')
        self.rename('/home/wupeiqi/tttttttttttt.py', '/home/wupeiqi/ooooooooo.py)
        self.close()

    def connect(self):
        transport = paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.pwd)
        self.__transport = transport

    def close(self):

        self.__transport.close()

    def upload(self,target_path):
        # 鏈接,上傳
        file_name = self.create_file()

        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 將location.py 上傳至服務器 /tmp/test.py
        sftp.put(file_name, target_path)

    def rename(self, old_path, new_path):

        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 執行命令
        cmd = "mv %s %s" % (old_path, new_path,)
        stdin, stdout, stderr = ssh.exec_command(cmd)
        # 獲取命令結果
        result = stdout.read()

    def cmd(self, command):
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 執行命令
        stdin, stdout, stderr = ssh.exec_command(command)
        # 獲取命令結果
        result = stdout.read()
        return result
        


ha = SSHConnection()
ha.run()
複製代碼
1
2
3
4
5
6
# 對於更多限制命令,須要在系統中設置
/etc/sudoers
   
Defaults    requiretty
Defaults:cmdb    !requiretty
 Demo
堡壘機

堡壘機執行流程:

  1. 管理員爲用戶在服務器上建立帳號(將公鑰放置服務器,或者使用用戶名密碼)
  2. 用戶登錄堡壘機,輸入堡壘機用戶名密碼,現實當前用戶管理的服務器列表
  3. 用戶選擇服務器,並自動登錄
  4. 執行操做並同時將用戶操做記錄

注:配置.brashrc實現ssh登錄後自動執行腳本,如:/usr/bin/python /home/wupeiqi/menu.py

實現過程(前戲)

# 利用sys.stdin,肆意妄爲執行操做
# 用戶在終端輸入內容,並將內容發送至遠程服務器
# 遠程服務器執行命令,並將結果返回
# 用戶終端顯示內容

版本一:能連上遠程服務器在終端任意輸入命令,但沒法作到tab鍵補全命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import paramiko
import sys
import os
import socket
import select
import getpass
from paramiko.py3compat import u
  
tran = paramiko.Transport(('192.168.1.21', 22,))
tran.start_client()
tran.auth_password('root', '123456')
  
# 打開一個通道
chan = tran.open_session()
# 獲取一個終端
chan.get_pty()
# 激活器
chan.invoke_shell()
  
while True:
    # 監視用戶輸入和服務器返回數據
    # sys.stdin 處理用戶輸入
    # chan 是以前建立的通道,用於接收服務器返回信息
    readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
    if chan in readable:
        try:
            x = u(chan.recv(1024))
            if len(x) == 0:
                print('\r\n*** EOF\r\n')
                break
            sys.stdout.write(x)
            sys.stdout.flush()
        except socket.timeout:
            pass
    if sys.stdin in readable:
        inp = sys.stdin.readline()
        chan.sendall(inp)
  
chan.close()
tran.close()

版本二:在版本一上增長了命令補全功能和命令記錄 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import paramiko
import sys
import os
import socket
import select
import getpass
import termios
import tty
from paramiko.py3compat import u
  
tran = paramiko.Transport(('192.168.1.21', 22,))
tran.start_client()
tran.auth_password('root', '123456')
  
# 打開一個通道
chan = tran.open_session()
# 獲取一個終端
chan.get_pty()
# 激活器
chan.invoke_shell()
  
  
# 獲取原tty屬性
oldtty = termios.tcgetattr(sys.stdin)
try:
    # 爲tty設置新屬性
    # 默認當前tty設備屬性:
    #   輸入一行回車,執行
    #   CTRL+C 進程退出,遇到特殊字符,特殊處理。
  
    # 這是爲原始模式,不認識全部特殊符號
    # 放置特殊字符應用在當前終端,如此設置,將全部的用戶輸入均發送到遠程服務器
    tty.setraw(sys.stdin.fileno())
    chan.settimeout(0.0)
  
    while True:
        # 監視 用戶輸入 和 遠程服務器返回數據(socket)
        # 阻塞,直到句柄可讀
        r, w, e = select.select([chan, sys.stdin], [], [], 1)
        if chan in r:
            try:
                x = u(chan.recv(1024))
                if len(x) == 0:
                    print('\r\n*** EOF\r\n')
                    break
                sys.stdout.write(x)
                sys.stdout.flush()
            except socket.timeout:
                pass
        if sys.stdin in r:
            x = sys.stdin.read(1)
            if len(x) == 0:
                break
            chan.send(x)
  
finally:
    # 從新設置終端屬性
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
  
  
chan.close()
tran.close()

版本三:windows、linux通用版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import paramiko
import sys
import os
import socket
import getpass
 
from paramiko.py3compat import u
 
# windows does not have termios...
try:
    import termios
    import tty
    has_termios = True
except ImportError:
    has_termios = False
 
 
def interactive_shell(chan):
    if has_termios:
        posix_shell(chan)
    else:
        windows_shell(chan)
 
 
def posix_shell(chan):
    import select
 
    oldtty = termios.tcgetattr(sys.stdin)
    try:
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)
        log = open('handle.log', 'a+', encoding='utf-8')
        flag = False
        temp_list = []
        while True:
            r, w, e = select.select([chan, sys.stdin], [], [])
            if chan in r:
                try:
                    x = u(chan.recv(1024))
                    if len(x) == 0:
                        sys.stdout.write('\r\n*** EOF\r\n')
                        break
                    if flag:
                        if x.startswith('\r\n'):
                            pass
                        else:
                            temp_list.append(x)
                        flag = False
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
            if sys.stdin in r:
                x = sys.stdin.read(1)
                import json
 
                if len(x) == 0:
                    break
 
                if x == '\t':
                    flag = True
                else:
                    temp_list.append(x)
                if x == '\r':
                    log.write(''.join(temp_list))
                    log.flush()
                    temp_list.clear()
                chan.send(x)
 
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
 
 
def windows_shell(chan):
    import threading
 
    sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
 
    def writeall(sock):
        while True:
            data = sock.recv(256)
            if not data:
                sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
                sys.stdout.flush()
                break
            sys.stdout.write(data)
            sys.stdout.flush()
 
    writer = threading.Thread(target=writeall, args=(chan,))
    writer.start()
 
    try:
        while True:
            d = sys.stdin.read(1)
            if not d:
                break
            chan.send(d)
    except EOFError:
        # user hit ^Z or F6
        pass
 
 
def run():
    default_username = getpass.getuser()
    username = input('Username [%s]: ' % default_username)
    if len(username) == 0:
        username = default_username
 
 
    hostname = input('Hostname: ')
    if len(hostname) == 0:
        print('*** Hostname required.')
        sys.exit(1)
 
    tran = paramiko.Transport((hostname, 22,))
    tran.start_client()
 
    default_auth = "p"
    auth = input('Auth by (p)assword or (r)sa key[%s] ' % default_auth)
    if len(auth) == 0:
        auth = default_auth
 
    if auth == 'r':
        default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
        path = input('RSA key [%s]: ' % default_path)
        if len(path) == 0:
            path = default_path
        try:
            key = paramiko.RSAKey.from_private_key_file(path)
        except paramiko.PasswordRequiredException:
            password = getpass.getpass('RSA key password: ')
            key = paramiko.RSAKey.from_private_key_file(path, password)
        tran.auth_publickey(username, key)
    else:
        pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
        tran.auth_password(username, pw)
 
    # 打開一個通道
    chan = tran.open_session()
    # 獲取一個終端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
 
    interactive_shell(chan)
 
    chan.close()
    tran.close()
 
 
if __name__ == '__main__':
    run()

更多參見:paramoko源碼 https://github.com/paramiko/paramiko

paramiko是一個用於作遠程控制的模塊,使用該模塊能夠對遠程服務器進行命令或文件操做,值得一說的是,fabric和ansible內部的遠程管理就是使用的paramiko來現實。其實它的底層是對ssh的上層代碼的一個封裝

1、下載安裝

1
2
3
#pycrypto,因爲 paramiko 模塊內部依賴pycrypto,因此先下載安裝pycrypto
tomcat@node:~$ pip install pycrypto
tomcat@node:~$ pip install paramiko

2、模塊使用

一、SSHClient 用於鏈接遠程服務器並執行基本命令

(1)基於用戶名密碼鏈接兩種方式:

-->第一種

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import paramiko
    
# 建立SSH對象
ssh = paramiko.SSHClient()
# 容許鏈接不在know_hosts文件中的主機
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 鏈接服務器
ssh.connect(hostname='192.168.1.21', port=22, username='root', password='123456')
    
# 執行命令
stdin, stdout, stderr = ssh.exec_command('ls')
# 獲取命令結果
result = stdout.read()
    
# 關閉鏈接
ssh.close()

-->二種:SSHClient 封裝 Transport  

1
2
3
4
5
6
7
8
9
10
11
12
import paramiko
 
transport = paramiko.Transport(('192.168.1.21', 22))
transport.connect(username='root', password='123456')
 
ssh = paramiko.SSHClient()
ssh._transport = transport
 
stdin, stdout, stderr = ssh.exec_command('df')
print stdout.read()
 
transport.close()

(2)基於公鑰密鑰鏈接的兩種方式:

-->第一種  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import paramiko
   
private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')
   
# 建立SSH對象
ssh = paramiko.SSHClient()
# 容許鏈接不在know_hosts文件中的主機
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 鏈接服務器
ssh.connect(hostname='192.168.1.21', port=22, username='root', key=private_key)
   
# 執行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 獲取命令結果
result = stdout.read()
   
# 關閉鏈接
ssh.close()

-->第二種:SSHClient 封裝 Transport  

1
2
3
4
5
6
7
8
9
10
11
12
13
import paramiko
 
private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')
 
transport = paramiko.Transport(('192.168.1.21', 22))
transport.connect(username='root', pkey=private_key)
 
ssh = paramiko.SSHClient()
ssh._transport = transport
 
stdin, stdout, stderr = ssh.exec_command('df')
 
transport.close()

二、SFTPClient用於鏈接遠程服務器並執行上傳下載

(1)基於用戶名密碼上傳下載:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import paramiko
   
transport = paramiko.Transport(('192.168.1.21',22))
transport.connect(username='root',password='123456')
   
sftp = paramiko.SFTPClient.from_transport(transport)
 
# 將location.py 上傳至服務器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
 
# 將remove_path 下載到本地 local_path
sftp.get('remove_path', 'local_path')
   
transport.close()

(2)基於公鑰密鑰上傳下載:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import paramiko
   
private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')
   
transport = paramiko.Transport(('192.168.1.21', 22))
transport.connect(username='root', pkey=private_key )
   
sftp = paramiko.SFTPClient.from_transport(transport)
 
# 將location.py 上傳至服務器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
 
# 將remove_path 下載到本地 local_path
sftp.get('remove_path', 'local_path')
   
transport.close()

三、實例:經過transport實現遠程執行命令和上傳文件  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import paramiko
 
class SSHConnection(object):
 
    def __init__(self, host='192.168.1.21', port=22, username='root',pwd='123456'):
        self.host = host
        self.port = port
        self.username = username
        self.pwd = pwd
        self.__k = None
 
    def run(self):
        self.connect()
        pass
        self.close()
 
    def connect(self):
        transport = paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.pwd)
        self.__transport = transport
 
    def close(self):
        self.__transport.close()
 
    def cmd(self, command):
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 執行命令
        stdin, stdout, stderr = ssh.exec_command(command)
        # 獲取命令結果
        result = stdout.read()
        return result
 
    def upload(self,local_path, target_path):
        # 鏈接,上傳
        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 將location.py 上傳至服務器 /tmp/test.py
        sftp.put(local_path, target_path)
 
ssh = SSHConnection()
ssh.connect()
r1 = ssh.cmd('df')
print(r1.decode())
#ssh.upload('test.py', "/root/test.py")
ssh.upload('s13_par.py', "/root/s7.py")
ssh.close()
複製代碼
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import paramiko
import uuid

class SSHConnection(object):

    def __init__(self, host='172.16.103.191', port=22, username='wupeiqi',pwd='123'):
        self.host = host
        self.port = port
        self.username = username
        self.pwd = pwd
        self.__k = None

    def create_file(self):
        file_name = str(uuid.uuid4())
        with open(file_name,'w') as f:
            f.write('sb')
        return file_name

    def run(self):
        self.connect()
        self.upload('/home/wupeiqi/tttttttttttt.py')
        self.rename('/home/wupeiqi/tttttttttttt.py', '/home/wupeiqi/ooooooooo.py)
        self.close()

    def connect(self):
        transport = paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.pwd)
        self.__transport = transport

    def close(self):

        self.__transport.close()

    def upload(self,target_path):
        # 鏈接,上傳
        file_name = self.create_file()

        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 將location.py 上傳至服務器 /tmp/test.py
        sftp.put(file_name, target_path)

    def rename(self, old_path, new_path):

        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 執行命令
        cmd = "mv %s %s" % (old_path, new_path,)
        stdin, stdout, stderr = ssh.exec_command(cmd)
        # 獲取命令結果
        result = stdout.read()

    def cmd(self, command):
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 執行命令
        stdin, stdout, stderr = ssh.exec_command(command)
        # 獲取命令結果
        result = stdout.read()
        return result
        


ha = SSHConnection()
ha.run()
複製代碼
1
2
3
4
5
6
# 對於更多限制命令,須要在系統中設置
/etc/sudoers
   
Defaults    requiretty
Defaults:cmdb    !requiretty
 Demo
堡壘機

堡壘機執行流程:

  1. 管理員爲用戶在服務器上建立帳號(將公鑰放置服務器,或者使用用戶名密碼)
  2. 用戶登錄堡壘機,輸入堡壘機用戶名密碼,現實當前用戶管理的服務器列表
  3. 用戶選擇服務器,並自動登錄
  4. 執行操做並同時將用戶操做記錄

注:配置.brashrc實現ssh登錄後自動執行腳本,如:/usr/bin/python /home/wupeiqi/menu.py

實現過程(前戲)

# 利用sys.stdin,肆意妄爲執行操做
# 用戶在終端輸入內容,並將內容發送至遠程服務器
# 遠程服務器執行命令,並將結果返回
# 用戶終端顯示內容

版本一:能連上遠程服務器在終端任意輸入命令,但沒法作到tab鍵補全命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import paramiko
import sys
import os
import socket
import select
import getpass
from paramiko.py3compat import u
  
tran = paramiko.Transport(('192.168.1.21', 22,))
tran.start_client()
tran.auth_password('root', '123456')
  
# 打開一個通道
chan = tran.open_session()
# 獲取一個終端
chan.get_pty()
# 激活器
chan.invoke_shell()
  
while True:
    # 監視用戶輸入和服務器返回數據
    # sys.stdin 處理用戶輸入
    # chan 是以前建立的通道,用於接收服務器返回信息
    readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
    if chan in readable:
        try:
            x = u(chan.recv(1024))
            if len(x) == 0:
                print('\r\n*** EOF\r\n')
                break
            sys.stdout.write(x)
            sys.stdout.flush()
        except socket.timeout:
            pass
    if sys.stdin in readable:
        inp = sys.stdin.readline()
        chan.sendall(inp)
  
chan.close()
tran.close()

版本二:在版本一上增長了命令補全功能和命令記錄 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import paramiko
import sys
import os
import socket
import select
import getpass
import termios
import tty
from paramiko.py3compat import u
  
tran = paramiko.Transport(('192.168.1.21', 22,))
tran.start_client()
tran.auth_password('root', '123456')
  
# 打開一個通道
chan = tran.open_session()
# 獲取一個終端
chan.get_pty()
# 激活器
chan.invoke_shell()
  
  
# 獲取原tty屬性
oldtty = termios.tcgetattr(sys.stdin)
try:
    # 爲tty設置新屬性
    # 默認當前tty設備屬性:
    #   輸入一行回車,執行
    #   CTRL+C 進程退出,遇到特殊字符,特殊處理。
  
    # 這是爲原始模式,不認識全部特殊符號
    # 放置特殊字符應用在當前終端,如此設置,將全部的用戶輸入均發送到遠程服務器
    tty.setraw(sys.stdin.fileno())
    chan.settimeout(0.0)
  
    while True:
        # 監視 用戶輸入 和 遠程服務器返回數據(socket)
        # 阻塞,直到句柄可讀
        r, w, e = select.select([chan, sys.stdin], [], [], 1)
        if chan in r:
            try:
                x = u(chan.recv(1024))
                if len(x) == 0:
                    print('\r\n*** EOF\r\n')
                    break
                sys.stdout.write(x)
                sys.stdout.flush()
            except socket.timeout:
                pass
        if sys.stdin in r:
            x = sys.stdin.read(1)
            if len(x) == 0:
                break
            chan.send(x)
  
finally:
    # 從新設置終端屬性
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
  
  
chan.close()
tran.close()

版本三:windows、linux通用版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import paramiko
import sys
import os
import socket
import getpass
 
from paramiko.py3compat import u
 
# windows does not have termios...
try:
    import termios
    import tty
    has_termios = True
except ImportError:
    has_termios = False
 
 
def interactive_shell(chan):
    if has_termios:
        posix_shell(chan)
    else:
        windows_shell(chan)
 
 
def posix_shell(chan):
    import select
 
    oldtty = termios.tcgetattr(sys.stdin)
    try:
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)
        log = open('handle.log', 'a+', encoding='utf-8')
        flag = False
        temp_list = []
        while True:
            r, w, e = select.select([chan, sys.stdin], [], [])
            if chan in r:
                try:
                    x = u(chan.recv(1024))
                    if len(x) == 0:
                        sys.stdout.write('\r\n*** EOF\r\n')
                        break
                    if flag:
                        if x.startswith('\r\n'):
                            pass
                        else:
                            temp_list.append(x)
                        flag = False
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
            if sys.stdin in r:
                x = sys.stdin.read(1)
                import json
 
                if len(x) == 0:
                    break
 
                if x == '\t':
                    flag = True
                else:
                    temp_list.append(x)
                if x == '\r':
                    log.write(''.join(temp_list))
                    log.flush()
                    temp_list.clear()
                chan.send(x)
 
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
 
 
def windows_shell(chan):
    import threading
 
    sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
 
    def writeall(sock):
        while True:
            data = sock.recv(256)
            if not data:
                sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
                sys.stdout.flush()
                break
            sys.stdout.write(data)
            sys.stdout.flush()
 
    writer = threading.Thread(target=writeall, args=(chan,))
    writer.start()
 
    try:
        while True:
            d = sys.stdin.read(1)
            if not d:
                break
            chan.send(d)
    except EOFError:
        # user hit ^Z or F6
        pass
 
 
def run():
    default_username = getpass.getuser()
    username = input('Username [%s]: ' % default_username)
    if len(username) == 0:
        username = default_username
 
 
    hostname = input('Hostname: ')
    if len(hostname) == 0:
        print('*** Hostname required.')
        sys.exit(1)
 
    tran = paramiko.Transport((hostname, 22,))
    tran.start_client()
 
    default_auth = "p"
    auth = input('Auth by (p)assword or (r)sa key[%s] ' % default_auth)
    if len(auth) == 0:
        auth = default_auth
 
    if auth == 'r':
        default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
        path = input('RSA key [%s]: ' % default_path)
        if len(path) == 0:
            path = default_path
        try:
            key = paramiko.RSAKey.from_private_key_file(path)
        except paramiko.PasswordRequiredException:
            password = getpass.getpass('RSA key password: ')
            key = paramiko.RSAKey.from_private_key_file(path, password)
        tran.auth_publickey(username, key)
    else:
        pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
        tran.auth_password(username, pw)
 
    # 打開一個通道
    chan = tran.open_session()
    # 獲取一個終端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
 
    interactive_shell(chan)
 
    chan.close()
    tran.close()
 
 
if __name__ == '__main__':
    run()

更多參見:paramoko源碼 https://github.com/paramiko/paramiko 

相關文章
相關標籤/搜索