+
+
%block>
diff --git a/server/www/teleport/webroot/app/base/configs.py b/server/www/teleport/webroot/app/base/configs.py
index 912586a..a06f215 100644
--- a/server/www/teleport/webroot/app/base/configs.py
+++ b/server/www/teleport/webroot/app/base/configs.py
@@ -533,6 +533,10 @@ class AppConfig(BaseAppConfig):
self.sys.storage.keep_log = 0
if not self.sys.storage.is_exists('keep_record'):
self.sys.storage.keep_record = 0
+ if not self.sys.storage.is_exists('cleanup_hour'):
+ self.sys.storage.cleanup_hour = 4
+ if not self.sys.storage.is_exists('cleanup_minute'):
+ self.sys.storage.cleanup_minute = 30
self.sys.loaded = True
diff --git a/server/www/teleport/webroot/app/controller/__init__.py b/server/www/teleport/webroot/app/controller/__init__.py
index 498118c..eb4fae3 100644
--- a/server/www/teleport/webroot/app/controller/__init__.py
+++ b/server/www/teleport/webroot/app/controller/__init__.py
@@ -217,10 +217,12 @@ controllers = [
(r'/system/get-logs', system.DoGetLogsHandler),
# - 系统配置页面
(r'/system/config', system.ConfigHandler),
- # - [json] 系统配置-发送测试邮件
- (r'/system/send-test-mail', system.DoSendTestMailHandler),
# - [json] 系统配置-保存配置
(r'/system/save-cfg', system.DoSaveCfgHandler),
+ # - [json] 系统配置-发送测试邮件
+ (r'/system/send-test-mail', system.DoSendTestMailHandler),
+ # - [json] 系统配置-清理存储空间
+ (r'/system/cleanup-storage', system.DoCleanupStorageHandler),
# - [json] 获取服务器时间
(r'/system/get-time', system.DoGetTimeHandler),
diff --git a/server/www/teleport/webroot/app/controller/record.py b/server/www/teleport/webroot/app/controller/record.py
deleted file mode 100644
index d355095..0000000
--- a/server/www/teleport/webroot/app/controller/record.py
+++ /dev/null
@@ -1,238 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import ctypes
-import json
-import os
-import platform
-
-from app.base.configs import get_cfg
-from app.module import record
-from app.module import user
-from app.base.controller import TPBaseAdminAuthHandler, TPBaseAdminAuthJsonHandler
-import tornado.web
-
-
-def get_free_space_bytes(folder):
- """ Return folder/drive free space (in bytes)
- """
- if platform.system() == 'Windows':
- _free_bytes = ctypes.c_ulonglong(0)
- _total_bytes = ctypes.c_ulonglong(0)
- ctypes.windll.kernel32.GetDiskFreeSpaceExW(folder, None, ctypes.pointer(_total_bytes), ctypes.pointer(_free_bytes))
- total_bytes = _total_bytes.value
- free_bytes = _free_bytes.value
- else:
- try:
- st = os.statvfs(folder)
- total_bytes = st.f_blocks * st.f_frsize
- free_bytes = st.f_bavail * st.f_frsize
- except:
- total_bytes = 0
- free_bytes = 0
-
- return total_bytes, free_bytes
-
-
-class LogHandler(TPBaseAdminAuthHandler):
- def get(self):
- if not get_cfg().core.detected:
- total_size = 0
- free_size = 0
- else:
- total_size, free_size = get_free_space_bytes(get_cfg().core.replay_path)
-
- param = {
- 'user_list': user.get_user_list(with_admin=True),
- 'total_size': total_size,
- 'free_size': free_size,
- }
-
- self.render('log/index.mako', page_param=json.dumps(param))
-
-
-class RecordHandler(TPBaseAdminAuthHandler):
- def get(self, protocol, record_id):
- protocol = int(protocol)
- if protocol == 1:
- return
- elif protocol == 2:
- self.render('log/record.mako', record_id=record_id)
-
-
-# class PlayRdpHandler(TPBaseAdminAuthHandler):
-# def get(self, ip, record_id):
-# # protocol = int(protocol)
-# # if protocol == 1:
-# # return
-# # elif protocol == 2:
-# # self.render('log/record.mako', record_id=record_id)
-# # return
-# # pass
-# filename = os.path.join(cfg.base.replay_path, 'replay', 'rdp', '{}'.format(record_id), 'tp-rdp.tpr')
-
-class ReplayStaticFileHandler(tornado.web.StaticFileHandler):
- def initialize(self, path, default_filename=None):
- super().initialize(path, default_filename)
- self.root = get_cfg().core.replay_path
- # self.default_filename = default_filename
-
-
-class ComandLogHandler(TPBaseAdminAuthHandler):
- def get(self, protocol, record_id):
-
- header = record.read_record_head(record_id)
- if header is None:
- return self.write_json(-3, '操作失败')
-
- # ret = dict()
- # ret['header'] = header
- # return self.write_json(0, data=ret)
-
- param = dict()
- param['header'] = header
- param['count'] = 0
- param['op'] = list()
-
- cmd_type = 0 # 0 = ssh, 1 = sftp
- protocol = int(protocol)
- if protocol == 1:
- pass
- elif protocol == 2:
- record_path = os.path.join(get_cfg().core.replay_path, 'ssh', '{:06d}'.format(int(record_id)))
- file_info = os.path.join(record_path, 'tp-ssh-cmd.txt')
- try:
- file = open(file_info, 'r')
- data = file.readlines()
- for i in range(len(data)):
- if 0 == i:
- cmd = data[i][22:-1]
- if 'SFTP INITIALIZE' == cmd:
- cmd_type = 1
- continue
- if cmd_type == 0:
- param['op'].append({'t': data[i][1:20], 'c': data[i][22:-1]})
- else:
- cmd_info = data[i][22:-1].split(':')
- if len(cmd_info) != 4:
- continue
- param['op'].append({'t': data[i][1:20], 'c': cmd_info[0], 'p1': cmd_info[2], 'p2': cmd_info[3]})
- except:
- pass
- param['count'] = len(param['op'])
-
- if cmd_type == 0:
- self.render('log/record-ssh-cmd.mako', page_param=json.dumps(param))
- else:
- self.render('log/record-sftp-cmd.mako', page_param=json.dumps(param))
-
-
-class RecordGetHeader(TPBaseAdminAuthJsonHandler):
- def post(self):
- args = self.get_argument('args', None)
- if args is not None:
- args = json.loads(args)
- else:
- return self.write_json(-1, '参数错误')
-
- record_id = args['id']
-
- header = record.read_record_head(record_id)
- if header is None:
- return self.write_json(-3, '操作失败')
-
- ret = dict()
- ret['header'] = header
- return self.write_json(0, data=ret)
-
-
-class RecordGetInfo(TPBaseAdminAuthJsonHandler):
- def post(self):
- args = self.get_argument('args', None)
- if args is not None:
- args = json.loads(args)
- else:
- return self.write_json(-1, '参数错误')
-
- record_id = args['id']
- file_id = args['file_id']
-
- data = record.read_record_info(record_id, file_id)
- if data is None:
- return self.write_json(-3, '操作失败')
-
- return self.write_json(0, data=data)
-
-
-class DeleteLog(TPBaseAdminAuthJsonHandler):
- # TODO: 用户可能会批量删除大量录像文件,因此io操作可能会比较耗时,这里应该改为异步方式。
- def post(self):
- args = self.get_argument('args', None)
- if args is not None:
- args = json.loads(args)
- else:
- return self.write_json(-1, '参数错误')
-
- log_list = args['log_list']
-
- if not record.delete_log(log_list):
- return self.write_json(-3, '操作失败')
-
- return self.write_json(0)
-
-
-class LogList(TPBaseAdminAuthJsonHandler):
- def post(self):
- filter = dict()
- order = dict()
- order['name'] = 'host_id'
- order['asc'] = True
- limit = dict()
- limit['page_index'] = 0
- limit['per_page'] = 25
-
- args = self.get_argument('args', None)
- if args is not None:
- args = json.loads(args)
-
- tmp = list()
- _filter = args['filter']
- if _filter is not None:
- for i in _filter:
- if i == 'user_name':
- _x = _filter[i].strip()
- if _x == '全部':
- tmp.append(i)
-
- if i == 'search':
- _x = _filter[i].strip()
- if len(_x) == 0:
- tmp.append(i)
- continue
-
- for i in tmp:
- del _filter[i]
-
- filter.update(_filter)
-
- _limit = args['limit']
- if _limit['page_index'] < 0:
- _limit['page_index'] = 0
- if _limit['per_page'] < 10:
- _limit['per_page'] = 10
- if _limit['per_page'] > 100:
- _limit['per_page'] = 100
-
- limit.update(_limit)
-
- _order = args['order']
- if _order is not None:
- order['name'] = _order['k']
- order['asc'] = _order['v']
-
- total, log_list = user.get_log_list(filter, _limit)
- ret = dict()
- ret['page_index'] = limit['page_index']
- ret['total'] = total
- ret['data'] = log_list
-
- return self.write_json(0, data=ret)
diff --git a/server/www/teleport/webroot/app/controller/system.py b/server/www/teleport/webroot/app/controller/system.py
index bfae37a..1a608f6 100644
--- a/server/www/teleport/webroot/app/controller/system.py
+++ b/server/www/teleport/webroot/app/controller/system.py
@@ -12,6 +12,8 @@ from app.base.controller import TPBaseHandler, TPBaseJsonHandler
from app.base.logger import *
from app.const import *
from app.model import syslog
+from app.model import record
+from app.base.core_server import core_service_async_post_http
class DoGetTimeHandler(TPBaseJsonHandler):
@@ -21,16 +23,27 @@ class DoGetTimeHandler(TPBaseJsonHandler):
class ConfigHandler(TPBaseHandler):
+ @tornado.gen.coroutine
def get(self):
ret = self.check_privilege(TP_PRIVILEGE_SYS_CONFIG)
if ret != TPE_OK:
return
+ cfg = get_cfg()
+
+ # core_detected = False
+ req = {'method': 'get_config', 'param': []}
+ _yr = core_service_async_post_http(req)
+ code, ret_data = yield _yr
+ if code != TPE_OK:
+ cfg.update_core(None)
+ else:
+ cfg.update_core(ret_data)
+
if not get_cfg().core.detected:
total_size = 0
free_size = 0
else:
- # total_size, free_size = get_free_space_bytes(get_cfg().core.replay_path)
total_size, _, free_size = shutil.disk_usage(get_cfg().core.replay_path)
param = {
@@ -182,46 +195,6 @@ class DoGetLogsHandler(TPBaseJsonHandler):
return self.write_json(0, data=ret)
-class DoSendTestMailHandler(TPBaseJsonHandler):
- @tornado.gen.coroutine
- def post(self):
- ret = self.check_privilege(TP_PRIVILEGE_SYS_CONFIG)
- if ret != TPE_OK:
- return
-
- args = self.get_argument('args', None)
- if args is None:
- return self.write_json(TPE_PARAM)
- try:
- args = json.loads(args)
- except:
- return self.write_json(TPE_JSON_FORMAT)
-
- try:
- _server = args['server']
- _port = int(args['port'])
- _ssl = args['ssl']
- _sender = args['sender']
- _password = args['password']
- _recipient = args['recipient']
- except:
- return self.write_json(TPE_PARAM)
-
- code, msg = yield mail.tp_send_mail(
- _recipient,
- '您好!\n\n这是一封测试邮件,仅用于验证系统的邮件发送模块工作是否正常。\n\n请忽略本邮件。',
- subject='测试邮件',
- sender='Teleport Server <{}>'.format(_sender),
- server=_server,
- port=_port,
- use_ssl=_ssl,
- username=_sender,
- password=_password
- )
-
- self.write_json(code, message=msg)
-
-
class DoSaveCfgHandler(TPBaseJsonHandler):
def post(self):
ret = self.check_privilege(TP_PRIVILEGE_SYS_CONFIG)
@@ -285,7 +258,74 @@ class DoSaveCfgHandler(TPBaseJsonHandler):
else:
return self.write_json(err)
+ if 'storage' in args:
+ _cfg = args['storage']
+ _keep_log = _cfg['keep_log']
+ _keep_record = _cfg['keep_record']
+ _cleanup_hour = _cfg['cleanup_hour']
+ _cleanup_minute = _cfg['cleanup_minute']
+ err = system_model.save_config(self, '更新存储策略设置', 'storage', _cfg)
+ if err == TPE_OK:
+ get_cfg().sys.storage.keep_log = _keep_log
+ get_cfg().sys.storage.keep_record = _keep_record
+ get_cfg().sys.storage.cleanup_hour = _cleanup_hour
+ get_cfg().sys.storage.cleanup_minute = _cleanup_minute
+ else:
+ return self.write_json(err)
+
return self.write_json(TPE_OK)
except:
log.e('\n')
self.write_json(TPE_FAILED)
+
+
+class DoSendTestMailHandler(TPBaseJsonHandler):
+ @tornado.gen.coroutine
+ def post(self):
+ ret = self.check_privilege(TP_PRIVILEGE_SYS_CONFIG)
+ if ret != TPE_OK:
+ return
+
+ args = self.get_argument('args', None)
+ if args is None:
+ return self.write_json(TPE_PARAM)
+ try:
+ args = json.loads(args)
+ except:
+ return self.write_json(TPE_JSON_FORMAT)
+
+ try:
+ _server = args['server']
+ _port = int(args['port'])
+ _ssl = args['ssl']
+ _sender = args['sender']
+ _password = args['password']
+ _recipient = args['recipient']
+ except:
+ return self.write_json(TPE_PARAM)
+
+ code, msg = yield mail.tp_send_mail(
+ _recipient,
+ '您好!\n\n这是一封测试邮件,仅用于验证系统的邮件发送模块工作是否正常。\n\n请忽略本邮件。',
+ subject='测试邮件',
+ sender='Teleport Server <{}>'.format(_sender),
+ server=_server,
+ port=_port,
+ use_ssl=_ssl,
+ username=_sender,
+ password=_password
+ )
+
+ self.write_json(code, message=msg)
+
+
+class DoCleanupStorageHandler(TPBaseJsonHandler):
+ @tornado.gen.coroutine
+ def post(self):
+ ret = self.check_privilege(TP_PRIVILEGE_SYS_CONFIG)
+ if ret != TPE_OK:
+ return
+
+ code, msg = yield record.cleanup_storage(self)
+
+ self.write_json(code, data=msg)
diff --git a/server/www/teleport/webroot/app/model/record.py b/server/www/teleport/webroot/app/model/record.py
index ca2fd61..160f3a6 100644
--- a/server/www/teleport/webroot/app/model/record.py
+++ b/server/www/teleport/webroot/app/model/record.py
@@ -11,6 +11,7 @@ from app.base.configs import get_cfg
from app.base.db import get_db, SQL
from app.base.logger import log
from app.base.utils import tp_timestamp_utc_now
+import tornado.gen
def get_records(sql_filter, sql_order, sql_limit, sql_restrict, sql_exclude):
@@ -253,66 +254,140 @@ def delete_log(log_list):
def session_fix():
- try:
- db = get_db()
+ db = get_db()
+ sql_list = []
- sql_list = []
+ sql = 'UPDATE `{dbtp}record` SET state={new_state}, time_end={time_end} WHERE state={old_state};' \
+ ''.format(dbtp=db.table_prefix, new_state=TP_SESS_STAT_ERR_RESET, old_state=TP_SESS_STAT_RUNNING, time_end=tp_timestamp_utc_now())
+ sql_list.append(sql)
- sql = 'UPDATE `{dbtp}record` SET state={new_state}, time_end={time_end} WHERE state={old_state};' \
- ''.format(dbtp=db.table_prefix, new_state=TP_SESS_STAT_ERR_RESET, old_state=TP_SESS_STAT_RUNNING, time_end=tp_timestamp_utc_now())
- sql_list.append(sql)
- # if not db.exec(sql):
- # ret = False
- sql = 'UPDATE `{dbtp}record` SET state={new_state},time_end={time_end} WHERE state={old_state};' \
- ''.format(dbtp=db.table_prefix, new_state=TP_SESS_STAT_ERR_START_RESET, old_state=TP_SESS_STAT_STARTED, time_end=tp_timestamp_utc_now())
- sql_list.append(sql)
- return db.transaction(sql_list)
- # if not db.exec(sql):
- # ret = False
- # return ret
- except:
- log.e('\n')
- return False
+ sql = 'UPDATE `{dbtp}record` SET state={new_state},time_end={time_end} WHERE state={old_state};' \
+ ''.format(dbtp=db.table_prefix, new_state=TP_SESS_STAT_ERR_START_RESET, old_state=TP_SESS_STAT_STARTED, time_end=tp_timestamp_utc_now())
+ sql_list.append(sql)
+ return db.transaction(sql_list)
def session_begin(sid, user_id, host_id, acc_id, user_username, acc_username, host_ip, conn_ip, conn_port, client_ip, auth_type, protocol_type, protocol_sub_type):
- try:
- db = get_db()
- sql = 'INSERT INTO `{}record` (sid,user_id,host_id,acc_id,state,user_username,host_ip,conn_ip,conn_port,client_ip,acc_username,auth_type,protocol_type,protocol_sub_type,time_begin,time_end) ' \
- 'VALUES ("{sid}",{user_id},{host_id},{acc_id},0,"{user_username}","{host_ip}","{conn_ip}",{conn_port},"{client_ip}","{acc_username}",{auth_type},{protocol_type},{protocol_sub_type},{time_begin},0)' \
- ';'.format(db.table_prefix,
- sid=sid, user_id=user_id, host_id=host_id, acc_id=acc_id, user_username=user_username, host_ip=host_ip, conn_ip=conn_ip, conn_port=conn_port,
- client_ip=client_ip, acc_username=acc_username, auth_type=auth_type, protocol_type=protocol_type, protocol_sub_type=protocol_sub_type,
- time_begin=tp_timestamp_utc_now())
+ db = get_db()
+ sql = 'INSERT INTO `{}record` (sid,user_id,host_id,acc_id,state,user_username,host_ip,conn_ip,conn_port,client_ip,acc_username,auth_type,protocol_type,protocol_sub_type,time_begin,time_end) ' \
+ 'VALUES ("{sid}",{user_id},{host_id},{acc_id},0,"{user_username}","{host_ip}","{conn_ip}",{conn_port},"{client_ip}","{acc_username}",{auth_type},{protocol_type},{protocol_sub_type},{time_begin},0)' \
+ ';'.format(db.table_prefix,
+ sid=sid, user_id=user_id, host_id=host_id, acc_id=acc_id, user_username=user_username, host_ip=host_ip, conn_ip=conn_ip, conn_port=conn_port,
+ client_ip=client_ip, acc_username=acc_username, auth_type=auth_type, protocol_type=protocol_type, protocol_sub_type=protocol_sub_type,
+ time_begin=tp_timestamp_utc_now())
- ret = db.exec(sql)
- if not ret:
- return TPE_DATABASE, 0
-
- record_id = db.last_insert_id()
- if record_id == -1:
- return TPE_DATABASE, 0
- else:
- return TPE_OK, record_id
-
- except:
- log.e('\n')
+ ret = db.exec(sql)
+ if not ret:
return TPE_DATABASE, 0
+ record_id = db.last_insert_id()
+ if record_id == -1:
+ return TPE_DATABASE, 0
+ else:
+ return TPE_OK, record_id
+
def session_update(record_id, state):
- try:
- db = get_db()
- sql = 'UPDATE `{}record` SET state={} WHERE id={};'.format(db.table_prefix, int(state), int(record_id))
- return db.exec(sql)
- except:
- return False
+ db = get_db()
+ sql = 'UPDATE `{}record` SET state={} WHERE id={};'.format(db.table_prefix, int(state), int(record_id))
+ return db.exec(sql)
def session_end(record_id, ret_code):
- try:
- db = get_db()
- sql = 'UPDATE `{}record` SET state={}, time_end={} WHERE id={};'.format(db.table_prefix, int(ret_code), tp_timestamp_utc_now(), int(record_id))
- return db.exec(sql)
- except:
- return False
+ db = get_db()
+ sql = 'UPDATE `{}record` SET state={}, time_end={} WHERE id={};'.format(db.table_prefix, int(ret_code), tp_timestamp_utc_now(), int(record_id))
+ return db.exec(sql)
+
+
+@tornado.gen.coroutine
+def cleanup_storage(handler):
+ # storage config
+ sto = get_cfg().sys.storage
+
+ db = get_db()
+ _now = tp_timestamp_utc_now()
+ msg = []
+ have_error = False
+
+ sto.keep_log = 5
+ sto.keep_record = 5
+
+ s = SQL(db)
+ chk_time = _now - sto.keep_log * 24 * 60 * 60
+
+ if sto.keep_log > 0:
+ # find out all sys-log to be remove
+ s.select_from('syslog', ['id'], alt_name='s')
+ s.where('s.log_time<{chk_time}'.format(chk_time=chk_time))
+ err = s.query()
+ if err != TPE_OK:
+ have_error = True
+ msg.append('清理系统日志时发生错误:无法获取系统日志信息!')
+ # return err, msg
+ else:
+ removed_log = len(s.recorder)
+ if 0 == removed_log:
+ msg.append('没有满足条件的系统日志需要清除!')
+ else:
+ s.reset().delete_from('syslog').where('log_time<{chk_time}'.format(chk_time=chk_time))
+ err = s.query()
+ if err != TPE_OK:
+ have_error = True
+ msg.append('清理系统日志时发生错误:无法清除指定的系统日志!')
+ else:
+ msg.append('{} 条系统日志已清除!'.format(removed_log))
+
+ if sto.keep_record > 0:
+ core_cfg = get_cfg().core
+ if not core_cfg.detected:
+ have_error = True
+ msg.append('清除指定会话录像失败:未能检测到核心服务!')
+ else:
+ replay_path = core_cfg.replay_path
+ if not os.path.exists(replay_path):
+ have_error = True
+ msg.append('清除指定会话录像失败:会话录像路径不存在({})!'.format(replay_path))
+ else:
+ # find out all record to be remove
+ s.reset().select_from('record', ['id', 'protocol_type'], alt_name='r')
+ s.where('r.time_begin<{chk_time}'.format(chk_time=chk_time))
+ err = s.query()
+ if err != TPE_OK:
+ have_error = True
+ msg.append('清除指定会话录像失败:无法获取会话录像信息!')
+ elif len(s.recorder) == 0:
+ msg.append('没有满足条件的会话录像需要清除!')
+ else:
+ record_removed = 0
+ for r in s.recorder:
+ if r.protocol_type == TP_PROTOCOL_TYPE_RDP:
+ path_remove = os.path.join(replay_path, 'rdp', '{:09d}'.format(r.id))
+ elif r.protocol_type == TP_PROTOCOL_TYPE_SSH:
+ path_remove = os.path.join(replay_path, 'ssh', '{:09d}'.format(r.id))
+ elif r.protocol_type == TP_PROTOCOL_TYPE_TELNET:
+ path_remove = os.path.join(replay_path, 'telnet', '{:09d}'.format(r.id))
+ else:
+ have_error = True
+ msg.append('会话录像记录编号 {},未知远程访问协议!'.format(r.id))
+ continue
+
+ if os.path.exists(path_remove):
+ print('remove path', path_remove)
+ try:
+ shutil.rmtree(path_remove)
+ except:
+ have_error = True
+ msg.append('会话录像记录 {} 清除失败,无法删除目录 {}!'.format(r.id, path_remove))
+
+ ss = SQL(db)
+ ss.delete_from('record').where('id={rid}'.format(rid=r.id))
+ ss.exec()
+
+ record_removed += 1
+
+ msg.append('{} 条会话录像数据已清除!'.format(record_removed))
+
+ if have_error:
+ return TPE_FAILED, msg
+ else:
+ return TPE_OK, msg