日志审计转移完成

pull/445/head
midoks 2 years ago
parent 6fc8803446
commit 5f85d41352
  1. 285
      class/core/logs_api.py
  2. 146
      route/static/app/logs.js
  3. 132
      route/templates/default/logs.html
  4. 2
      scripts/install.sh
  5. 2
      scripts/install_dev.sh
  6. 2
      scripts/update.sh
  7. 2
      scripts/update_dev.sh

@ -21,11 +21,15 @@ import re
import json
import pwd
from datetime import datetime
from flask import request
class logs_api:
__months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
'Jul': '07', 'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
def __init__(self):
pass
@ -61,3 +65,284 @@ class logs_api:
data['page'] = mw.getPage(_page)
return mw.getJson(data)
def getLogsTitle(self, log_name):
log_name = log_name.replace('.1', '')
if log_name in ['auth.log', 'secure'] or log_name.find('auth.') == 0:
return '授权日志'
if log_name in ['dmesg'] or log_name.find('dmesg') == 0:
return '内核缓冲区日志'
if log_name in ['syslog'] or log_name.find('syslog') == 0:
return '系统警告/错误日志'
if log_name in ['btmp']:
return '失败的登录记录'
if log_name in ['utmp', 'wtmp']:
return '登录和重启记录'
if log_name in ['lastlog']:
return '用户最后登录'
if log_name in ['yum.log']:
return 'yum包管理器日志'
if log_name in ['anaconda.log']:
return 'Anaconda日志'
if log_name in ['dpkg.log']:
return 'dpkg日志'
if log_name in ['daemon.log']:
return '系统后台守护进程日志'
if log_name in ['boot.log']:
return '启动日志'
if log_name in ['kern.log']:
return '内核日志'
if log_name in ['maillog', 'mail.log']:
return '邮件日志'
if log_name.find('Xorg') == 0:
return 'Xorg日志'
if log_name in ['cron.log']:
return '定时任务日志'
if log_name in ['alternatives.log']:
return '更新替代信息'
if log_name in ['debug']:
return '调试信息'
if log_name.find('apt') == 0:
return 'apt-get相关日志'
if log_name.find('installer') == 0:
return '系统安装相关日志'
if log_name in ['messages']:
return '综合日志'
return '{}日志'.format(log_name.split('.')[0])
def getAuditLogsFilesApi(self):
log_dir = '/var/log'
log_files = []
for log_file in os.listdir(log_dir):
log_suffix = log_file.split('.')[-1:]
if log_suffix[0] in ['gz', 'xz', 'bz2', 'asl']:
continue
if log_file in ['.', '..', 'faillog', 'fontconfig.log', 'unattended-upgrades', 'tallylog']:
continue
filename = os.path.join(log_dir, log_file)
if os.path.isfile(filename):
file_size = os.path.getsize(filename)
if not file_size:
continue
tmp = {
'name': log_file,
'size': file_size,
'log_file': filename,
'title': self.getLogsTitle(log_file),
'uptime': os.path.getmtime(filename)
}
log_files.append(tmp)
else:
for next_name in os.listdir(filename):
if next_name[-3:] in ['.gz', '.xz']:
continue
next_file = os.path.join(filename, next_name)
if not os.path.isfile(next_file):
continue
file_size = os.path.getsize(next_file)
if not file_size:
continue
log_name = '{}/{}'.format(log_file, next_name)
tmp = {
'name': log_name,
'size': file_size,
'log_file': next_file,
'title': self.getLogsTitle(log_name),
'uptime': os.path.getmtime(next_file)
}
log_files.append(tmp)
log_files = sorted(log_files, key=lambda x: x['name'], reverse=True)
return mw.getJson(log_files)
def getAuditFileApi(self):
log_name = request.form.get('log_name', '').strip()
return self.getAuditLogsName(log_name)
def __to_date2(self, date_str):
tmp = date_str.split()
s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1],
tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
return s_date
def __to_date3(self, date_str):
tmp = date_str.split()
s_date = str(datetime.now().year) + '-' + \
self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
return s_date
def __to_date4(self, date_str):
tmp = date_str.split()
s_date = str(datetime.now().year) + '-' + \
self.__months.get(tmp[0], tmp[0]) + '-' + tmp[1] + ' ' + tmp[2]
return s_date
def getAuditLast(self, log_name):
# 获取日志
cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format(
'/var/log/' + log_name)
result = mw.execShell(cmd)
lastlog_list = []
for _line in result[0].split("\n"):
if not _line:
continue
tmp = {}
sp_arr = _line.split()
tmp['用户'] = sp_arr[0]
if sp_arr[0] == 'runlevel':
tmp['来源'] = sp_arr[4]
tmp['端口'] = ' '.join(sp_arr[1:4])
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[5:])) + ' ' + ' '.join(sp_arr[-2:])
elif sp_arr[0] in ['reboot', 'shutdown']:
tmp['来源'] = sp_arr[3]
tmp['端口'] = ' '.join(sp_arr[1:3])
if sp_arr[-3] == '-':
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-3:])
else:
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-2:])
elif sp_arr[1] in ['tty1', 'tty', 'tty2', 'tty3', 'hvc0', 'hvc1', 'hvc2'] or len(sp_arr) == 9:
tmp['来源'] = ''
tmp['端口'] = sp_arr[1]
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[2:])) + ' ' + ' '.join(sp_arr[-3:])
else:
tmp['来源'] = sp_arr[2]
tmp['端口'] = sp_arr[1]
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[3:])) + ' ' + ' '.join(sp_arr[-3:])
# tmp['_line'] = _line
lastlog_list.append(tmp)
# lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True)
return mw.returnJson(True, 'ok!', lastlog_list)
def getAuditLastLog(self):
cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username'''
result = mw.execShell(cmd)
lastlog_list = []
for _line in result[0].split("\n"):
if not _line:
continue
tmp = {}
sp_arr = _line.split()
tmp['用户'] = sp_arr[0]
# tmp['_line'] = _line
if _line.find('Never logged in') != -1:
tmp['最后登录时间'] = '0'
tmp['最后登录来源'] = '-'
tmp['最后登录端口'] = '-'
lastlog_list.append(tmp)
continue
tmp['最后登录来源'] = sp_arr[2]
tmp['最后登录端口'] = sp_arr[1]
tmp['最后登录时间'] = self.__to_date2(' '.join(sp_arr[3:]))
lastlog_list.append(tmp)
lastlog_list = sorted(lastlog_list, key=lambda x: x[
'最后登录时间'], reverse=True)
for i in range(len(lastlog_list)):
if lastlog_list[i]['最后登录时间'] == '0':
lastlog_list[i]['最后登录时间'] = '从未登录过'
return mw.returnData(True, 'ok!', lastlog_list)
def parseAuditFile(self, log_name, result):
log_list = []
is_string = True
for _line in result.split("\n"):
if not _line.strip():
continue
if log_name.find('sa/sa') == -1:
if _line[:3] in self.__months:
_msg = _line[16:]
_tmp = _msg.split(": ")
_act = ''
if len(_tmp) > 1:
_act = _tmp[0]
_msg = _tmp[1]
else:
_msg = _tmp[0]
_line = {
"时间": self.__to_date4(_line[:16].strip()),
"角色": _act,
"事件": _msg
}
is_string = False
elif _line[:2] in ['19', '20', '21', '22', '23', '24']:
_msg = _line[19:]
_tmp = _msg.split(" ")
_act = _tmp[1]
_msg = ' '.join(_tmp[2:])
_line = {
"时间": _line[:19].strip(),
"角色": _act,
"事件": _msg
}
is_string = False
elif log_name.find('alternatives') == 0:
_tmp = _line.split(": ")
_last = _tmp[0].split(" ")
_act = _last[0]
_msg = ' '.join(_tmp[1:])
_line = {
"时间": ' '.join(_last[1:]).strip(),
"角色": _act,
"事件": _msg
}
is_string = False
else:
if not is_string:
if type(_line) != dict:
continue
log_list.append(_line)
return log_list
def getAuditLogsName(self, log_name):
# print(log_name)
if log_name in ['wtmp', 'btmp', 'utmp'] or log_name.find('wtmp') == 0 or log_name.find('btmp') == 0 or log_name.find('utmp') == 0:
return self.getAuditLast(log_name)
if log_name.find('lastlog') == 0:
return self.getAuditLastLog()
if log_name.find('sa/sa') == 0:
if log_name.find('sa/sar') == -1:
return mw.execShell("sar -f /var/log/{}".format(log_name))[0]
log_dir = '/var/log'
log_file = log_dir + '/' + log_name
if not os.path.exists(log_file):
return mw.returnData(False, '日志文件不存在!')
result = mw.getLastLine(log_file, 100)
try:
log_list = self.parseAuditFile(log_name, result)
_string = []
_dict = []
_list = []
for _line in log_list:
if isinstance(_line, str):
_string.append(_line.strip())
elif isinstance(_line, dict):
_dict.append(_line)
elif isinstance(_line, list):
_list.append(_line)
else:
continue
_str_len = len(_string)
_dict_len = len(_dict)
_list_len = len(_list)
if _str_len > _dict_len + _list_len:
return "\n".join(_string)
elif _dict_len > _str_len + _list_len:
return mw.returnData(True, 'ok!', _dict)
else:
return mw.returnData(True, 'ok!', _list)
except:
return mw.returnData(True, 'ok!', result)

@ -1,12 +1,17 @@
$(document).ready(function(){
getLogs(1);
logsLoad();
});
function changeLogsViewH(){
var l = $(window).height();
$('.container-fluid .tab-view-box').css('max-height',l-80-60);
$('.container-fluid .tab-view-box').css('height',l-80-40);
$('#panelLogs').css('height',l-80-40-50);
$('#logAudit .logAuditTab').css('height',l-80-40-50);
$('#logAudit .logAuditContent').css('height',l-80-40-50);
}
function logsLoad(){
@ -14,9 +19,34 @@ function logsLoad(){
$(window).resize(function(){
changeLogsViewH();
});
getLogs(1);
}
$('#cutTab .tabs-item').click(function(){
var type = $(this).data('name');
$('#cutTab .tabs-item').removeClass('active');
$(this).addClass('active');
$('.tab-view-box .tab-con').addClass('hide').removeClass('show').removeClass('w-full');
$('#'+type).addClass('show').addClass('w-full');
switch(type){
case 'panelLogs':
getLogs(1);
break;
case 'logAudit':
getAuditLogsFiles();
break;
}
});
$('#panelLogs .refresh').click(function(){
getLogs(1);
});
@ -26,7 +56,119 @@ $('#panelLogs .clear').click(function(){
});
function getAuditLogsFiles(){
$.post('/logs/get_audit_logs_files',{}, function(data) {
var option = '';
for (var i = 0; i < data.length; i++) {
var tip = data[i]['name'] +' - '+data[i]['title'] + '(' + toSize(data[i]['size']) + ')';
if (i==0){
option += '<div class="logAuditItem active" title="'+tip+'" data-file="'+data[i]['name']+'">'+tip+'</div>';
} else {
option += '<div class="logAuditItem" title="'+tip+'" data-file="'+data[i]['name']+'">'+tip+'</div>';
}
}
$("#logAudit .logAuditTab").html(option);
getAuditFile(data[0]['name']);
$('#logAudit .logAuditItem').click(function(){
$('#logAudit .logAuditItem').removeClass('active');
$(this).addClass('active');
getAuditFile($(this).data('file'));
});
},'json');
}
function getAuditFile(log_name){
$.post('/logs/get_audit_file',{log_name:log_name}, function(data) {
// console.log(data);
try{
if (typeof(data) == 'object'){
var plist = data.data;
var pre_html ='<div id="logAuditTable" style="position: relative;display: block;">\
<div class="tootls_group tootls_top">\
<div class="pull-left">\
<button type="button" title="刷新列表" class="refresh btn btn-success btn-sm mr5"><span>刷新列表</span></button>\
</div>\
<!-- <div class="pull-right">\
<div class="logs_search" style="position: relative;">\
<input type="text" class="search_input" style="" placeholder="请输入来源/端口/角色/事件">\
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>\
</div>\
</div> --> \
</div>\
<div class="divtable mtb10" style="max-height: 83px;">\
<table class="table table-hover">\
<thead style="position: relative;z-index: 1;">\
<tr>\
<th><span data-index="0"><span>用户</span></span></th>\
<th><span data-index="1"><span>来源</span></span></th>\
<th><span data-index="2"><span>端口</span></span></th>\
<th><span data-index="3"><span>时间</span></span></th>\
</tr>\
</thead>\
<tbody>\
<tr><td><span>root</span></td>\
<td><span>117.139.193.29</span></td>\
<td><span>pts/0</span></td>\
<td><span>2023-08-25 13:27 still logged in</span></td>\
</tr>\
</tbody>\
</table>\
</div>\
<div class="tootls_group tootls_bottom">\
<div class="pull-left"></div>\
<div class="pull-right">\
</div>\
</div>\
</div>'
// var pre_html = '<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">\
// <thead><tr><th>时间</th><th>角色</th><th>事件</th></tr></thead>\
// <tbody></tbody>\
// </table>';
$('#logAudit .logAuditContent').html(pre_html);
if (plist.length>0){
var tmp = plist[0];
var thead = '';
tbody += '<tr>'
for (var i in tmp) {
tbody+='<th>'+ i + '</th>';
}
tbody += '</tr>';
$('#logAudit .logAuditContent thead').html(tbody);
}
var tbody = '';
for (var i = 0; i < plist.length; i++) {
tbody += '<tr>';
for (var vv in plist[i]) {
tbody+= '<td>'+ plist[i][vv] + '</td>'
}
tbody += '</tr>';
}
$('#logAudit .logAuditContent tbody').html(tbody);
$('#logAudit .refresh').click(function(){
getAuditFile(log_name);
});
}
if (typeof(data) == 'string'){
var cc = '<div id="logAuditPre">\
<pre style="height: 100%; background-color: rgb(51, 51, 51); color: rgb(255, 255, 255); overflow-x: hidden; overflow-wrap: break-word; white-space: pre-wrap;"><code>'+data+'</code></pre>\
</div>';
$('#logAudit .logAuditContent').html(cc);
}
} catch (e) {
layer.msg(str(e),{icon:2,time:10000,shade: [0.3, '#000']});
}
});
}
function getLogs(page,search) {
search = search == undefined ? '':search;

@ -8,6 +8,80 @@
#operationLog tbody tr td:nth-child(2){width:130px;}
#operationLog thead th:nth-child(4),
#operationLog tbody tr td:nth-child(4){width:150px;}
.logAuditContent {
padding: 0 0 0 15px;
margin-left: 15px;
width: 100%;
flex: 1;
border-left: 1px solid #ececec;
}
.tootls_group .logs_search .search_input {
height: 30px;
line-height: 30px;
border-radius: 2px;
border: 1px solid #ccc;
outline: none;
padding-left: 8px;
vertical-align: top;
width: 230px;
}
.tootls_group .logs_search .glyphicon-search {
height: 28px;
line-height: 28px;
padding: 0 10px;
color: #888;
position: absolute;
right: 0;
font-size: 14px;
cursor: pointer;
}
#logAudit .tootls_group {
justify-content: space-between;
}
#logAudit .tootls_group, #panelRun .tootls_group.tools_bottom {
display: flex;
flex-wrap: wrap;
height: auto;
line-height: inherit;
}
.logAuditTabContent {
display: flex;
height: 100%;
}
.logAuditTabContent .logAuditTab {
border: 1px solid #ececec;
overflow: auto;
}
.logAuditTabContent .logAuditTab .logAuditItem {
height: 35px;
line-height: 35px;
padding: 0 10px;
border-bottom: 1px solid #ececec;
cursor: pointer;
overflow: hidden;
}
.logAuditTabContent .logAuditTab .logAuditItem:hover, .logAuditTabContent .logAuditTab .logAuditItem.active {
background-color: #f2f2f2;
}
#logAuditTable thead th:nth-child(1),
#logAuditTable tbody tr td:nth-child(1){width:;}
#logAuditTable thead th:nth-child(2),
#logAuditTable tbody tr td:nth-child(2){width:;}
#logAuditTable thead th:nth-child(3),
#logAuditTable tbody tr td:nth-child(3){width:;}
#logAuditTable thead th:nth-child(4),
#logAuditTable tbody tr td:nth-child(4){width:300px;}
</style>
<div class="main-content" style="min-height: 389px;">
@ -20,7 +94,7 @@
</div>
<div class="bgw mtb15 pd15 tab-view-box firewall-tab-view">
<div class="bgw mtb15 pd15 tab-view-box firewall-tab-view" style="overflow: hidden;">
<!-- logs start -->
<div class="tab-con show w-full" id="panelLogs" style="padding: 0px;">
@ -53,9 +127,63 @@
</div>
<!-- logs end -->
</div>
<div class="tab-con hide" id="logAudit" style="height: auto;">
<div class="logAuditTabContent">
<div class="logAuditTab" style="width: 300px;overflow: hidden;overflow-y: auto;">
<!--
<div class="logAuditItem active" title="wtmp - 登录和重启记录" data-file="/wtmp">
wtmp - 登录和重启记录(4.88 KB)
</div>
-->
</div>
<div class="logAuditContent" style="overflow: hidden;overflow-y: auto;">
<div id="logAuditTable" class="bt_table" style="display: block;">
<div class="tootls_group tootls_top">
<div class="pull-left">
<button type="button" title="刷新列表" class="btn btn-success btn-sm mr5"><span>刷新列表</span></button>
</div>
<div class="pull-right">
<div class="logs_search" style="position: relative;">
<input type="text" class="search_input" style="" placeholder="请输入来源/端口/角色/事件">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
</div>
</div>
</div>
<div class="divtable mtb10" style="max-height: 83px;">
<table class="table table-hover">
<thead style="position: relative;z-index: 1;">
<tr>
<th><span data-index="0"><span>用户</span></span></th>
<th><span data-index="1"><span>来源</span></span></th>
<th><span data-index="2"><span>端口</span></span></th>
<th><span data-index="3"><span>时间</span></span></th>
</tr>
</thead>
<tbody>
<tr><td><span>root</span></td>
<td><span>117.139.193.29</span></td>
<td><span>pts/0</span></td>
<td><span>2023-08-25 13:27 still logged in</span></td>
</tr>
</tbody>
</table>
</div>
<div class="tootls_group tootls_bottom">
<div class="pull-left"></div>
<div class="pull-right">
<div class="page"><span class="Pcount">第 1 页</span></div>
</div>
</div>
</div>
<div id="logAuditPages" class="page" style="display: flex; justify-content: flex-end;"></div>
<div id="logAuditPre" style="display: none;"><pre style="height: 188px; background-color: rgb(51, 51, 51); color: rgb(255, 255, 255); overflow-x: hidden; overflow-wrap: break-word; white-space: pre-wrap;"><code></code></pre></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/static/app/logs.js?v={{config.version}}"></script>
{% endblock %}

@ -163,7 +163,7 @@ endTime=`date +%s`
((outTime=(${endTime}-${startTime})/60))
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
} 1> >(tee mw-install.log) 2>&1
} 1> >(tee /var/logs/mw-install.log) 2>&1
echo -e "\nInstall completed. If error occurs, please contact us with the log file mw-install.log ."
echo "安装完毕,如果出现错误,请带上同目录下的安装日志 mw-install.log 联系我们反馈."

@ -160,7 +160,7 @@ endTime=`date +%s`
((outTime=(${endTime}-${startTime})/60))
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
} 1> >(tee mw-install.log) 2>&1
} 1> >(tee /var/logs/mw-install.log) 2>&1
echo -e "\nInstall completed. If error occurs, please contact us with the log file mw-install.log ."
echo "安装完毕,如果出现错误,请带上同目录下的安装日志 mw-install.log 联系我们反馈."

@ -108,4 +108,4 @@ endTime=`date +%s`
((outTime=($endTime-$startTime)/60))
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
} 1> >(tee /www/server/mdserver-web/logs/mw-update.log) 2>&1
} 1> >(tee /var/logs/mw-update.log) 2>&1

@ -119,4 +119,4 @@ endTime=`date +%s`
((outTime=($endTime-$startTime)/60))
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
} 1> >(tee /www/server/mdserver-web/logs/mw-update.log) 2>&1
} 1> >(tee /var/logs/mw-update.log) 2>&1
Loading…
Cancel
Save