313 lines
12 KiB
Python
313 lines
12 KiB
Python
#coding: utf-8
|
||
# +-------------------------------------------------------------------
|
||
# | YakPanel
|
||
# +-------------------------------------------------------------------
|
||
# | Copyright (c) 2015-2020 YakPanel(https://www.yakpanel.com) All rights reserved.
|
||
# +-------------------------------------------------------------------
|
||
# | Author: baozi <baozi@yakpanel.com>
|
||
# | Author: baozi
|
||
# +-------------------------------------------------------------------
|
||
import sys,os,re,json
|
||
|
||
import public,panelPush, time
|
||
from datetime import datetime, timedelta
|
||
|
||
try:
|
||
from YakPanel import cache
|
||
except :
|
||
from cachelib import SimpleCache
|
||
cache = SimpleCache()
|
||
|
||
class base_push:
|
||
|
||
# 版本信息 目前无作用
|
||
def get_version_info(self, get=None):
|
||
raise NotImplementedError
|
||
|
||
# 格式化返回执行周期, 目前无作用
|
||
def get_push_cycle(self, data: dict):
|
||
return data
|
||
|
||
# 获取模块推送参数
|
||
def get_module_config(self, get: public.dict_obj):
|
||
raise NotImplementedError
|
||
|
||
# 获取模块配置项
|
||
def get_push_config(self, get: public.dict_obj):
|
||
# 其实就是配置信息,没有也会从全局配置文件push.json中读取
|
||
raise NotImplementedError
|
||
|
||
# 写入推送配置文件
|
||
def set_push_config(self, get: public.dict_obj):
|
||
raise NotImplementedError
|
||
|
||
# 删除推送配置
|
||
def del_push_config(self, get: public.dict_obj):
|
||
# 从配置中删除信息,并做一些您想做的事,如记日志
|
||
raise NotImplementedError
|
||
|
||
# 无意义???
|
||
def get_total(self):
|
||
return True
|
||
|
||
# 检查并获取推送消息,返回空时,不做推送, 传入的data是配置项
|
||
def get_push_data(self, data, total):
|
||
# data 内容
|
||
# index : 时间戳 time.time()
|
||
# 消息 以类型为key, 以内容为value, 内容中包含title 和msg
|
||
# push_keys: 列表,发送了信息的推送任务的id,用来验证推送任务次数() 意义不大
|
||
raise NotImplementedError
|
||
|
||
class tamper_push(base_push):
|
||
__tamper_path = "{}/tamper".format(public.get_setup_path())
|
||
__total_path = "{}/total/total.json".format(__tamper_path)
|
||
__config_file = "{}/tamper.conf".format(__tamper_path)
|
||
__push_conf = "{}/class/push/push.json".format(public.get_panel_path())
|
||
__logs_path = "{}/logs".format(__tamper_path)
|
||
|
||
def __init__(self) -> None:
|
||
self.__push = panelPush.panelPush()
|
||
try:
|
||
config = public.readFile(self.__config_file)
|
||
config_dict = json.loads(config)
|
||
self.__config = {}
|
||
for i in config_dict["paths"]:
|
||
self.__config[i["pid"]] = i
|
||
except:
|
||
self.__config = None
|
||
|
||
|
||
# 版本信息 目前无作用
|
||
def get_version_info(self, get=None):
|
||
data = {}
|
||
data['ps'] = 'Tamper-proof for Enterprise'
|
||
data['version'] = '1.0'
|
||
data['date'] = '2023-03-24'
|
||
data['author'] = 'YakPanel'
|
||
data['help'] = 'http://www.yakpanel.com/bbs'
|
||
return data
|
||
|
||
# 获取模块推送参数
|
||
def get_module_config(self, get: public.dict_obj):
|
||
data = []
|
||
item = self.__push.format_push_data(push = ["mail",'dingding','weixin',"feishu", "wx_account", "tg"], project = 'tamper',type = '')
|
||
item['cycle'] = 30
|
||
item['title'] = 'Tamper-proof for Enterprise'
|
||
data.append(item)
|
||
return data
|
||
|
||
|
||
# 获取模块配置项
|
||
def get_push_config(self, get: public.dict_obj):
|
||
id = get.id
|
||
# 其实就是配置信息,没有也会从全局配置文件push.json中读取
|
||
push_list = self.__push._get_conf()
|
||
|
||
if not id in push_list["tamper_push"]:
|
||
res_data = public.returnMsg(False, 'The specified configuration was not found.')
|
||
res_data['code'] = 100
|
||
return res_data
|
||
result = push_list["tamper_push"][id]
|
||
return result
|
||
|
||
|
||
# 写入推送配置文件
|
||
def set_push_config(self, get: public.dict_obj):
|
||
if self.__config is None:
|
||
return public.returnMsg(False, public.lang('Configuration error, cannot be set, please try use the command fix: "bash /www/server/panel/plugin/tamper_core/install.sh install"')
|
||
)
|
||
try:
|
||
id = int(get.id)
|
||
if id != 0 and id not in self.__config:
|
||
return public.returnMsg(False, public.lang("No protection directory specified"))
|
||
except ValueError:
|
||
return public.returnMsg(False, public.lang("No protection directory specified"))
|
||
|
||
pdata = json.loads(get.data)
|
||
data = self.__push._get_conf()
|
||
if "tamper_push" not in data: data["tamper_push"] = {}
|
||
if not str(id) in data["tamper_push"]:
|
||
if id != 0:
|
||
public.WriteLog("Tamper-proof for Enterprise","Protected directory: {} set tampering alarm ".format(self.__config[id]["path"]))
|
||
else:
|
||
public.WriteLog("Tamper-proof for Enterprise","All directory is set, tampering alarm ")
|
||
else:
|
||
if id != 0:
|
||
public.WriteLog("Tamper-proof for Enterprise","Protected directory: {} Changed alert config".format(self.__config[id]["path"]))
|
||
else:
|
||
public.WriteLog("Tamper-proof for Enterprise"," All directory alarm config changed")
|
||
self._del_today_push_count(id)
|
||
pdata["status"] = True
|
||
pdata["pid"] = id
|
||
pdata["project"] = "tamper_core"
|
||
data["tamper_push"][str(id)] = pdata
|
||
return data
|
||
|
||
# 删除推送配置
|
||
def del_push_config(self, get: public.dict_obj):
|
||
# 从配置中删除信息,并做一些您想做的事,如记日志
|
||
id = get.id
|
||
data = self.__push._get_conf()
|
||
if str(id).strip() in data["tamper_push"]:
|
||
del data["tamper_push"][id]
|
||
public.writeFile(self.__push_conf,json.dumps(data))
|
||
return public.returnMsg(True, public.lang("successfully deleted."))
|
||
|
||
# 无意义???
|
||
def get_total(self):
|
||
return True
|
||
|
||
# 检查并获取推送消息,返回空时,不做推送, 传入的data是配置项
|
||
def get_push_data(self, data, total):
|
||
# 返回内容
|
||
# index : 时间戳 time.time()
|
||
# 消息 以类型为key, 以内容为value, 内容中包含title 和msg
|
||
# push_keys: 列表,发送了信息的推送任务的id,用来验证推送任务次数() 意义不大
|
||
"""
|
||
@检测推送数据
|
||
@data dict 推送数据
|
||
title:标题
|
||
count:触发次数
|
||
cycle:周期 天、小时
|
||
keys:检测键值
|
||
"""
|
||
if not self._log_check(data["id"], data["cycle"], data["count"]): return None
|
||
if data["push_count"] <= self._get_today_push_count(data["id"]):return None
|
||
result = {'index': time.time(), }
|
||
tamper_path = "All protected directory " if not int(data["id"]) else "Protected Directory:{}".format(self.__config[int(data["id"])]["path"])
|
||
for m_module in data['module'].split(','):
|
||
if m_module == 'sms': continue
|
||
s_list = [">Notification: Tamper-proof for Enterprise alarm", ">Alarm content: <font color=#ff0000>Last {} minutes, {} has been tampered with more than {} times, and it has been successfully intercepted, please pay attention to the website situation , and deal with it in timely manner.</font> ".format(data['cycle'], tamper_path, data['count'])]
|
||
sdata = public.get_push_info('Tamper-proof for Enterprise alarm', s_list)
|
||
result[m_module] = sdata
|
||
self._set_total()
|
||
self._set_today_push_count(data["id"])
|
||
return result
|
||
|
||
def _log_check(self, id, cycle, count):
|
||
target_time = datetime.now() - timedelta(minutes=cycle)
|
||
tday, yday = datetime.now().strftime('%Y-%m-%d'), (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
|
||
files = []
|
||
if id == "0":
|
||
for i in self.__config.keys():
|
||
files.append("{}/{}/{}.log".format(self.__logs_path,i,tday))
|
||
files.append("{}/{}/{}.log".format(self.__logs_path,i,yday))
|
||
else:
|
||
files.append("{}/{}/{}.log".format(self.__logs_path,id,tday))
|
||
files.append("{}/{}/{}.log".format(self.__logs_path,id,yday))
|
||
|
||
_count = 0
|
||
public.print_log(files)
|
||
_f = '%Y-%m-%d %H:%M:%S'
|
||
for i in self._get_logs(files):
|
||
if _count >= count:
|
||
return True
|
||
if datetime.strptime(i, _f) > target_time:
|
||
_count += 1
|
||
else:
|
||
return _count >= count
|
||
return _count >= count
|
||
|
||
def _get_logs(self, files):
|
||
def the_generator(self):
|
||
_buf = b""
|
||
for fp in self._get_fp():
|
||
is_start = True
|
||
while is_start:
|
||
buf = b''
|
||
while True:
|
||
pos = fp.tell()
|
||
read_size = pos if pos <= 38 else 38
|
||
fp.seek(-read_size, 1)
|
||
_buf = fp.read(read_size) + _buf
|
||
fp.seek(-read_size, 1)
|
||
nl_idx = _buf.rfind(ord('\n'))
|
||
if nl_idx == -1:
|
||
if pos <= 38:
|
||
buf, _buf = _buf, b''
|
||
is_start = False
|
||
break
|
||
else:
|
||
buf = _buf[nl_idx+1:]
|
||
_buf = _buf[:nl_idx]
|
||
break
|
||
yield self._get_time(buf.decode("utf-8"))
|
||
|
||
def the_init(self,log_files):
|
||
self.log_files = log_files
|
||
|
||
def the_get_fp(self):
|
||
for i in self.log_files:
|
||
if not os.path.exists(i): continue
|
||
with open(i, 'rb') as fp:
|
||
fp.seek(-1, 2)
|
||
yield fp
|
||
|
||
def the_get_time(self, log: str):
|
||
return log.split("] [", 1)[0].strip("[").strip()
|
||
|
||
attr = {
|
||
"__init__": the_init,
|
||
"_get_fp": the_get_fp,
|
||
"__iter__": the_generator,
|
||
"_get_time": the_get_time,
|
||
}
|
||
return type("LogContent", (object, ), attr)(files)
|
||
|
||
def _set_total(self):
|
||
try:
|
||
total = json.loads(public.readFile(self.__total_path))
|
||
if "warning_msg" not in total:
|
||
total["warning_msg"] = 1
|
||
else:
|
||
total["warning_msg"] += 1
|
||
public.writeFile(self.__total_path, json.dumps(total))
|
||
except:
|
||
pass
|
||
|
||
def _get_today_push_count(self, id):
|
||
t_day = datetime.now().strftime('%Y-%m-%d')
|
||
today_tip = '{}/data/push/tips/tamper_today.json'.format(public.get_panel_path())
|
||
if os.path.exists(today_tip):
|
||
tip = json.loads(public.readFile(today_tip))
|
||
if tip["t_day"] != t_day:
|
||
tip = {"t_day": t_day}
|
||
res = 0
|
||
elif id in tip:
|
||
res = tip[id]
|
||
else:
|
||
res = 0
|
||
else:
|
||
tip = {"t_day": t_day}
|
||
res = 0
|
||
|
||
public.writeFile(today_tip, json.dumps(tip))
|
||
setattr(self, "_tip_", tip)
|
||
return res
|
||
|
||
|
||
def _set_today_push_count(self, id):
|
||
today_tip = '{}/data/push/tips/tamper_today.json'.format(public.get_panel_path())
|
||
if hasattr(self, "_tip_"):
|
||
tip = getattr(self, "_tip_")
|
||
else:
|
||
tip = json.loads(public.readFile(today_tip))
|
||
if id in tip:
|
||
tip[id] += 1
|
||
else:
|
||
tip[id] = 1
|
||
|
||
public.writeFile(today_tip, json.dumps(tip))
|
||
|
||
def _del_today_push_count(self, id):
|
||
t_day = datetime.now().strftime('%Y-%m-%d')
|
||
today_tip = '{}/data/push/tips/tamper_today.json'.format(public.get_panel_path())
|
||
if os.path.exists(today_tip):
|
||
tip = json.loads(public.readFile(today_tip))
|
||
if tip["t_day"] != t_day:
|
||
tip = {"t_day": t_day}
|
||
elif id in tip:
|
||
del tip[id]
|
||
|
||
public.writeFile(today_tip, json.dumps(tip))
|
||
|