当前位置 主页 > 服务器问题 > Linux/apache问题 >

    Django实现WebSSH操作物理机或虚拟机的方法

    栏目:Linux/apache问题 时间:2019-11-19 10:03

    我想用它替换掉xshell、crt之类的工具

    WebSSH操作物理机或虚拟机

    上篇文章给大家介绍详解基于django实现的webssh简单例子,有小伙伴说咖啡哥,我们现在还没有用上Kubernetes,但我想通过浏览器连接我们的物理机和虚拟机该怎么办?

    这就比较简单了,既然我们已经实现了浏览器操作Kubernetes的Pod,那么想想操作Pod和物理机虚拟机有什么区别呢?

    整个数据流是一点没变:用户打开浏览器--》浏览器发送websocket请求给Django建立长连接--》Django与要操作的服务器建立SSH通道,实时的将收到的用户数据发送给SSH后的主机,并将主机执行的结果数据返回给浏览器

    唯一不一样的地方就是Django与要操作的服务器建立SSH通道的方式,在Kubernetes中是通过Kubernetes提供的API建立的Stream流,而操作物理机或者虚拟机的时候我们可以使用Paramiko模块来建立SSH长连接隧道,Paramiko模块建立SSH长连接通道的方法如下:

    # 实例化SSHClient
    ssh = paramiko.SSHClient()
    # 当远程服务器没有本地主机的密钥时自动添加到本地,这样不用在建立连接的时候输入yes或no进行确认
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 连接SSH服务器,这里以账号密码的方式进行认证,也可以用key
    ssh.connect(hostname=host, port=port, username=username, password=password, timeout=8)
    # 打开ssh通道,建立长连接
    transport = ssh.get_transport()
    self.ssh_channel = transport.open_session()
    # 获取ssh通道,并设置term和终端大小
    self.ssh_channel.get_pty(term=term, width=cols, height=rows)
    # 激活终端,这样就可以正常登陆了
    self.ssh_channel.invoke_shell()

    连接建立,可以通过如下方法给SSH通道发送数据

    self.ssh_channel.send(data)

    当然SSH返回的数据也可以通过如下方法持续的输出给Websocket

    while not self.ssh_channel.exit_status_ready():
     # SSH返回的数据需要转码为utf-8,否则json序列化会失败
     data = self.ssh_channel.recv(1024).decode('utf-8','ignore')
     if len(data) != 0:
     message = {'flag': 'success', 'message': data}
     self.websocket.send(json.dumps(message))
     else:
     break

    有了这些信息,结合详解基于django实现的webssh简单例子的文章,实现WebSSH浏览器操作物理机或者虚拟机就不算困难了,完整的Consumer代码如下:

    import io
    import json
    import paramiko
    from threading import Thread
    from channels.generic.websocket import WebsocketConsumer
    from cmdb.backends.sshargs import args
    class SSHBridge(object):
     def __init__(self, websocket):
     self.websocket = websocket
     def connect(self, host, port, username, authtype, password=None, pkey=None, term='xterm', cols=80, rows=24):
     ssh = paramiko.SSHClient()
     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
     try:
     if authtype == 2:
     pkey = paramiko.RSAKey.from_private_key(io.StringIO(pkey))
     ssh.connect(username=username, hostname=host, port=port, pkey=pkey, timeout=8)
     else:
     ssh.connect(hostname=host, port=port, username=username, password=password, timeout=8)
     except Exception as e:
     message = json.dumps({'flag': 'error', 'message': str(e)})
     self.websocket.send(message)
     return False
     # 打开一个ssh通道并建立连接
     transport = ssh.get_transport()
     self.ssh_channel = transport.open_session()
     self.ssh_channel.get_pty(term=term, width=cols, height=rows)
     self.ssh_channel.invoke_shell()
     # 连接建立一次,之后交互数据不会再进入该方法
     for i in range(2):
     recv = self.ssh_channel.recv(1024).decode('utf-8', 'ignore')
     message = json.dumps({'flag': 'success', 'message': recv})
     self.websocket.send(message)
     def close(self):
     try:
     self.websocket.close()
     self.ssh_channel.close()
     except BaseException as e:
     pass
     def _ws_to_ssh(self, data):
     try:
     self.ssh_channel.send(data)
     except OSError as e:
     self.close()
     def _ssh_to_ws(self):
     try:
     while not self.ssh_channel.exit_status_ready():
     data = self.ssh_channel.recv(1024).decode('utf-8', 'ignore')
     if len(data) != 0:
     message = {'flag': 'success', 'message': data}
     self.websocket.send(json.dumps(message))
     else:
     break
     except Exception as e:
     message = {'flag': 'error', 'message': str(e)}
     self.websocket.send(json.dumps(message))
     self.close()
     def shell(self, data):
     Thread(target=self._ws_to_ssh, args=(data,)).start()
     Thread(target=self._ssh_to_ws).start()
    class SSHConsumer(WebsocketConsumer):
     def connect(self):
     self.pk = self.scope['url_route']['kwargs'].get('id')
     self.query = self.scope.get('query_string')
     self.user = self.scope['user']
     self.accept()
     # ssh_connect_args为SSH连接需要的参数
     ssh_connect_args = args(self.pk, self.user, self.query)
     self.ssh = SSHBridge(websocket=self)
     self.ssh.connect(**ssh_connect_args)
     def disconnect(self, close_code):
     self.ssh.close()
     def receive(self, text_data=None):
     text_data = json.loads(text_data)
     self.ssh.shell(data=text_data.get('data', ''))