mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-17 21:25:47 +02:00
470 lines
18 KiB
Python
470 lines
18 KiB
Python
#coding: utf-8
|
||
# + -------------------------------------------------------------------
|
||
# | 宝塔Linux面板
|
||
# + -------------------------------------------------------------------
|
||
# | Copyright (c) 2015-2016 宝塔软件(http:#bt.cn) All rights reserved.
|
||
# + -------------------------------------------------------------------
|
||
# | Author: hezhihong <272267659@@qq.cn>
|
||
# + -------------------------------------------------------------------
|
||
import public, os, time
|
||
from logsModel.base import logsBase
|
||
|
||
try:
|
||
from BTPanel import session
|
||
except:
|
||
pass
|
||
#英文转月份缩写
|
||
month_list = {
|
||
"Jan": "1",
|
||
"Feb": "2",
|
||
"Mar": "3",
|
||
"Apr": "4",
|
||
"May": "5",
|
||
"Jun": "6",
|
||
"Jul": "7",
|
||
"Aug": "8",
|
||
"Sept": "9",
|
||
"Sep": "9",
|
||
"Oct": "10",
|
||
"Nov": "11",
|
||
"Dec": "12"
|
||
}
|
||
|
||
|
||
class main(logsBase):
|
||
def __init__(self):
|
||
self.__messages_file = "/var/log/"
|
||
self.__ftp_backup_path = public.get_backup_path() + '/pure-ftpd/'
|
||
if not os.path.isdir(self.__ftp_backup_path):
|
||
public.ExecShell('mkdir -p {}'.format(self.__ftp_backup_path))
|
||
self.__script_py = public.get_panel_path() + '/script/ftplogs_cut.py'
|
||
|
||
def get_file_list(self, path, is_bakcup=False):
|
||
"""
|
||
@name 取所有messages日志文件
|
||
@param path: 日志文件路径
|
||
@return: 返回日志文件列表
|
||
"""
|
||
files = os.listdir(path)
|
||
if is_bakcup:
|
||
file_name_list = [{
|
||
"file": "/var/log/pure-ftpd.log",
|
||
"time": int(time.time())
|
||
}]
|
||
else:
|
||
file_name_list = []
|
||
for i in files:
|
||
tmp_dict = {}
|
||
if not i: continue
|
||
file_path = path + i
|
||
tmp_dict['file'] = file_path
|
||
if is_bakcup:
|
||
if os.path.isfile(file_path) and i.find('pure-ftpd.log') != -1:
|
||
tmp_dict['time'] = int(
|
||
public.to_date(
|
||
times=os.path.basename(file_path).split('_')[0] +
|
||
' 00:00:00'))
|
||
file_name_list.append(tmp_dict)
|
||
else:
|
||
if os.path.isfile(file_path) and i.find('messages') != -1:
|
||
tmp_dict['time'] = int(
|
||
public.to_date(
|
||
times=os.path.basename(file_path).split('-')[1] +
|
||
' 00:00:00'))
|
||
file_name_list.append(tmp_dict)
|
||
file_name_list = sorted(file_name_list,
|
||
key=lambda x: x['time'],
|
||
reverse=False)
|
||
return file_name_list
|
||
|
||
def set_ftp_log(self, get):
|
||
"""
|
||
@name 开启、关闭、获取日志状态
|
||
@author hezhihong
|
||
@param get.exec_name 执行的动作
|
||
"""
|
||
if not hasattr(get, 'exec_name'):
|
||
return public.returnMsg(False, 'The parameter is incorrect!')
|
||
conf_path = '/etc/rsyslog.conf'
|
||
conf = public.readFile(conf_path)
|
||
import re
|
||
search_str = r"ftp\.\*.*\t*.*\t*.*-/var/log/pure-ftpd.log"
|
||
search_str_two = "ftp.none"
|
||
rep_str = '\nftp.*\t\t-/var/log/pure-ftpd.log\n'
|
||
result = re.search(search_str, conf)
|
||
#获取日志状态
|
||
if get.exec_name == 'getlog':
|
||
if result:
|
||
return_result = 'start'
|
||
else:
|
||
return_result = 'stop'
|
||
return public.returnMsg(True, return_result)
|
||
#开启日志审计
|
||
elif get.exec_name == 'start':
|
||
if result:
|
||
conf = conf.replace(search_str, rep_str)
|
||
else:
|
||
conf += rep_str
|
||
#禁止ftp日志写入/var/log/messages
|
||
|
||
d_conf = conf[conf.rfind('info;'):]
|
||
d_conf = d_conf[:d_conf.find('/')]
|
||
s_conf = d_conf.replace(',', ';')
|
||
if s_conf.find(search_str_two) == -1:
|
||
str_index = s_conf.rfind(';')
|
||
s_conf = s_conf[:str_index +
|
||
1] + search_str_two + s_conf[str_index + 1:]
|
||
conf = conf.replace(d_conf, s_conf)
|
||
self.add_crontab()
|
||
#关闭日志审计
|
||
elif get.exec_name == 'stop':
|
||
if result:
|
||
conf = re.sub(search_str, '', conf)
|
||
#取消禁止ftp日志写入/var/log/messages
|
||
if conf.find(search_str_two) != -1:
|
||
conf = conf.replace(search_str_two, '')
|
||
for i in [';;', ',,', ';,', ',;']:
|
||
if conf.find(i) != -1: conf = conf.replace(i, '')
|
||
self.del_crontab()
|
||
public.writeFile(conf_path, conf)
|
||
public.ExecShell('systemctl restart rsyslog')
|
||
return public.returnMsg(True, 'successfully set')
|
||
|
||
def get_format_time(self, englist_time):
|
||
"""
|
||
@name 时间英文转换
|
||
"""
|
||
chinanese_time = ''
|
||
try:
|
||
for i in month_list.keys():
|
||
if i in englist_time:
|
||
tmp_time = englist_time.replace(i, month_list[i])
|
||
tmp_time = tmp_time.split()
|
||
chinanese_time = '{}-{} {}'.format(tmp_time[0], tmp_time[1],
|
||
tmp_time[2])
|
||
break
|
||
return chinanese_time
|
||
except:
|
||
return chinanese_time
|
||
|
||
def get_login_log(self, get):
|
||
"""
|
||
@name 取登录日志
|
||
@author hezhihong
|
||
@param get.user_name ftp用户名
|
||
return
|
||
"""
|
||
|
||
search_str = 'pure-ftpd:'
|
||
search_str2 = 'pure-ftpd['
|
||
if not hasattr(get, 'user_name'):
|
||
return public.returnMsg(False, 'The parameter is incorrect!')
|
||
args = public.dict_obj()
|
||
args.exec_name = 'getlog'
|
||
file_name = self.__ftp_backup_path
|
||
is_backup = True
|
||
if self.set_ftp_log(get) == 'stop':
|
||
file_name = self.__messages_file
|
||
is_backup = False
|
||
file_list = self.get_file_list(file_name, is_backup)
|
||
data = []
|
||
sortid = 0
|
||
tmp_dict = {}
|
||
login_all = []
|
||
for file in file_list:
|
||
|
||
if not os.path.isfile(file['file']): continue
|
||
conf = public.readFile(file['file'])
|
||
lines = conf.split('\n')
|
||
for line in lines:
|
||
if not line: continue
|
||
login_info = {}
|
||
if search_str not in line and search_str2 not in line:
|
||
continue
|
||
tmp_value = ' is now logged in'
|
||
info = line[:line.find(search_str)].strip()
|
||
if not info:
|
||
info = line[:line.find(search_str2)].strip()
|
||
hostname = info.split()[-1]
|
||
exec_time = info.split(hostname)[0].strip()
|
||
exec_time = self.get_format_time(exec_time)
|
||
ip = line[line.find('(') + 1:line.find(')')].split('@')[1]
|
||
|
||
#取登录成功日志
|
||
if tmp_value in line:
|
||
user = line.split(tmp_value)[0].strip().split()[-1]
|
||
if user == '?' or user != get.user_name: continue
|
||
dict_index = '{}__{}'.format(user, ip)
|
||
if dict_index not in tmp_dict:
|
||
tmp_dict[dict_index] = []
|
||
tmp_dict[dict_index].append(exec_time)
|
||
|
||
#取登出日志
|
||
tmp_value = '[INFO] Logout.'
|
||
tmp_value_two = 'Timeout - try typing a little faster next time'
|
||
if tmp_value in line or tmp_value_two in line:
|
||
user = line[line.find('(') +
|
||
1:line.find(')')].split('@')[0]
|
||
if user == '?' or user != get.user_name: continue
|
||
dict_index = '{}__{}'.format(user, ip)
|
||
try:
|
||
login_info['out_time'] = exec_time
|
||
login_info['in_time'] = tmp_dict[dict_index][0]
|
||
login_info['user'] = user
|
||
login_info['ip'] = ip
|
||
login_info['status'] = 'Success' #0为登录失败,1为登录成功
|
||
login_info['sortid'] = sortid
|
||
login_all.append(login_info)
|
||
tmp_dict[dict_index] = []
|
||
sortid += 1
|
||
except:
|
||
pass
|
||
#取登录失败日志
|
||
tmp_value = 'Authentication failed for user'
|
||
if tmp_value in line:
|
||
user = line.split(tmp_value)[-1].replace('[', '').replace(
|
||
']', '').strip()
|
||
if user == '?' or user != get.user_name: continue
|
||
login_info['user'] = user
|
||
login_info['ip'] = ip
|
||
login_info['status'] = 'Failure' #0为登录失败,1为登录成功
|
||
login_info['in_time'] = exec_time
|
||
login_info['out_time'] = exec_time
|
||
login_info['sortid'] = sortid
|
||
login_all.append(login_info)
|
||
sortid += 1
|
||
|
||
if tmp_dict:
|
||
for item in tmp_dict.keys():
|
||
if not tmp_dict[item]: continue
|
||
info = {
|
||
"status": "login successful",
|
||
"in_time": tmp_dict[item][0],
|
||
"out_time": "connecting",
|
||
"user": item.split('__')[0],
|
||
"ip": item.split('__')[1],
|
||
"sortid": sortid
|
||
}
|
||
sortid += 1
|
||
login_all.append(info)
|
||
#搜索过滤
|
||
if login_all and 'search' in get and get.search and get.search.strip():
|
||
for info in login_all:
|
||
try:
|
||
search_str = str(get.search).strip().lower()
|
||
# public.writeFile('/tmp/aa.aa', get.search)
|
||
if info['ip'].find(search_str) != -1 or info['user'].lower(
|
||
).find(search_str) != -1 or info['status'].find(
|
||
search_str) != -1 or info['in_time'].find(
|
||
search_str) != -1:
|
||
data.append(info)
|
||
elif info['out_time'] and info['out_time'].find(
|
||
search_str) != -1:
|
||
data.append(info)
|
||
except:
|
||
pass
|
||
else:
|
||
for info2 in login_all:
|
||
data.append(info2)
|
||
|
||
data = sorted(data, key=lambda x: x['sortid'], reverse=True)
|
||
return self.get_page(data, get)
|
||
|
||
def get_page(self, data, get):
|
||
"""
|
||
@name 取分页
|
||
@author hezhihong
|
||
@param data 需要分页的数据 list
|
||
@param get.p 第几页
|
||
@return 指定分页数据
|
||
"""
|
||
# 包含分页类
|
||
import page
|
||
# 实例化分页类
|
||
page = page.Page()
|
||
|
||
info = {}
|
||
info['count'] = len(data)
|
||
info['row'] = 10
|
||
info['p'] = 1
|
||
if hasattr(get, 'p'):
|
||
info['p'] = int(get['p'])
|
||
info['uri'] = {}
|
||
info['return_js'] = ''
|
||
# 获取分页数据
|
||
result = {}
|
||
result['page'] = page.GetPage(info, limit='1,2,3,4,5,8')
|
||
n = 0
|
||
result['data'] = []
|
||
for i in range(info['count']):
|
||
if n >= page.ROW: break
|
||
if i < page.SHIFT: continue
|
||
n += 1
|
||
result['data'].append(data[i])
|
||
return result
|
||
|
||
def get_action_log(self, get):
|
||
"""
|
||
@name 取操作日志
|
||
@author hezhihong
|
||
@param get.user_name ftp用户名
|
||
return {"upload":[],"download":[],"rename":[],"delete":[]}
|
||
"""
|
||
search_str = 'pure-ftpd:'
|
||
args = public.dict_obj()
|
||
args.exec_name = 'getlog'
|
||
file_name = self.__ftp_backup_path
|
||
is_backup = True
|
||
if self.set_ftp_log(get) == 'stop':
|
||
file_name = self.__messages_file
|
||
is_backup = False
|
||
file_list = self.get_file_list(file_name, is_backup)
|
||
if not hasattr(get, 'user_name'):
|
||
return public.returnMsg(False, 'The parameter is incorrect!')
|
||
data = []
|
||
tmp_data = []
|
||
sortid = 0
|
||
for file in file_list:
|
||
if not os.path.isfile(file['file']): continue
|
||
conf = public.readFile(file['file'])
|
||
lines = conf.split('\n')
|
||
for line in lines:
|
||
if not line: continue
|
||
action_info = {}
|
||
if search_str not in line: continue
|
||
|
||
tmp_v = line.split(search_str)
|
||
hostname = tmp_v[0].strip().split()[3].strip()
|
||
action_time = tmp_v[0].replace(hostname, '').strip()
|
||
action_info['time'] = self.get_format_time(action_time)
|
||
|
||
upload_value = ' uploaded '
|
||
download_value = ' downloaded '
|
||
rename_value = 'successfully renamed or moved:'
|
||
delete_value = ' Deleted '
|
||
ip = line[line.find('(') + 1:line.find(')')].split('@')[1]
|
||
action_info['ip'] = ip
|
||
action_info['type'] = ''
|
||
#取操作用户
|
||
user = ''
|
||
if upload_value in line or download_value in line or rename_value in line or delete_value in line:
|
||
user = line[line.find('(') +
|
||
1:line.find(')')].split('@')[0]
|
||
action_info['sortid'] = sortid
|
||
sortid = sortid + 1
|
||
if not user or user != get.user_name: continue
|
||
#取上传日志
|
||
if (get.type == 'all'
|
||
or get.type == 'upload') and upload_value in line:
|
||
line_list = line.split()
|
||
upload_index = line_list.index('uploaded')
|
||
# action_info['file'] = line_list[upload_index - 1].replace(
|
||
# '//', '/')
|
||
action_info['file'] = line[line.find(']') +
|
||
1:line.rfind('(')].replace(
|
||
'uploaded',
|
||
'').replace('//',
|
||
'/').strip()
|
||
action_info['type'] = 'upload'
|
||
tmp_data.append(action_info)
|
||
#取下载日志
|
||
if (get.type == 'all'
|
||
or get.type == 'download') and download_value in line:
|
||
line_list = line.split()
|
||
upload_index = line_list.index('downloaded')
|
||
action_info['file'] = line_list[upload_index - 1].replace(
|
||
'//', '/')
|
||
action_info['type'] = 'download'
|
||
tmp_data.append(action_info)
|
||
#取重命名日志
|
||
if (get.type == 'all'
|
||
or get.type == 'rename') and rename_value in line:
|
||
action_info['file'] = line.split(rename_value)[1].replace(
|
||
'->', 'Renamed to').strip().replace('//', '/')
|
||
action_info['type'] = 'rename'
|
||
tmp_data.append(action_info)
|
||
#取删除日志
|
||
if (get.type == 'all'
|
||
or get.type == 'delete') and delete_value in line:
|
||
action_info['file'] = line.split()[-1].strip().replace(
|
||
'//', '/')
|
||
action_info['type'] = 'delete'
|
||
tmp_data.append(action_info)
|
||
# f.close
|
||
#搜索过滤
|
||
if tmp_data and 'search' in get and get.search and get.search.strip():
|
||
for info in tmp_data:
|
||
search_str = str(get.search).strip().lower()
|
||
if info['ip'].find(search_str) != -1 or info['file'].lower(
|
||
).find(search_str) != -1 or info['type'].find(
|
||
search_str) != -1 or info['time'].find(
|
||
search_str) != -1 or get.user_name.lower().find(
|
||
search_str) != -1:
|
||
data.append(info)
|
||
else:
|
||
for info2 in tmp_data:
|
||
data.append(info2)
|
||
data = sorted(data, key=lambda x: x['sortid'], reverse=True)
|
||
return self.get_page(data, get)
|
||
|
||
def del_crontab(self):
|
||
"""
|
||
@name 删除项目定时清理任务
|
||
@auther hezhihong<2022-10-31>
|
||
@return
|
||
"""
|
||
cron_name = '[Do not delete] FTP audit log cutting task'
|
||
cron_path = public.GetConfigValue('setup_path') + '/cron/'
|
||
cron_list = public.M('crontab').where("name=?", (cron_name, )).select()
|
||
if cron_list:
|
||
for i in cron_list:
|
||
if not i: continue
|
||
cron_echo = public.M('crontab').where(
|
||
"id=?", (i['id'], )).getField('echo')
|
||
args = {"id": i['id']}
|
||
import crontab
|
||
crontab.crontab().DelCrontab(args)
|
||
del_cron_file = cron_path + cron_echo
|
||
public.ExecShell(
|
||
"crontab -u root -l| grep -v '{}'|crontab -u root -".
|
||
format(del_cron_file))
|
||
|
||
def add_crontab(self):
|
||
"""
|
||
@name 构造日志切割任务
|
||
"""
|
||
python_path = ''
|
||
try:
|
||
python_path = public.ExecShell('which btpython')[0].strip("\n")
|
||
except:
|
||
try:
|
||
python_path = public.ExecShell('which python')[0].strip("\n")
|
||
except:
|
||
pass
|
||
if not python_path: return False
|
||
if not public.M('crontab').where('name=?',
|
||
('[Do not delete] FTP audit log cutting task', )).count():
|
||
cmd = '{} {}'.format(python_path, self.__script_py)
|
||
args = {
|
||
"name": "[Do not delete] FTP audit log cutting task",
|
||
"type": 'day',
|
||
"where1": '',
|
||
"hour": '0',
|
||
"minute": '1',
|
||
"sName": "",
|
||
"sType": 'toShell',
|
||
"notice": '0',
|
||
"notice_channel": '',
|
||
"save": '',
|
||
"save_local": '1',
|
||
"backupTo": '',
|
||
"sBody": cmd,
|
||
"urladdress": ''
|
||
}
|
||
import crontab
|
||
res = crontab.crontab().AddCrontab(args)
|
||
if res and "id" in res.keys():
|
||
return True
|
||
return False
|
||
return True
|