使用Python利用SSH控制pickering的LXI设备

SSH解释

百度百科解释:

SSH 为 Secure Shell 的缩写,由 IETF 的网络小组(Network Working Group)所制定;SSH 为建立在应用层基础上的安全协议。SSH 是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。SSH最初是UNIX系统上的一个程序,后来又迅速扩展到其他操作平台。SSH在正确使用时可弥补网络中的漏洞。SSH客户端适用于多种平台。几乎所有UNIX平台—包括HP-UX、Linux、AIX、Solaris、Digital UNIX、Irix,以及其他平台,都可运行SSH。

传统的网络服务程序,如:ftp、pop和telnet在本质上都是不安全的,因为它们在网络上用明文传送口令和数据,别有用心的人非常容易就可以截获这些口令和数据。而且,这些服务程序的安全验证方式也是有其弱点的, 就是很容易受到“中间人”(man-in-the-middle)这种方式的攻击。所谓“中间人”的攻击方式, 就是“中间人”冒充真正的服务器接收你传给服务器的数据,然后再冒充你把数据传给真正的服务器。服务器和你之间的数据传送被“中间人”一转手做了手脚之后,就会出现很严重的问题。通过使用SSH,你可以把所有传输的数据进行加密,这样"中间人"这种攻击方式就不可能实现了,而且也能够防止DNS欺骗和IP欺骗。使用SSH,还有一个额外的好处就是传输的数据是经过压缩的,所以可以加快传输的速度。SSH有很多功能,它既可以代替Telnet,又可以为FTP、PoP、甚至为PPP提供一个安全的"通道"

python中的SSH

Python中的paramiko包提供了便捷的ssh连接功能。官网连接如下,功能比较全,官方API解释比较详细。
http://www.paramiko.org/
Pickering的LXI设备提供使用SSH控制的方式,其开启SSH服务的指导连接如下:
https://www.pickeringtest.com/zh-cn/kb/hardware-topics/lxi-ethernet-specific-pages/information-pickering-lxi-products/ssh-control-of-lxi-devices
其原理为在ssh连上以后,系统会在shell中打开一个程序,用户直接在面板中输入相应的命令,实现对设备的控制。
常规的一个简单的paramiko包的应用如下:

import paramiko
 
# 实例化SSHClient
client = paramiko.SSHClient()
 
# 自动添加策略,保存服务器的主机名和密钥信息,如果不添加,那么不再本地know_hosts文件中记录的主机将无法连接
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
# 连接SSH服务端,以用户名和密码进行认证
client.connect(hostname='192.168.1.105', port=22, username='root', password='123456')
 
# 打开一个Channel并执行命令
stdin, stdout, stderr = client.exec_command('df -h ')  # stdout 为正确输出,stderr为错误输出,同时是有1个变量有值
 
# 打印执行结果
print(stdout.read().decode('utf-8'))
 
# 关闭SSHClient
client.close()

但是在使用的过程中,发现提交命令后在stdout.read()这步程序会锁住,没办法读取到返回值。经过长时间的尝试,是因为在连接到设备后,设备开始执行设备控制程序,此时paramiko认为命令一直在执行,所以一直在等到返回值,其通道没有关闭,所以就卡在了read上边。
经过思考,发现其实在连接设备以后,我们需要的并不是执行系统命令,而是只需要往shell中发送字符串就可以,于是有了接下来的程序,这个程序在连接到设备后,不是执行命令,而是建立一个channel,然后发送字符串和接收输出。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Frank

import paramiko,time



class PiSSH():
    def __init__(self,host:str='',port:int = 22,username:str = '',password:str = '',timeout:float =1.0):
        '''
        :param host:
        :param port:
        :param username:
        :param password:
        :param timeout: 该参数是连接ssh时的超时设置,单位为秒
        '''
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.timeout = timeout               #该参数是连接ssh时的超时设置,单位为秒
        self.channel = None
        self.ssh_client = None
        self.read_recv_time_delay = 0.05      #发送指令后回读的延时,若send函数的回读值不完整适当调大该参数,单位是秒


    def __new__(cls, *args, **kwargs):
        '''
        单例模式,保证只有一个连接
        :param args:
        :param kwargs:
        :return:
        '''
        if not hasattr(PiSSH,'_instance'):
            cls._instance = super(PiSSH, cls).__new__(cls)
        return cls._instance




    def _connect(self):
        '''
        连接sshhost,并且开启一个shell的channel,用于信息的收发,相当于直接在远程机上打开shell,
        一般在实例化对象之后,只要不销毁,就只会连接一次,若连接超时或者突然断开连接会抛出异常
        :return:
        '''
        if not self._connected():

            self.ssh_client = paramiko.SSHClient()

            self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
            self.ssh_client.connect(hostname = self.host,
                                    port = self.port,
                                    username= self.username,
                                    password = self.password,
                                    timeout = self.timeout
                                    )
            self.channel = self.ssh_client.invoke_shell()



    def _connected(self):
        '''
        检测是否已连接
        :return:
        '''
        if self.channel and self.ssh_client.get_transport():
            if self.ssh_client.get_transport().is_active():
                return True
            else:
                return False
        return False


    def send(self,command:str) -> str:
        '''
        发送指令,返回指令执行结果的字符串
        :param command:
        :return:
        '''
        self._connect()
        self.channel.send(command+'\n')
        time.sleep(self.read_recv_time_delay)
        result =''
        while self.channel.recv_ready():
            result += self.channel.recv(1024).decode('utf-8')

        return result


    def __del__(self):
        '''
        销毁的时候自动关闭连接
        :return:
        '''
        if self._connected():
            self.channel.close()
            self.ssh_client.close()

使用这个类进行验证:

from pissh import PiSSH

host = '169.254.217.14'    #设置为机箱IP
username = 'sshuser'
password = '123456'         #需要开启机箱的SSH功能,
port = 22

pi =PiSSH(host=host,port=port,username=username,password=password,timeout=1)  #实例化
pi.read_recv_time_delay =0.05                    #设置回读延时,若回读不完整可以调大,目前默认0.05秒,我在我的电脑上测试没问题,默认就是0.05s

if __name__ == '__main__':

    while True:
        command = input('请输入指令》》').strip().upper()
        if command == 'Q':
            break    #按q退出
        res = pi.send(command)
        print(res)

可以正确的执行指令,测试成功

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章