python paramiko

简介:
    paramiko 是 python 下对 ssh(v2) 协议封装的一个库, 可以用于实现客户端或者服务器端的一些功能。本章节主要讲述如何实现客户端功能

安装:
    pip install paramiko

常用组件:
    Channel 实现 ssh 通道建立和维护功能
    Client 实现 ssh 客户端功能
    SFTP 实现 sftp 功能
    Transport 实现 ssh 客户端和服务器端数据传输功能

Client:

Client 组件主要有以下几个 class:
    SSHClient           对 Transport 的高级封装, 实现了传输、通道、SFTP 等方法

    以下几个 class 实现 ssh 新主机密钥管理策略:
        AutoAddPolicy           自动添加主机名和秘钥到 HostKeys 对象(默认为 known_hosts)
        RejectPolicy            缺省值, 自动拒绝未知的主机和秘钥
        WarningPolicy           类似于 AutoAddPolicy 但会发出一个警告


paramiko.client.SSHClient
    常用方法:
        load_system_host_keys()         # 加载 known_hosts 文件默认为 ~/.ssh/known_hosts
        set_missing_host_key_policy()   # 设置新主机和密码管理策略, 接受一个参数, 值可以是 AutoAddPolicy、RejectPolicy、WarningPolicy, 默认为 RejectPolicy
        connect()                       # 建立服务器和客户端之间的连接
            参数:
                hostname                    # 远程主机的地址    
                port=22                     # 远程主机 sshd 监听的端口, 默认 22
                username=None               # 要进行身份验证的用户名(默认为当前系统用户的用户名)
                password=None               # 用户名对应的密码, 使用秘钥登录时如果 passphrase 没有给定值而 password 给定了值时, password 将作为 key 的解密密码
                passphrase=None             # ssh key 的解密密码
                pkey=None                   # 指定 ssh 秘钥路径(如果该选项不为 None , 将启用 ssh 秘钥认证)
                key_filename=None           # 尝试进行身份验证的可选秘钥文件或者文件列表
                timeout=None                # TCP 连接超时时间
                allow_agent=True            # 是否允许连接到 ssh 代理, 默认允许
                look_for_keys=True          # 是否允许 paramiko 在当前系统的 ~/.ssh/ 中搜索秘钥用于尝试认证, 默认允许
                compress=False              # 是否打开压缩 
                banner_timeout=None         # 等待显示 ssh 标题的超时时间
                auth_timeout=None           # 等待身份认证的超时时间
        
        exec_command()                  # 执行远程命令
            参数:
                command                     # 执行指定的 shell 命令
                timeout                     # 设置命令超时时间
                environment                 # 设置 shell 环境变量, 字典类型

            返回值:
                stdint                      # 标准输入
                stdout                      # 标准输出
                stderr                      # 标准错误

        get_transport()                 # 获取底层的 transport 对象, 用于执行低级的任务
            返回值:
                返回 Transport 对象

        invoke_shell()                  # 打开一个 ssh 交互式会话
            参数:
                term                        # 模拟终端的类型
                width                       # 终端窗口的宽度(以字符为单位)
                height                      # 终端窗口的高度(以字符为单位)
                width_pixels                # 终端窗口的宽度(以像素为单位)
                height_pixels               # 终端窗口的高度(以像素为单位)
                environment                 # 设置 shell 环境变量, 字典类型
        
        open_sftp()                     # 向 ssh 服务器申请打开 sftp

        close()                         # 断开和服务器的连接

    异常:  
        BadHostKeyException        无法验证服务器的主机密钥
        AuthenticationException    认证失败
        SSHException               连接或建立 SSH 会话时出现任何其他错误	
        socket.error               连接时发生套接字错误

  

Client 示例:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : HuYuan
# @File    : paramiko_ssh_client.py

import paramiko

class UseSSHClient:
    def __init__(self, hostname, username, port=22, password=None, pkey=None, timeout=30, passphrase=None):
        self.hostname = hostname
        self.username = username
        self.port = port
        self.password = password
        self.pkey = pkey
        self.timeout = timeout
        self.passphrase = passphrase

        self._open_ssh()
		
	def get_key_obj(self, pkeyobj, pkey_file=None, pkey_obj=None, password=None):
		if pkey_file:
			with open(pkey_file) as fo:
				try:
					pkey = pkeyobj.from_private_key(fo, password=password)
					return pkey
				except:
					pass
		else:
			try:
				pkey = pkeyobj.from_private_key(pkey_obj, password=password)
				return pkey
			except:
				pkey_obj.seek(0)

    def _open_ssh(self):
        sshclient = paramiko.client.SSHClient()
        sshclient.set_missing_host_key_policy(paramiko.client.AutoAddPolicy)
        if self.password:
            sshclient.connect(hostname=self.hostname, username=self.username, port=self.port,
                              password=self.password, timeout=self.timeout)
        else:
            # 解析 key
			pkey = get_key_obj(paramiko.RSAKey, pkey_file=self.pkey) or 
			       get_key_obj(paramiko.DSSKey, pkey_file=self.pkey) or 
			       get_key_obj(paramiko.ECDSAKey, pkey_file=self.pkey) or 
			       get_key_obj(paramiko.Ed25519Key, pkey_file=self.pkey)
				   
            sshclient.connect(hostname=self.hostname, username=self.username, pkey=pkey,
                              port=self.port, timeout=self.timeout)

        self.ssh = sshclient

    def exec_comd(self, cmd):
        result = self.ssh.exec_command(cmd)
        return result

    def sftp_get(self, server_path, local_path):
        sftp = self.ssh.open_sftp()
        sftp.get(server_path, local_path)

    def sftp_put(self, server_path, local_path):
        sftp = self.ssh.open_sftp()
        sftp.put(local_path, server_path)

    def close(self):
        self.ssh.close()


if __name__ == '__main__':
    hostname = '192.168.1.100'
    username = 'root'
    password = '123.com'

    sshClient = UseSSHClient(hostname=hostname, username=username, password=password)

    sshClient.sftp_get('/etc/fstab', 'fstab')
    sshClient.sftp_put('/tmp/fstab', 'fstab')

    stdin, stdout, stderr = sshClient.exec_comd('ls /tmp/fstab')
    print(stdout.read())

    sshClient.close()

  

pkey 对象:

paramiko 目前支持 4 总 key 认证, 分别为:
    RSAKey     --> rsa     加密 --> 实现 class  paramiko.rsakey.RSAKey
    DSSKey     --> dsa     加密 --> 实现 class  paramiko.dsskey.DSSKey
    ECDSAKey   --> ecdsa   加密 --> 实现 class  paramiko.ecdsakey.ECDSAKey
    Ed25519Key --> ed25519 加密 --> 实现 class  paramiko.ed25519key.Ed25519Key

他们拥有相同的方法和属性:
    from_private_key()                  # 从文件(或类文件) 对象中读取私钥来创建密钥对象
        参数:
            file_obj                        # 文件或类文件对象
            password=None                   # 如果 password 不为 None, 则 password 值作为私钥的解密密码

        返回值:
            返回密钥对象, 传递给类似 connect() 的参数, 进行身份验证

    from_private_key_file()             # 从文件中读取私钥来创建密钥对象
        参数:
            filename                        # 密钥文件
            password=None                   # 如果 password 不为 None, 则 password 值作为私钥的解密密码

        返回值:
            返回密钥对象, 传递给类似 connect() 的参数, 进行身份验证

    write_private_key()                 # 将私钥内容写入文件(或类文件)对象
        参数:
            file_obj                        # 文件或类文件对象
            password=None                   # 如果 password 不为 None, 则在写入之前使用 password 指定的密码对密钥进行加密

    write_private_key_file()            # 将私钥内容写入文件
        参数:
            filename                        # 密钥文件
            password=None                   # 如果 password 不为 None, 则在写入之前使用 password 指定的密码对密钥进行加密

    get_name()                          # 获取密钥文件的名称

    get_base64()                        # 获取密钥公共部分的 base64 字符串

    get_bits()                          # 返回此键中的有效位数, 这对于判断密钥的相对安全性很有用

    get_fingerprint()                   # 获取密钥公共部分的 MD5 指纹

  

SFTP:

组件:
    paramiko.sftp_client.SFTPClient      用于实现 sftp client 功能
    paramiko.sftp_client.SFTP            SFTPClient 用于向后兼容的别名


常用方法:
    from_transport()                    # 从 Transport 对象中打开 sftp 会话

    put()                               # 上传文件
        参数:
            remotepath                      # 远程路径
            localpath                       # 本地路径
            callback                        # 回掉函数,获取已接收的字节数和总字节数
            confirm                         # 文件传输完成之后是否调用stat()函数 以确认文件大小,默认为True

    get()                               # 下载文件
        参数:
            remotepath                      # 远程路径
            localpath                       # 本地路径
            callback                        # 回掉函数,获取已接收的字节数和总字节数

    getfo()                             # 下载文件, 本地打开一个文件句柄用于写入远程服务的数据
        参数:
            remotepath                      # 远程路径
            fl                              # 本地文件句柄
            callback

    putfo()                             # 上传文件, 参数类似于 getfo() 

    getcwd()                            # 获取当前 sftp 会话所在目录

    listdir()                           # 列出指定路径下的所有文件目录列表(包括以隐藏文件), 默认 sftp 会话所在目录
        参数:
            path                            # 指定远程路径

    listdir_attr()                      # 以类似于 ls -l 的格式列出指定路径下的所有文件目录列表(包括以隐藏文件), 默认 sftp 会话所在目录
        参数:
            path                            # 指定远程路径

    mkdir()                             # 在远程服务器上创建目录
        参数:
            path                            # 要创建的目录路径和名称
            mode                            # 目录权限, 数字格式, 默认为 o777
    
    open()                              # 在远程服务器上打开文件, 参数和 python 的 open() 函数相同

    readlink()                          # 返回符号链接的原始路径
        参数:
            path                            # 指定符号链接

    symlink()                           # 创建符号链接
        参数:
            source                          # 符号链接的原始路径
            dest                            # 符号链接的目标路径

    remove()                            # 删除指定的文件(不能对目录进行删除)
        参数:
            path                            # 指定需要删除的文件

    rmdir()                             # 删除指定目录, 参数和 remove() 相同

    rename()                            # 重命名文件或目录
        参数:
            oldpath                         # 指定需要重命名的文件
            newpath                         # 重命名之后的名称

    stat()                              # 检查远程系统上的指定文件的信息
        参数:
            path                            # 需要检查的文件

    utime()                             # 修改文件的 atime 和 mtime
        参数:
            path                            # 指定的文件
            times                           # 修改后的时间元组, 格式 (atime, mtime)

  

Channel:

paramiko.channel.Channel            对 paramiko 的底层 class 之一, 实现通道建立和维护等功能 

常用方法:
    exec_command()                      # 在远程服务器上执行命令

    fileno()                            # 返回 OS 级文件描述符, 可用于轮询, 但不能用于读取或写入, 这主要是为了让 Python 的 select 模块能够工作(此方法将导致 Channel 读取效率降低)
    
    get_id()                            # 获取通道 ID

    set_name()                          # 设置通道名称

    get_name()                          # 获取通道名称

    get_pty()                           # 向服务器请求一个 pty 终端
        参数:
            term="vt100"                    # 终端类型
            width                           # 终端窗口的宽度(以字符为单位)
            height                          # 终端窗口的高度(以字符为单位)
            width_pixels                    # 终端窗口的宽度(以像素为单位)
            height_pixels                   # 终端窗口的高度(以像素为单位)

    get_transport()                       # 获取底层的 transport 对象, 用于执行低级的任务
    getpeername()                         # 获取服务器地址

    settimeout()                          # 设置通道超时时间
    gettimeout()                          # 查看通道超时时间

    invoke_shell()                      # 在此 channel 上请求交互式 shell 会话(通常会和 get_pty() 一起使用)

    invoke_subsystem()                  # 请求服务器上的子系统
        参数:
            subsystem                       # 子系统的名称, 比如: sftp

    recv()                                  # 接收远程服务器返回的数据
        参数:           
            nbytes                          # 设置一次获取的最大字节数

    recv_exit_status()                      # 获取服务上进程返回的退出状态, 在使用 exec_command 执行命令获取返回值时有用

    request_forward_agent()             # 请求转发 ssh 代理

    request_x11()                       # 请求 x11 会话

    resize_pty()                        # 重新设置 pty 的大小, 用于更改 get_pty() 设置的大小
        参数:
            width                           # 终端窗口的宽度(以字符为单位)
            height                          # 终端窗口的高度(以字符为单位)
            width_pixels                    # 终端窗口的宽度(以像素为单位)
            height_pixels                   # 终端窗口的高度(以像素为单位)


    send()                              # 向服务器发送数据, 检查数据是否发送完毕, 如果仅传输了部分数据, 则尝试传送剩余数据
        参数:
            s                               # 要发送的数据

    sendall()                           # 向服务器发送数据, 直到所有数据都已发送或发生错误
        参数:
            s                               # 要发送的数据

    set_environment_variable()          # 设置环境变量 
        参数:
            name                            # 变量名
            value                           # 变量值

    update_environment()                # 更新环境变量
        参数:
            environment                     # 需要更新的环境变量的值, 类型为 dict


    setblocking()                       # 设置通道的阻塞模式:
        参数:
            blocking                        # 如果 blocking 为 0, 则将通道设置为非阻塞模式; 否则设置为阻止模式, 默认为阻塞模式

    settimeout()                        # 设置阻塞读写的超时时间, 如果 setblocking() 设置为 0, 那么相当于 settimeout(0)
        参数:
            timeout                         # 超时时间

    shutdown()                          # 关闭通道
        how                                 # 怎么样关闭通道, 0: 停止接收数据、1: 停止发送数据、2: 停止接收和发送数据

    shutdown_read()                     # shutdown(0) 的简写
    shutdown_write()                    # shutdown(1) 的简写

    close()                             # 断开和服务器的连接

  

Transport:

paramiko.transport.Transport        paramiko 的核心主件, 实现了一系列对 ssh 底层的操作

常用方法:
    connect()                           # 协商会话, 并可选的进行身份认证
        参数: 
            username=""                     # 要进行身份验证的用户名
            password=None                   # 用户名对应的密码
            pkey=None                       # 使用秘钥认证

    # 如果 connect() 方法只是进行了会话协商而没有进行身份验证时, 则可以使用以下方法进行身份验证
        auth_password()                 # 使用密码认证
            参数:
                username                    # 进行身份验证的用户
                password                    # 用户密码

        auth_publickey()                # 使用秘钥认证
            参数:
                username                    # 进行身份验证的用户
                key                         # 用户秘钥

        auth_none()                     # 尝试使用空密码登录, 由于 Linux 的密码策略所以该方法几乎都是失败
            参数:
                username                    # 用户名

    close()                             # 断开和服务器的连接

    get_username()                      # 获取登录用户的用户名
    
    getpeername()                       # 获取服务器地址

    is_active()                         # 如果当前会话处于活动状态则返回 True, 反之则返回 false

    is_authenticated()                  # 如果当前会话处于活动状态且已通过身份验证则返回 True, 反之则返回 false

    open_channel()                      # 向服务器请求打开新的 channel
        参数:
            kind                        # 打开通道的类型(session, forwarded-tcpip, direct-tcpip, x11)
            dest_addr=None              # 端口转发的目标地址(ip, port), 只有当通道类型为 forwarded-tcpip 和 direct-tcpip 时生效
            src_addr=None               # 端口转发的源地址, 只有当通道类型为 forwarded-tcpip、direct-tcpip 或 x11 时生效
            window_size=None            # 会话的窗口大小
            max_packet_size=None        # 此会话的最大数据包大小
            timeout=None                # 打开通道的超时时间, 默认问 1h

    open_forwarded_tcpip_channel()      # open_channel() forwarded-tcpip 的简写
    open_session()                      # open_channel() session 的简写
    open_x11_channel()                  # open_channel() x11 的简写

    open_sftp_client()                  # 打开 sftp 通道

    set_keepalive()                     # 打开/关闭 keepalive 数据包(默认为关闭), 如果设置了此值, 在 interval 指定的时间内没有数据传输将发送 keepalive 数据包
        参数:
            interval                        # 指定多长时间没有数据传输后开始发送 keepalive

    use_compression()                   # 打开或关闭压缩, 建议关闭, 因为它会对交互式会话产生负面影响
        参数:
            compress                    # 是否打开压缩, true/false

  

Transport 示例:

import paramiko

hostname = '192.168.1.100'
username = 'root'
password = '123.com'
port = 22
transport = paramiko.transport.Transport(sock=(hostname, port))
transport.connect()
transport.auth_password('root','123.com')
sftp = transport.open_sftp_client()
sftp.get('/etc/fstab', 'fstab-transport')

  

示例: 实现 ssh 客户端(只能在 Linux 下运行)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : HuYuan
# @File    : python_shell.py

import paramiko
import sys
import socket
import termios
import tty
import select


def posix_shell(chan):
    local_tty = termios.tcgetattr(sys.stdin)
    try:
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)
        while True:
            read, write, error = select.select([chan, sys.stdin], [], [])
            if chan in read:
                try:
                    recv = chan.recv(1024)
                    if not len(recv):
                        break
                    sys.stdout.write(recv.decode())
                    sys.stdout.flush()
                except socket.timeout:
                    pass

            if sys.stdin in read:
                content = sys.stdin.read(1)
                if not len(content):
                    break

                chan.send(content)
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, local_tty)


def open_ssh_client():
    hostname = '192.168.1.100'
    username = 'root'
    password = '123.com'
    port = 22

    try:
        ssh_client = paramiko.client.SSHClient()
        ssh_client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
        ssh_client.connect(hostname=hostname, username=username, password=password, port=port, timeout=10)
        tran = ssh_client.get_transport()
        chan = tran.open_session()
        chan.get_pty()
        chan.invoke_shell()
        posix_shell(chan)
        tran.close()

    except socket.timeout as e:
        print('连接超时', e)
        exit(10)

    except Exception as e:
        print(e)


if __name__ == '__main__':
    open_ssh_client()

  

相关项目:

django + paramiko + websocket 实现 webssh:
项目地址: https://github.com/huyuan1999/django-webssh

  

原文地址:https://www.cnblogs.com/huyuanblog/p/10155933.html