Skip to content

Commit de2d5c3

Browse files
committed
webssh添加命令记录
1 parent 0d37061 commit de2d5c3

25 files changed

+548
-431
lines changed
-19 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

Ops/routing.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
-------------------------------------------------
44
File Name: routing
55
Description:
6-
Author: Administrator
6+
Author: pythonzm
77
date: 2018/6/6
88
-------------------------------------------------
99
Change Activity:
@@ -14,10 +14,10 @@
1414
from channels.routing import ProtocolTypeRouter, URLRouter
1515
from channels.auth import AuthMiddlewareStack
1616
from projs.utils.log_websocket import LogConsumer
17-
from fort.utils.webssh_websocket import FortConsumer
17+
from fort.utils.webssh import FortConsumer
1818
from assets.utils.webssh import SSHConsumer
19-
from fort.utils.guacamole_websocket import GuacamoleConsumer
20-
from assets.utils.guacamole import AdminGuacamole
19+
from fort.utils.webguacamole import GuacamoleConsumer
20+
from assets.utils.webguacamole import AdminGuacamole
2121
from task.utils.ans_module_websocket import AnsModuleConsumer
2222
from task.utils.ans_playbook_websocket import AnsPlaybookConsumer
2323
from projs.utils.deploy_websocket import DeployConsumer
71 Bytes
Binary file not shown.

assets/models.py

+1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ class AdminRecord(models.Model):
264264
admin_login_status_time = models.CharField(max_length=16, verbose_name='登录时长')
265265
admin_record_file = models.CharField(max_length=256, verbose_name='操作记录')
266266
admin_record_mode = models.CharField(max_length=10, choices=record_modes, verbose_name='登录协议', default='ssh')
267+
admin_record_cmds = models.TextField(verbose_name='命令记录', default='')
267268

268269
class Meta:
269270
db_table = 'ops_admin_record'

assets/utils/webguacamole.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
-------------------------------------------------
4+
File Name: root_rdp
5+
Description:
6+
Author: Administrator
7+
date: 2019-01-21
8+
-------------------------------------------------
9+
Change Activity:
10+
2019-01-21:
11+
-------------------------------------------------
12+
"""
13+
from utils.guacamole import MyGuacamole
14+
from utils.crypt_pwd import CryptPwd
15+
from assets.models import ServerAssets
16+
17+
18+
class AdminGuacamole(MyGuacamole):
19+
def __init__(self, *args, **kwargs):
20+
super(AdminGuacamole, self).__init__(*args, **kwargs)
21+
self.server = ServerAssets.objects.select_related('assets').get(id=self.scope['path'].split('/')[3])
22+
self.ip = self.server.assets.asset_management_ip
23+
self.port = self.server.port
24+
self.username = self.server.username
25+
self.password = CryptPwd().decrypt_pwd(self.server.password)

assets/utils/webssh.py

+7-119
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,14 @@
11
# -*- coding: utf-8 -*-
2-
import paramiko
3-
import threading
4-
import time
5-
import os
6-
from socket import timeout
7-
from assets.tasks import admin_file
8-
from channels.generic.websocket import WebsocketConsumer
9-
from assets.models import ServerAssets, AdminRecord
10-
from django.conf import settings
2+
from utils.ssh import MySSH
113
from utils.crypt_pwd import CryptPwd
12-
from conf.logger import fort_logger
4+
from assets.models import ServerAssets
135

146

15-
class MyThread(threading.Thread):
16-
def __init__(self, chan):
17-
super(MyThread, self).__init__()
18-
self.chan = chan
19-
self._stop_event = threading.Event()
20-
self.start_time = time.time()
21-
self.current_time = time.strftime(settings.TIME_FORMAT)
22-
self.stdout = []
23-
24-
def stop(self):
25-
self._stop_event.set()
26-
27-
def run(self):
28-
while not self._stop_event.is_set() or not self.chan.chan.exit_status_ready():
29-
time.sleep(0.1)
30-
try:
31-
data = self.chan.chan.recv(1024)
32-
if data:
33-
str_data = data.decode('utf-8', 'ignore')
34-
self.chan.send(str_data)
35-
self.stdout.append([time.time() - self.start_time, 'o', str_data])
36-
except timeout:
37-
self.chan.send('\n由于长时间没有操作,连接已断开!', close=True)
38-
self.stdout.append([time.time() - self.start_time, 'o', '\n由于长时间没有操作,连接已断开!'])
39-
break
40-
41-
def record(self):
42-
record_path = os.path.join(settings.MEDIA_ROOT, 'admin_ssh_records', self.chan.scope['user'].username,
43-
time.strftime('%Y-%m-%d'))
44-
if not os.path.exists(record_path):
45-
os.makedirs(record_path, exist_ok=True)
46-
record_file_name = '{}.{}.cast'.format(self.chan.host_ip, time.strftime('%Y%m%d%H%M%S'))
47-
record_file_path = os.path.join(record_path, record_file_name)
48-
49-
header = {
50-
"version": 2,
51-
"width": self.chan.width,
52-
"height": self.chan.height,
53-
"timestamp": round(self.start_time),
54-
"title": "Demo",
55-
"env": {
56-
"TERM": os.environ.get('TERM'),
57-
"SHELL": os.environ.get('SHELL', '/bin/bash')
58-
},
59-
}
60-
61-
admin_file.delay(record_file_path, self.stdout, header)
62-
63-
login_status_time = time.time() - self.start_time
64-
if login_status_time >= 60:
65-
login_status_time = '{} m'.format(round(login_status_time / 60, 2))
66-
elif login_status_time >= 3600:
67-
login_status_time = '{} h'.format(round(login_status_time / 3660, 2))
68-
else:
69-
login_status_time = '{} s'.format(round(login_status_time))
70-
71-
try:
72-
AdminRecord.objects.create(
73-
admin_login_user=self.chan.scope['user'],
74-
admin_server=self.chan.host_ip,
75-
admin_remote_ip=self.chan.remote_ip,
76-
admin_start_time=self.current_time,
77-
admin_login_status_time=login_status_time,
78-
admin_record_file=record_file_path.split('media/')[1]
79-
)
80-
except Exception as e:
81-
fort_logger.error('数据库添加用户操作记录失败,原因:{}'.format(e))
82-
83-
84-
class SSHConsumer(WebsocketConsumer):
7+
class SSHConsumer(MySSH):
858
def __init__(self, *args, **kwargs):
869
super(SSHConsumer, self).__init__(*args, **kwargs)
87-
self.ssh = paramiko.SSHClient()
8810
self.server = ServerAssets.objects.select_related('assets').get(id=self.scope['path'].split('/')[3])
89-
self.host_ip = self.server.assets.asset_management_ip
90-
self.width = 150
91-
self.height = 30
92-
self.t1 = MyThread(self)
93-
self.remote_ip = self.scope['query_string'].decode('utf8')
94-
self.chan = None
95-
96-
def connect(self):
97-
if self.scope["user"].is_anonymous:
98-
self.close(code=1007)
99-
else:
100-
self.accept()
101-
102-
username = self.server.username
103-
try:
104-
self.ssh.load_system_host_keys()
105-
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
106-
self.ssh.connect(self.host_ip, int(self.server.port), username,
107-
CryptPwd().decrypt_pwd(self.server.password), timeout=5)
108-
except Exception as e:
109-
fort_logger.error('用户{}通过webssh连接{}失败!原因:{}'.format(username, self.host_ip, e))
110-
self.send('用户{}通过webssh连接{}失败!原因:{}'.format(username, self.host_ip, e))
111-
self.close()
112-
self.chan = self.ssh.invoke_shell(term='xterm', width=self.width, height=self.height)
113-
# 设置如果3分钟没有任何输入,就断开连接
114-
self.chan.settimeout(60 * 3)
115-
self.t1.setDaemon(True)
116-
self.t1.start()
117-
118-
def receive(self, text_data=None, bytes_data=None):
119-
self.chan.send(text_data)
120-
121-
def disconnect(self, close_code):
122-
try:
123-
self.t1.record()
124-
self.t1.stop()
125-
finally:
126-
self.ssh.close()
11+
self.ip = self.server.assets.asset_management_ip
12+
self.port = self.server.port
13+
self.username = self.server.username
14+
self.password = CryptPwd().decrypt_pwd(self.server.password)

assets/views.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,6 @@ def guacamole_terminal(request, pk):
358358
return render(request, 'assets/admin_guacamole.html', locals())
359359

360360

361-
# 已整合至系统日志
362361
@admin_auth
363362
def login_record(request):
364363
if request.method == 'GET':
@@ -382,7 +381,8 @@ def login_record(request):
382381
'admin_remote_ip': search_record.admin_remote_ip,
383382
'admin_start_time': search_record.admin_start_time,
384383
'admin_login_status_time': search_record.admin_login_status_time,
385-
'admin_record_mode': search_record.get_admin_record_mode_display()
384+
'admin_record_mode': search_record.get_admin_record_mode_display(),
385+
'admin_record_cmds': search_record.admin_record_cmds
386386
}
387387
records.append(record)
388388
return JsonResponse({'code': 200, 'records': records})

fort/models.py

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class FortRecord(models.Model):
7171
login_status_time = models.CharField(max_length=16, verbose_name='登录时长')
7272
record_file = models.CharField(max_length=256, verbose_name='操作记录')
7373
record_mode = models.CharField(max_length=10, choices=record_modes, verbose_name='登录协议', default='ssh')
74+
record_cmds = models.TextField(verbose_name='命令记录', default='')
7475

7576
class Meta:
7677
db_table = 'ops_fort_record'

fort/utils/guacamole_websocket.py

-131
This file was deleted.

fort/utils/webguacamole.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from utils.guacamole import MyGuacamole
2+
from assets.models import ServerAssets
3+
from fort.models import FortServerUser
4+
5+
6+
class GuacamoleConsumer(MyGuacamole):
7+
def __init__(self, *args, **kwargs):
8+
super(GuacamoleConsumer, self).__init__(*args, **kwargs)
9+
self.fort_server = ServerAssets.objects.select_related('assets').get(id=self.scope['path'].split('/')[3])
10+
self.fort_user = FortServerUser.objects.get(id=self.scope['path'].split('/')[4])
11+
self.ip = self.fort_server.assets.asset_management_ip
12+
self.username = self.fort_user.fort_username
13+
self.password = self.fort_user.fort_password
14+
15+
def connect(self):
16+
if self.scope["user"].is_anonymous:
17+
self.close(code=1007)
18+
else:
19+
self.accept('guacamole')
20+
21+
server_protocol = self.fort_user.fort_server.server_protocol
22+
if server_protocol == 'vnc':
23+
self.client.handshake(protocol=server_protocol,
24+
hostname=self.ip,
25+
port=self.fort_user.fort_vnc_port,
26+
password=self.password, width=self.width, height=self.height,
27+
dpi=self.dpi)
28+
elif server_protocol == 'rdp':
29+
self.client.handshake(protocol=server_protocol,
30+
hostname=self.ip, port=self.fort_server.port,
31+
password=self.fort_user.fort_password,
32+
username=self.username, width=self.width, height=self.height, dpi=self.dpi)
33+
self.send('0.,{0}.{1};'.format(len(self.group_name), self.group_name))
34+
self.guacamole_thread.setDaemon(True)
35+
self.guacamole_thread.start()

0 commit comments

Comments
 (0)