这个功能我可以不用,但你不能没有

前几篇文章实现了对物理机虚拟机以及Kubernetes中Pod的WebSSH操作,可以方便的在web端对系统进行管理,同时也支持对所有操作进行全程录像,以方便后续的查看与审计

这一篇文章接着实现一个看起来很炫酷,但实际上你可能不会经常使用,又必须要存在的功能:实时监控用户操作,在必要的时候将用户踢下线

实时查看操作

django通过channels实现websocket中有一个非常重要的概念叫layer,layer可以将多个channel合并成一个group,我们可以发送消息给group,那么group里的每个channel都能收到

关于Channel我有写过两篇文章结合聊天室web端实现tail-f功能这两个案例来详细介绍,两篇文章是『Django使用Channels实现WebSocket--上篇』『Django使用Channels实现WebSocket--下篇』,对上边提到的名词一脸懵逼的朋友可以通过这两篇文章来学习

之前的WebSSH仅是单连接,只需要客户端和服务器建立长连接,然后处理指令就ok了,我们并没有启用channel的layer,实际上也可以看作是单channel,但要实现监控的功能,就需要将操作者和管理员(监控者)的多个channel合并在一起组成一个group,这样操作者的所有操作都可以发送给这个group,同处于这个group内监控者就能实时收到消息了,大概流程变化如下图所示

接下来看下具体实现,以下所有代码均是在这篇文章的基础上进行说明讲解:『Django实现WebSSH操作物理机或虚拟机』

首先我们要启用layer,这个需要在settings.py中添加如下配置

CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('ops-coffee.cn', 6379)],
},
},
}

然后将处理WebSSH连接名为SSHConsumer的Consumer做改造,以使其支持layer,代码如下

class SSHConsumer(WebsocketConsumer):
def connect(self):
# 格式化参数
ssh_connect_args = args(self.scope) # 新建录像记录
self.host = Host.objects.get(host=ssh_connect_args.get('host'))
self.group_name = '%s-%s-%d' % (
ssh_connect_args.get('host'), ssh_connect_args.get('username'), time.time()) self.therecord = Record.objects.create(
host=self.host,
user=self.scope['user'],
group=self.group_name,
channel=self.channel_name,
cols=ssh_connect_args.get('cols'),
rows=ssh_connect_args.get('rows'),
is_connecting=True
) async_to_sync(self.channel_layer.group_add)(
self.group_name,
self.channel_name
) self.accept() # WebSocket连接成功后,连接ssh
self.ssh = SSHBridge(self.therecord, websocket=self)
self.ssh.connect(**ssh_connect_args) def disconnect(self, close_code):
# 将连接状态置为False
self.therecord.is_connecting = False
self.therecord.save() async_to_sync(self.channel_layer.group_discard)(
self.group_name,
self.channel_name
) self.ssh.close() def receive(self, text_data=None):
text_data = json.loads(text_data) if text_data.get('flag') == 'resize':
self.ssh.resize_pty(cols=text_data['cols'], rows=text_data['rows'])
else:
self.ssh.shell(data=text_data.get('data', '')) def ssh_message(self, event):
self.send(text_data=json.dumps(
event['message']
))

在connect连接建立时新建一条记录,存储主机、用户、group_namechannel_name以及初始窗口的colsrows信息,同时标记is_connecting为True,这里的group_name命名与文章『堡垒机的核心武器:WebSSH录像实现』中我们定义的录像文件名规则一致,另外将这篇文章中新建录像记录的操作从SSHBridge.record中给转到了连接建立的connect中来,更合理也更方便

在disconnect连接关闭时,将is_connecting标记为False,这样我们在前端页面上就可以根据这个标记来判断WebSSH是否正在连接,如果连接则展示监控强制结束按钮,否则展示播放命令提取按钮

同时添加个ssh_message方法,用来接收发送到组的数据

到这里,我们已经将WebSSH改造成了支持layer的模式,那么接下来就是要在用户点击监控的时候将用户与服务端建立的连接channel加入到上述group中

新建一个名为MonitorConsumer的consumer,主要用来处理监控连接

class MonitorConsumer(WebsocketConsumer):
def connect(self):
pk = self.scope['url_route']['kwargs'].get('id')
self.group_name = Record.objects.get(id=pk).group async_to_sync(self.channel_layer.group_add)(
self.group_name,
self.channel_name
) self.accept() # 判断用户已经结束了这个webssh连接时就关闭监控
self.connecting = Record.objects.get(id=pk).is_connecting
if not self.connecting:
self.close() def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard)(
self.group_name,
self.channel_name
) self.close() def receive(self, text_data=None):
pass def ssh_message(self, event):
self.send(text_data=json.dumps(
event['message']
))

MonitorConsumer与SSHConsumer有两个地方不一样,其一是SSHConsumer中我们直接新生成了个group_name,而MonitorConsumer中需要在connect时获取到要监控的ID,然后通过ID拿到group_name,将monitor连接加入到这个group,其二是监控只能看,不能操作,所以也不需要前端发送数据的term.on和Consumer的receive处理数据

最后需要修改SSHBridge方法中发送给websocket的指令,从self.websocket.send改为发送到group的模式,如下

async_to_sync(self.websocket.channel_layer.group_send)(
self.group_name,
{
'type': 'ssh.message',
'message': message
}
)

至此,监控功能就算完成了,什么?前端页面怎么弄?参考下之前的WebSSH界面,几乎可以完全复制

踢用户下线

踢用户下线就比较简单了,逻辑是点击页面上的强制结束按钮,给后端view发送个请求带上这条记录的ID,view拿到ID后,通过ID找到group_name,然后向group发送disconnect消息,这个group里的所有channel在收到disconnect消息后就会断开连接了

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer async_to_sync(get_channel_layer().group_send)(
Record.objects.get(id=pk).group,
{'type': 'disconnect'}
)

演示与说明

所有实现环环相扣,单看这一篇文章可能云里雾里,不知所云,但你如果能把这个系列文章都给看下的话,我想实现个简单的堡垒机应该没有问题吧,更重要的是你会对websocket以及django中的Channels有着更加深刻的理解和运用

原本只是想给我最牛X的Alodi系统添加个WebSSH,可以方便开发或测试在项目运行过程中出现问题时提供一个快速的调试途径,没想到这竟然写了一个系列,实现了这么多有趣好玩儿的功能

最后想起了这句成语:有意栽花花不开,无心插柳柳成荫,真是奇妙~


相关文章推荐阅读:

堡垒机WebSSH进阶之实时监控和强制下线的更多相关文章

  1. 主机管理+堡垒机系统开发:webssh(十)

    一.安装shellinabox 1.安装依赖工具 yum install git openssl-devel pam-devel zlib-devel autoconf automake libtoo ...

  2. IronFort---基于Django和Websocket的堡垒机

    WebSSH有很多,基于Django的Web服务也有很多,使用Paramiko在Python中进行SSH访问的就更多了.但是通过gevent将三者结合起来,实现通过浏览器访问的堡垒机就很少见了.本文将 ...

  3. 开发基于Django和Websocket的堡垒机

    WebSSH有很多,基于Django的Web服务也有很多,使用Paramiko在Python中进行SSH访问的就更多了.但是通过gevent将三者结合起来,实现通过浏览器访问的堡垒机就很少见了.本文将 ...

  4. 堡垒机环境-jumpserver部署

    1:安装数据库 这里是提前安装,也可以不安装,在安装jumpserver主程序的时候,他会询问你是否安装 yum -y install ncurses-devel cmake echo 'export ...

  5. CentOS7 开源跳板机(堡垒机) Jumpserver

    开源跳板机(堡垒机)Jumpserver 环境 CentOS 7   x64       关闭 selinux  firewalld jumpserver: 172.24.0.14 testserve ...

  6. jumpserver 堡垒机环境搭建(图文详解)

    摘要: Jumpserver 是一款由python编写开源的跳板机(堡垒机)系统,实现了跳板机应有的功能.基于ssh协议来管理,客户端无需安装agent. 特点: 完全开源,GPL授权 Python编 ...

  7. Python---初识堡垒机

    在学习堡垒机之前,我们需要首先了解下Python的paramiko模块,该模块机遇SSH用于连接远程服务器并执行相关操作. SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接: ...

  8. jumpserver 堡垒机环境搭建(图文具体解释)

    Jumpserver 是一款由python编写开源的跳板机(堡垒机)系统,实现了跳板机应有的功能.基于ssh协议来管理,客户端无需安装agent. 特点: 全然开源,GPL授权 Python编写.容易 ...

  9. python之堡垒机(第九天)

    本节作业: 通过使用paramiko和sqlalchemy实现堡垒机功能 主要功能实现: 1.用户登录堡垒机后,无需知道密码或密钥可以SSH登录远端服务器: 2.用户对一个组内所有主机批量执行指定命令 ...

随机推荐

  1. 关于JQUery.parseJSON()函数的知识札记

    JSON数据也许大家都很陌生,而对我来讲属于半成品,由于项目问题,做web虽然用的是JSON数据格式传输,但是关于解析这一块还真不知道该注意什么,更不知道它是如何解析的,由于最近要把串口通信协议与此一 ...

  2. 模板(ac):启发式合并

    首先说明一点:线段树合并不是启发式合并. 启发式合并的大概内容就是:把小的数据结构按照这个数据结构的正常插入方法,一个一个地暴力塞进去. 而线段树合并显然不是这个东西. 这道题的题解太烂了,所以耽误了 ...

  3. LCD 调试总结

    (1) 液晶显示模式 并行:MCU接口.RGB接口.Vysnc接口 串行:SPI接口.MDDI接口 (2) 屏幕颜色 实质上即为色阶的概念.色阶是表示手机液晶显示屏亮度强弱的指数标准,也就是通常所说的 ...

  4. 可保图片不变形的object-fit

    Object-fit 我们有时候浏览一些网站的时候,偶尔会遇到这种情况:  明显它喵的形变了,尤其是这种这么业余的失误,还是出现在一个专门做图片的网站上. 产生这种现象的原因是:图片写了固定的宽高,这 ...

  5. powerdesigner连接Mysql进行反向工程并生成word文档图文教程

    1 软件版本 windows7 64位 powerdesigner 15.1 Mysql 5.1.56 mysql-connector-odbc-3.51.30-winx64 对于mysql-conn ...

  6. 树莓派debian配置lamp[解决Apache不显示php网页]

    Apache + MySql + Php. 1.安装Apache Apache可以用下面的命令来安装 sudo apt-get install apache2 Apache默认路径是/var/www/ ...

  7. 【Java】面向对象之继承

    多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可.其中如图中所示,食草动物.食肉动物.兔子.羊.狮子.豹都可以称为子类,动物类称为父 ...

  8. 领扣(LeetCode)Fizz Buzz 个人题解

    写一个程序,输出从 1 到 n 数字的字符串表示. 1. 如果 n 是3的倍数,输出“Fizz”: 2. 如果 n 是5的倍数,输出“Buzz”: 3.如果 n 同时是3和5的倍数,输出 “FizzB ...

  9. PostGIS 结合Openlayers以及Geoserver实现最短路径分析(三)

    接上篇,前面在ArcMap中和Postgis中将数据都已经进行了预处理. 接下来回到Geoserver中,进行数据发布. 1.新建工作区 2.填写完工作区信息 3.打开数据存储,添加新的数据存储 4. ...

  10. Session.run() & Tensor.eval()

    如果有一个Tensor t,在使用t.eval()时,等价于: tf.get_defaut_session().run(t) t = tf.constant(42.0) sess = tf.Sessi ...