首页 > 服务器 > Web服务器 > 正文

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

2024-09-02 12:20:51
字体:
来源:转载
供稿:网友

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

WebSSH操作物理机或虚拟机

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

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

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

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

# 实例化SSHClientssh = paramiko.SSHClient()# 当远程服务器没有本地主机的密钥时自动添加到本地,这样不用在建立连接的时候输入yes或no进行确认ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())# 连接SSH服务器,这里以账号密码的方式进行认证,也可以用keyssh.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 ioimport jsonimport paramikofrom threading import Threadfrom channels.generic.websocket import WebsocketConsumerfrom cmdb.backends.sshargs import argsclass 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', ''))
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表