Paramiko
•Paramiko是对ssh2协议的实现,不仅仅提供远程shell的安全接入功能,还可以为其他远程服务建立安全的隧道(SFTP)
•自身完全由python实现,但是依赖于第三方的C wrapper提供底层的加密功能
当我们使用paramiko作为client与服务器进行连接时,有以下步骤:
1. 定义Transport 连接的远端socket
2. 协商加密方法
3. 身份认证
4. 建立Channel
5. 开始数据传输
在一个Transport上可以建立多个互不干扰的Channel
日常使用中,绝大多数情况下,我们可以使用SSHClient这个已经封装好的类,简化操作。
在一些特殊情况下,如果需要对Transport或Channel有更直接的控制(比如调整Transport默认window的大小等)就需要手动建立。
下面举个栗子:
Python 2.7.14 (default, Mar 9 2018, 23:57:12)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import paramiko
>>>
>>> transport=paramiko.transport.Transport(('192.168.56.13', 22))
>>> transport
<paramiko.Transport at 0x8531f90L (unconnected)>
此时可以看到Transport还未连接
>>> transport.connect(username='nokia', password='nokia123')
>>> transport
<paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))>
此时可以看到调用connect后,Transport连接成功,但是还没有open的channel
>>> session=transport.open_session()
open_session实际建立的是类型为session的channel
>>> transport
<paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>
此时可以看到在Transport上有1个open channel
>>> session
<paramiko.Channel 0 (open) window=0 -> <paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>
>>> session.invoke_shell()
>>> session
<paramiko.Channel 0 (open) window=2097152 in-buffer=305 -> <paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>
在使用invoke_shell()之后,interactive shell建立成功,并且channel成功接入本次shell的stdin,stdout,stderr。window=2097152为stdin buffer目前的大小(这个解释很可能不正确,请忽略),in-buffer为stdout buffer目前接收到的数据
>>> session.recv(305)
'Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-78-generic x86_64)\n\n * Documentation: https://help.ubuntu.com\n * Management: https://landscape.canonical.com\n * Support: https://ubuntu.com/advantage\n\n104 packages can be updated.\n0 updates are security updates.\n\n\n*** System restart required ***\n'
使用recv读取stdout中的305bytes
>>> session
<paramiko.Channel 0 (open) window=2097152 -> <paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>
此时可以看到in-buffer不再显示
>>> session.send_ready()
True
此时可以看到session可以发送数据,因为window还可以接收2097152bytes
>>> session.recv_ready()
False
此时可以看到session目前不能接收数据,因为in-buffer中还没有数据
>>> session.send('ls -l /tmp\n')
11
可以看到我们发送了11bytes,注意我们在末尾添加了(\n),不添加的话相当于没有敲回车
>>> session
<paramiko.Channel 0 (open) window=2097141 in-buffer=131 -> <paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>
此时window变成了2097152-11=2097141
>>> session.recv(131)
'total 4\ndrwx------ 3 root root 4096 May 13 18:22 systemd-private-ed49881fc45b48b9ae609698c400370c-systemd-timesyncd.service-CE71qv\n'
>>> session
<paramiko.Channel 0 (open) window=2097141 -> <paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>
>>> session.close()
>>> session
<paramiko.Channel 0 (closed) -> <paramiko.Transport at 0x8531f90L (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))>>
>>> transport.close()
>>> transport
<paramiko.Transport at 0x8531f90L (unconnected)>
目前还有一个问题我不太确定,就是在每次send之后,channel的window size都会减小,而且减小的值就是所发送的字节数,为什么这个window size不会像in-buffer一样每次进行重置呢?