堡壘機WebSSH進階之實時監控和強制下線

NO IMAGE

這個功能我可以不用,但你不能沒有

前幾篇文章實現了對物理機虛擬機以及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內監控者就能實時收到消息了,大概流程變化如下圖所示

堡壘機WebSSH進階之實時監控和強制下線

接下來看下具體實現,以下所有代碼均是在這篇文章的基礎上進行說明講解:『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'}
)

演示與說明

堡壘機WebSSH進階之實時監控和強制下線

所有實現環環相扣,單看這一篇文章可能雲裡霧裡,不知所云,但你如果能把這個系列文章都給看下的話,我想實現個簡單的堡壘機應該沒有問題吧,更重要的是你會對websocket以及django中的Channels有著更加深刻的理解和運用

原本只是想給我最牛X的Alodi系統添加個WebSSH,可以方便開發或測試在項目運行過程中出現問題時提供一個快速的調試途徑,沒想到這竟然寫了一個系列,實現了這麼多有趣好玩兒的功能

最後想起了這句成語:有意栽花花不開,無心插柳柳成蔭,真是奇妙~


堡壘機WebSSH進階之實時監控和強制下線

相關文章推薦閱讀:

相關文章

javaWeb學習總結——文件上傳、下載

阿里巴巴業務平臺-招聘“前端、客戶端”開發,虛位以待

mobx源碼解讀(四):講講autorun和reaction

自定義View實現一個日期選擇器