mirror of https://github.com/midoks/mdserver-web
parent
ed3e3336b2
commit
528094cfa7
@ -1,19 +0,0 @@ |
|||||||
# plugins_system_safe |
|
||||||
|
|
||||||
系统加固插件 |
|
||||||
|
|
||||||
# DEBUG |
|
||||||
``` |
|
||||||
|
|
||||||
https://code_hosting_007.midoks.me/midoks/plugins_system_safe |
|
||||||
|
|
||||||
ln -s /www/git/midoks/plugins_system_safe /www/server/mdserver-web/plugins/system_safe |
|
||||||
|
|
||||||
ln -s /www/wwwroot/midoks/plugins_system_safe /www/server/mdserver-web/plugins/system_safe |
|
||||||
|
|
||||||
|
|
||||||
python3 /www/server/mdserver-web/plugins/system_safe/index.py bg_start |
|
||||||
|
|
||||||
systemctl daemon-reload |
|
||||||
``` |
|
||||||
|
|
@ -0,0 +1,5 @@ |
|||||||
|
{ |
||||||
|
"open": true, |
||||||
|
"excludePath": [ "cache", "threadcache", "log", "logs", "config", "runtime", "temp","tmp","caches","tmps"], |
||||||
|
"protectExt": [ "php", "html", "htm", "shtml", "tpl", "js", "css", "jsp", "do" ] |
||||||
|
} |
After Width: | Height: | Size: 556 B |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,628 @@ |
|||||||
|
# coding:utf-8 |
||||||
|
|
||||||
|
import sys |
||||||
|
import io |
||||||
|
import os |
||||||
|
import time |
||||||
|
import json |
||||||
|
import re |
||||||
|
import psutil |
||||||
|
from datetime import datetime |
||||||
|
|
||||||
|
sys.dont_write_bytecode = True |
||||||
|
sys.path.append(os.getcwd() + "/class/core") |
||||||
|
import mw |
||||||
|
|
||||||
|
app_debug = False |
||||||
|
if mw.isAppleSystem(): |
||||||
|
app_debug = True |
||||||
|
|
||||||
|
|
||||||
|
class App: |
||||||
|
|
||||||
|
__total = 'total.json' |
||||||
|
__sites = [] |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def getPluginName(self): |
||||||
|
return 'tamper_proof_py' |
||||||
|
|
||||||
|
def getPluginDir(self): |
||||||
|
return mw.getPluginDir() + '/' + self.getPluginName() |
||||||
|
|
||||||
|
def getServerDir(self): |
||||||
|
return mw.getServerDir() + '/' + self.getPluginName() |
||||||
|
|
||||||
|
def getInitDFile(self): |
||||||
|
if app_debug: |
||||||
|
return '/tmp/' + self.getPluginName() |
||||||
|
return '/etc/init.d/' + self.getPluginName() |
||||||
|
|
||||||
|
def getInitDTpl(self): |
||||||
|
path = self.getPluginDir() + "/init.d/" + self.getPluginName() + ".tpl" |
||||||
|
return path |
||||||
|
|
||||||
|
def getArgs(self): |
||||||
|
args = sys.argv[2:] |
||||||
|
tmp = {} |
||||||
|
args_len = len(args) |
||||||
|
|
||||||
|
if args_len == 1: |
||||||
|
t = args[0].strip('{').strip('}') |
||||||
|
if t.strip() == '': |
||||||
|
tmp = [] |
||||||
|
else: |
||||||
|
t = t.split(':') |
||||||
|
tmp[t[0]] = t[1] |
||||||
|
elif args_len > 1: |
||||||
|
for i in range(len(args)): |
||||||
|
t = args[i].split(':') |
||||||
|
tmp[t[0]] = t[1] |
||||||
|
return tmp |
||||||
|
|
||||||
|
def checkArgs(self, data, ck=[]): |
||||||
|
for i in range(len(ck)): |
||||||
|
if not ck[i] in data: |
||||||
|
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!')) |
||||||
|
return (True, mw.returnJson(True, 'ok')) |
||||||
|
|
||||||
|
def getTotal(self, siteName=None, day=None): |
||||||
|
defaultTotal = {"total": 0, "delete": 0, |
||||||
|
"create": 0, "modify": 0, "move": 0} |
||||||
|
if siteName: |
||||||
|
total = {} |
||||||
|
total_path = self.getServerDir() + '/sites/' + siteName + '/' + self.__total |
||||||
|
if not os.path.exists(total_path): |
||||||
|
total['site'] = defaultTotal |
||||||
|
else: |
||||||
|
total_data = mw.readFile(total_path) |
||||||
|
if total_data['site']: |
||||||
|
total['site'] = json.loads(total_data['site']) |
||||||
|
else: |
||||||
|
total['site'] = defaultTotal |
||||||
|
|
||||||
|
if not day: |
||||||
|
day = time.strftime("%Y-%m-%d", time.localtime()) |
||||||
|
total_day_path = self.getServerDir() + '/sites/' + siteName + '/day/total.json' |
||||||
|
if not os.path.exists(total_day_path): |
||||||
|
total['day'] = defaultTotal |
||||||
|
else: |
||||||
|
total['day'] = mw.readFile(total_day_path) |
||||||
|
if total['day']: |
||||||
|
total['day'] = json.loads(total['day']) |
||||||
|
else: |
||||||
|
total['day'] = defaultTotal |
||||||
|
else: |
||||||
|
filename = self.getServerDir() + '/sites/' + self.__total |
||||||
|
if os.path.exists(filename): |
||||||
|
total = json.loads(mw.readFile(filename)) |
||||||
|
else: |
||||||
|
total = defaultTotal |
||||||
|
return total |
||||||
|
|
||||||
|
def getSites(self): |
||||||
|
sites_path = self.getServerDir() + '/sites.json' |
||||||
|
t = mw.readFile(sites_path) |
||||||
|
if not os.path.exists(sites_path) or not t: |
||||||
|
mw.writeFile(sites_path, '[]') |
||||||
|
data = json.loads(mw.readFile(sites_path)) |
||||||
|
|
||||||
|
is_write = False |
||||||
|
rm_keys = ['lock', 'bak_open'] |
||||||
|
for i in data: |
||||||
|
i_keys = i.keys() |
||||||
|
if not 'open' in i_keys: |
||||||
|
i['open'] = False |
||||||
|
for o in rm_keys: |
||||||
|
if o in i_keys: |
||||||
|
if i[o]: |
||||||
|
i['open'] = True |
||||||
|
i.pop(o) |
||||||
|
is_write = True |
||||||
|
if is_write: |
||||||
|
mw.writeFile(sites_path, json.dumps(data)) |
||||||
|
|
||||||
|
self.__sites = data |
||||||
|
return data |
||||||
|
|
||||||
|
def writeSites(self, data): |
||||||
|
mw.writeFile(self.getServerDir() + '/sites.json', json.dumps(data)) |
||||||
|
# mw.ExecShell('/etc/init.d/bt_tamper_proof reload') |
||||||
|
|
||||||
|
def __getFind(self, siteName): |
||||||
|
data = self.getSites() |
||||||
|
for siteInfo in data: |
||||||
|
if siteName == siteInfo['siteName']: |
||||||
|
return siteInfo |
||||||
|
return None |
||||||
|
|
||||||
|
def writeLog(self, log): |
||||||
|
mw.writeLog('防篡改程序', log) |
||||||
|
|
||||||
|
def saveSiteConfig(self, siteInfo): |
||||||
|
data = self.getSites() |
||||||
|
for i in range(len(data)): |
||||||
|
if data[i]['siteName'] != siteInfo['siteName']: |
||||||
|
continue |
||||||
|
data[i] = siteInfo |
||||||
|
break |
||||||
|
self.writeSites(data) |
||||||
|
|
||||||
|
def syncSites(self): |
||||||
|
data = self.getSites() |
||||||
|
sites = mw.M('sites').field('name,path').select() |
||||||
|
|
||||||
|
config_path = self.getPluginDir() + '/conf/config.json' |
||||||
|
config = json.loads(mw.readFile(config_path)) |
||||||
|
names = [] |
||||||
|
n = 0 |
||||||
|
|
||||||
|
# print(config) |
||||||
|
for siteTmp in sites: |
||||||
|
names.append(siteTmp['name']) |
||||||
|
siteInfo = self.__getFind(siteTmp['name']) |
||||||
|
if siteInfo: |
||||||
|
if siteInfo['path'] != siteTmp['path']: |
||||||
|
siteInfo['path'] = siteTmp['path'] |
||||||
|
self.saveSiteConfig(siteInfo) |
||||||
|
data = self.getSites() |
||||||
|
continue |
||||||
|
siteInfo = {} |
||||||
|
siteInfo['siteName'] = siteTmp['name'] |
||||||
|
siteInfo['path'] = siteTmp['path'] |
||||||
|
siteInfo['open'] = False |
||||||
|
siteInfo['excludePath'] = config['excludePath'] |
||||||
|
siteInfo['protectExt'] = config['protectExt'] |
||||||
|
data.append(siteInfo) |
||||||
|
n += 1 |
||||||
|
|
||||||
|
newData = [] |
||||||
|
for siteInfoTmp in data: |
||||||
|
if siteInfoTmp['siteName'] in names: |
||||||
|
newData.append(siteInfoTmp) |
||||||
|
else: |
||||||
|
mw.execShell("rm -rf " + self.getServerDir() + |
||||||
|
'/sites/' + siteInfoTmp['siteName']) |
||||||
|
n += 1 |
||||||
|
if n > 0: |
||||||
|
self.writeSites(newData) |
||||||
|
self.__sites = None |
||||||
|
|
||||||
|
def initDreplace(self): |
||||||
|
file_tpl = self.getInitDTpl() |
||||||
|
service_path = self.getServerDir() |
||||||
|
|
||||||
|
initD_path = service_path + '/init.d' |
||||||
|
if not os.path.exists(initD_path): |
||||||
|
os.mkdir(initD_path) |
||||||
|
|
||||||
|
# init.d |
||||||
|
file_bin = initD_path + '/' + self.getPluginName() |
||||||
|
if not os.path.exists(file_bin): |
||||||
|
# initd replace |
||||||
|
content = mw.readFile(file_tpl) |
||||||
|
content = content.replace('{$SERVER_PATH}', service_path) |
||||||
|
mw.writeFile(file_bin, content) |
||||||
|
mw.execShell('chmod +x ' + file_bin) |
||||||
|
|
||||||
|
# systemd |
||||||
|
# /usr/lib/systemd/system |
||||||
|
systemDir = mw.systemdCfgDir() |
||||||
|
systemService = systemDir + '/tamper_proof_py.service' |
||||||
|
systemServiceTpl = self.getPluginDir() + '/init.d/tamper_proof_py.service.tpl' |
||||||
|
if os.path.exists(systemDir) and not os.path.exists(systemService): |
||||||
|
se_content = mw.readFile(systemServiceTpl) |
||||||
|
se_content = se_content.replace('{$SERVER_PATH}', service_path) |
||||||
|
mw.writeFile(systemService, se_content) |
||||||
|
mw.execShell('systemctl daemon-reload') |
||||||
|
|
||||||
|
return file_bin |
||||||
|
|
||||||
|
def getDays(self, path): |
||||||
|
days = [] |
||||||
|
if not os.path.exists(path): |
||||||
|
os.makedirs(path) |
||||||
|
for dirname in os.listdir(path): |
||||||
|
if dirname == '..' or dirname == '.' or dirname == 'total.json': |
||||||
|
continue |
||||||
|
if not os.path.isdir(path + '/' + dirname): |
||||||
|
continue |
||||||
|
days.append(dirname) |
||||||
|
days = sorted(days, reverse=True) |
||||||
|
return days |
||||||
|
|
||||||
|
def status(self): |
||||||
|
''' |
||||||
|
状态 |
||||||
|
''' |
||||||
|
initd_file = self.getServerDir() + '/init.d/' + self.getPluginName() |
||||||
|
if not os.path.exists(initd_file): |
||||||
|
return 'stop' |
||||||
|
cmd = initd_file + ' status|grep already' |
||||||
|
data = mw.execShell(cmd) |
||||||
|
if data[0] != '': |
||||||
|
return 'start' |
||||||
|
return 'stop' |
||||||
|
|
||||||
|
def tpOp(self, method): |
||||||
|
file = self.initDreplace() |
||||||
|
if not mw.isAppleSystem(): |
||||||
|
cmd = 'systemctl ' + method + ' ' + self.getPluginName() |
||||||
|
data = mw.execShell(cmd) |
||||||
|
if data[1] == '': |
||||||
|
return mw.returnJson(True, '操作成功') |
||||||
|
return mw.returnJson(False, '操作失败') |
||||||
|
|
||||||
|
cmd = file + ' ' + method |
||||||
|
data = mw.execShell(cmd) |
||||||
|
if data[1] == '': |
||||||
|
return mw.returnJson(True, '操作成功') |
||||||
|
return mw.returnJson(False, '操作失败') |
||||||
|
|
||||||
|
def start(self): |
||||||
|
return self.tpOp('start') |
||||||
|
|
||||||
|
def restart(self): |
||||||
|
return self.tpOp('restart') |
||||||
|
|
||||||
|
def service_admin(self): |
||||||
|
if mw.isAppleSystem(): |
||||||
|
return mw.returnJson(False, '仅支持Linux!') |
||||||
|
|
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['serviceStatus']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
method = args['serviceStatus'] |
||||||
|
return self.tpOp(method) |
||||||
|
|
||||||
|
def initd_status(self): |
||||||
|
if mw.isAppleSystem(): |
||||||
|
return "Apple Computer does not support" |
||||||
|
shell_cmd = 'systemctl status %s | grep loaded | grep "enabled;"' % ( |
||||||
|
self.getPluginName()) |
||||||
|
data = mw.execShell(shell_cmd) |
||||||
|
if data[0] == '': |
||||||
|
return 'fail' |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
def initd_install(self): |
||||||
|
if mw.isAppleSystem(): |
||||||
|
return "Apple Computer does not support" |
||||||
|
|
||||||
|
mw.execShell('systemctl enable ' + self.getPluginName()) |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
def initd_uninstall(self): |
||||||
|
if mw.isAppleSystem(): |
||||||
|
return "Apple Computer does not support" |
||||||
|
|
||||||
|
mw.execShell('systemctl disable ' + self.getPluginName()) |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
def set_site_status(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteName']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
siteName = args['siteName'] |
||||||
|
siteInfo = self.__getFind(siteName) |
||||||
|
if not siteInfo: |
||||||
|
return mw.returnJson(False, '指定站点不存在!') |
||||||
|
try: |
||||||
|
siteInfo['open'] = not siteInfo['open'] |
||||||
|
except: |
||||||
|
siteInfo['open'] = not siteInfo['open'] |
||||||
|
|
||||||
|
m_logs = {True: '开启', False: '关闭'} |
||||||
|
self.writeLog('%s站点[%s]防篡改保护' % |
||||||
|
(m_logs[siteInfo['open']], siteInfo['siteName'])) |
||||||
|
self.siteReload(siteInfo) |
||||||
|
self.saveSiteConfig(siteInfo) |
||||||
|
self.restart() |
||||||
|
return mw.returnJson(True, '设置成功!') |
||||||
|
|
||||||
|
def get_run_logs(self): |
||||||
|
log_file = self.getServerDir() + '/service.log' |
||||||
|
return mw.returnJson(True, mw.getLastLine(log_file, 200)) |
||||||
|
|
||||||
|
# 取文件指定尾行数 |
||||||
|
def getNumLines(self, path, num, p=1): |
||||||
|
pyVersion = sys.version_info[0] |
||||||
|
try: |
||||||
|
import cgi |
||||||
|
if not os.path.exists(path): |
||||||
|
return "" |
||||||
|
start_line = (p - 1) * num |
||||||
|
count = start_line + num |
||||||
|
fp = open(path, 'rb') |
||||||
|
buf = "" |
||||||
|
fp.seek(-1, 2) |
||||||
|
if fp.read(1) == "\n": |
||||||
|
fp.seek(-1, 2) |
||||||
|
data = [] |
||||||
|
b = True |
||||||
|
n = 0 |
||||||
|
for i in range(count): |
||||||
|
while True: |
||||||
|
newline_pos = str.rfind(str(buf), "\n") |
||||||
|
pos = fp.tell() |
||||||
|
if newline_pos != -1: |
||||||
|
if n >= start_line: |
||||||
|
line = buf[newline_pos + 1:] |
||||||
|
try: |
||||||
|
data.append(json.loads(cgi.escape(line))) |
||||||
|
except: |
||||||
|
pass |
||||||
|
buf = buf[:newline_pos] |
||||||
|
n += 1 |
||||||
|
break |
||||||
|
else: |
||||||
|
if pos == 0: |
||||||
|
b = False |
||||||
|
break |
||||||
|
to_read = min(4096, pos) |
||||||
|
fp.seek(-to_read, 1) |
||||||
|
t_buf = fp.read(to_read) |
||||||
|
if pyVersion == 3: |
||||||
|
if type(t_buf) == bytes: |
||||||
|
t_buf = t_buf.decode('utf-8') |
||||||
|
buf = t_buf + buf |
||||||
|
fp.seek(-to_read, 1) |
||||||
|
if pos - to_read == 0: |
||||||
|
buf = "\n" + buf |
||||||
|
if not b: |
||||||
|
break |
||||||
|
fp.close() |
||||||
|
except: |
||||||
|
return [] |
||||||
|
if len(data) >= 2000: |
||||||
|
arr = [] |
||||||
|
for d in data: |
||||||
|
arr.insert(0, json.dumps(d)) |
||||||
|
mw.writeFile(path, "\n".join(arr)) |
||||||
|
return data |
||||||
|
|
||||||
|
def get_safe_logs(self): |
||||||
|
|
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteName']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
siteName = args['siteName'] |
||||||
|
|
||||||
|
data = {} |
||||||
|
path = self.getPluginDir() + '/sites/' + siteName + '/day' |
||||||
|
data['days'] = self.getDays(path) |
||||||
|
|
||||||
|
if not data['days']: |
||||||
|
data['logs'] = [] |
||||||
|
else: |
||||||
|
p = 1 |
||||||
|
if hasattr(args, 'p'): |
||||||
|
p = args['p'] |
||||||
|
|
||||||
|
day = data['days'][0] |
||||||
|
if hasattr(args, 'day'): |
||||||
|
day = args['day'] |
||||||
|
data['get_day'] = day |
||||||
|
logs_path = path + '/' + day + '/logs.json' |
||||||
|
data['logs'] = self.getNumLines(logs_path, 2000, int(p)) |
||||||
|
return mw.returnJson(True, 'ok', data) |
||||||
|
|
||||||
|
def get_site_find(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteName']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
siteName = args['siteName'] |
||||||
|
data = self.__getFind(siteName) |
||||||
|
return mw.returnJson(True, 'ok', data) |
||||||
|
|
||||||
|
def siteReload(self, siteInfo): |
||||||
|
cmd = "python3 {} {}".format( |
||||||
|
mw.getPluginDir() + '/tamper_proof_service.py unlock', siteInfo['path']) |
||||||
|
mw.execShell(cmd) |
||||||
|
tip_file = mw.getServerDir() + '/tips/' + siteInfo['siteName'] + '.pl' |
||||||
|
if os.path.exists(tip_file): |
||||||
|
os.remove(tip_file) |
||||||
|
|
||||||
|
def remove_protect_ext(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteName', 'protectExt']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
siteName = args['siteName'] |
||||||
|
protectExt = args['protectExt'].strip() |
||||||
|
|
||||||
|
siteInfo = self.__getFind(siteName) |
||||||
|
|
||||||
|
if not siteInfo: |
||||||
|
return mw.returnJson(False, '指定站点不存在!') |
||||||
|
if not protectExt: |
||||||
|
return mw.returnJson(False, '被删除的保护列表不能为空') |
||||||
|
|
||||||
|
for protectExt in protectExt.split(','): |
||||||
|
if not protectExt in siteInfo['protectExt']: |
||||||
|
continue |
||||||
|
siteInfo['protectExt'].remove(protectExt) |
||||||
|
self.writeLog('站点[%s]从受保护列表中删除[.%s]' % |
||||||
|
(siteInfo['siteName'], protectExt)) |
||||||
|
self.siteReload(siteInfo) |
||||||
|
self.saveSiteConfig(siteInfo) |
||||||
|
return mw.returnJson(True, '删除成功!') |
||||||
|
|
||||||
|
def add_protect_ext(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteName', 'protectExt']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
siteName = args['siteName'] |
||||||
|
protectExt = args['protectExt'].strip() |
||||||
|
|
||||||
|
siteInfo = self.__getFind(siteName) |
||||||
|
if not siteInfo: |
||||||
|
return mw.returnJson(False, '指定站点不存在!') |
||||||
|
protectExt = protectExt.lower() |
||||||
|
for protectExt in protectExt.split("\n"): |
||||||
|
if protectExt[0] == '/': |
||||||
|
if os.path.isdir(protectExt): |
||||||
|
continue |
||||||
|
if protectExt in siteInfo['protectExt']: |
||||||
|
continue |
||||||
|
siteInfo['protectExt'].insert(0, protectExt) |
||||||
|
self.writeLog('站点[%s]添加文件类型或文件名[.%s]到受保护列表' % |
||||||
|
(siteInfo['siteName'], protectExt)) |
||||||
|
self.siteReload(siteInfo) |
||||||
|
self.saveSiteConfig(siteInfo) |
||||||
|
return mw.returnJson(True, '添加成功!') |
||||||
|
|
||||||
|
def add_excloud(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteName', 'excludePath']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
siteName = args['siteName'] |
||||||
|
excludePath = args['excludePath'].strip() |
||||||
|
siteInfo = self.__getFind(siteName) |
||||||
|
if not siteInfo: |
||||||
|
return mw.returnJson(False, '指定站点不存在!') |
||||||
|
|
||||||
|
if not excludePath: |
||||||
|
return mw.returnJson(False, '排除内容不能为空') |
||||||
|
|
||||||
|
for excludePath in excludePath.split('\n'): |
||||||
|
if not excludePath: |
||||||
|
continue |
||||||
|
if excludePath.find('/') != -1: |
||||||
|
if not os.path.exists(excludePath): |
||||||
|
continue |
||||||
|
excludePath = excludePath.lower() |
||||||
|
if excludePath[-1] == '/': |
||||||
|
excludePath = excludePath[:-1] |
||||||
|
if excludePath in siteInfo['excludePath']: |
||||||
|
continue |
||||||
|
siteInfo['excludePath'].insert(0, excludePath) |
||||||
|
self.writeLog('站点[%s]添加排除目录名[%s]到排除列表' % |
||||||
|
(siteInfo['siteName'], excludePath)) |
||||||
|
|
||||||
|
self.siteReload(siteInfo) |
||||||
|
self.saveSiteConfig(siteInfo) |
||||||
|
return mw.returnJson(True, '添加成功!') |
||||||
|
|
||||||
|
def remove_excloud(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteName', 'excludePath']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
siteName = args['siteName'] |
||||||
|
siteInfo = self.__getFind(siteName) |
||||||
|
excludePath = args['excludePath'].strip() |
||||||
|
if excludePath == '': |
||||||
|
return mw.returnJson(False, '排除文件或目录不能为空') |
||||||
|
if not siteInfo: |
||||||
|
return mw.returnJson(False, '指定站点不存在!') |
||||||
|
|
||||||
|
for excludePath in excludePath.split(','): |
||||||
|
if not excludePath: |
||||||
|
continue |
||||||
|
if not excludePath in siteInfo['excludePath']: |
||||||
|
continue |
||||||
|
siteInfo['excludePath'].remove(excludePath) |
||||||
|
self.writeLog('站点[%s]从排除列表中删除目录名[%s]' % |
||||||
|
(siteInfo['siteName'], excludePath)) |
||||||
|
self.siteReload(siteInfo) |
||||||
|
self.saveSiteConfig(siteInfo) |
||||||
|
return mw.returnJson(True, '删除成功!') |
||||||
|
|
||||||
|
def sim_test(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['path']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
path = args['path'].strip() |
||||||
|
if not os.path.exists(path): |
||||||
|
return mw.returnJson(False, "此目录不存在") |
||||||
|
|
||||||
|
# 判断是否安装php |
||||||
|
import site_api |
||||||
|
php_version = site_api.site_api().getPhpVersion() |
||||||
|
if not php_version: |
||||||
|
return mw.returnJson(False, "未安装PHP测试失败") |
||||||
|
|
||||||
|
php_path = '/www/server/php/' + php_version[1]['version'] + '/bin/php' |
||||||
|
php_name = path + "/" + str(int(time.time())) + ".php" |
||||||
|
if os.path.exists(php_name): |
||||||
|
mw.execShell("rm -rf %s" % php_name) |
||||||
|
# 写入 |
||||||
|
cmd = php_path + \ |
||||||
|
" -r \"file_put_contents('{}','{}');\"".format(php_name, php_name) |
||||||
|
mw.execShell(cmd) |
||||||
|
time.sleep(0.5) |
||||||
|
if os.path.exists(php_name): |
||||||
|
if os.path.exists(php_name): |
||||||
|
mw.execShell("rm -rf %s" % php_name) |
||||||
|
return mw.returnJson(False, "拦截失败,可能未开启防篡改") |
||||||
|
return mw.returnJson(True, "拦截成功") |
||||||
|
|
||||||
|
def set_site_status_all(self): |
||||||
|
args = self.getArgs() |
||||||
|
check = self.checkArgs(args, ['siteNames', 'siteState']) |
||||||
|
if not check[0]: |
||||||
|
return check[1] |
||||||
|
|
||||||
|
sites = self.getSites() |
||||||
|
siteState = True if args['siteState'] == '1' else False |
||||||
|
siteNames = json.loads(args['siteNames']) |
||||||
|
m_logs = {True: '开启', False: '关闭'} |
||||||
|
for i in range(len(sites)): |
||||||
|
if sites[i]['siteName'] in siteNames: |
||||||
|
sites[i]['open'] = siteState |
||||||
|
self.writeLog('%s站点[%s]防篡改保护' % |
||||||
|
(m_logs[siteState], sites[i]['siteName'])) |
||||||
|
self.writeSites(sites) |
||||||
|
return mw.returnJson(True, '批量设置成功') |
||||||
|
|
||||||
|
def get_index(self): |
||||||
|
self.syncSites() |
||||||
|
args = self.getArgs() |
||||||
|
day = None |
||||||
|
if 'day' in args: |
||||||
|
day = args['day'] |
||||||
|
|
||||||
|
ser_status = self.status() |
||||||
|
ser_status_bool = False |
||||||
|
if ser_status == 'start': |
||||||
|
ser_status_bool = True |
||||||
|
data = {} |
||||||
|
data['open'] = ser_status_bool |
||||||
|
data['total'] = self.getTotal() |
||||||
|
data['sites'] = self.getSites() |
||||||
|
for i in range(len(data['sites'])): |
||||||
|
data['sites'][i]['total'] = self.getTotal( |
||||||
|
data['sites'][i]['siteName'], day) |
||||||
|
return mw.returnJson(True, 'ok', data) |
||||||
|
|
||||||
|
def get_speed(self): |
||||||
|
print("12") |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
func = sys.argv[1] |
||||||
|
classApp = App() |
||||||
|
try: |
||||||
|
data = eval("classApp." + func + "()") |
||||||
|
print(data) |
||||||
|
except Exception as e: |
||||||
|
print(mw.getTracebackInfo()) |
@ -0,0 +1,15 @@ |
|||||||
|
{ |
||||||
|
"title": "网站防篡改程序[PY]", |
||||||
|
"tip": "lib", |
||||||
|
"name": "tamper_proof_py", |
||||||
|
"type": "soft", |
||||||
|
"ps": "事件型防篡改程序,可有效保护网站重要文件不被木马篡改", |
||||||
|
"versions": "1.0", |
||||||
|
"shell": "install.sh", |
||||||
|
"checks": "server/tamper_proof_py", |
||||||
|
"path": "server/tamper_proof_py", |
||||||
|
"author": "midoks", |
||||||
|
"home": "", |
||||||
|
"date":"2022-01-18", |
||||||
|
"pid":"4" |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
[Unit] |
||||||
|
Description=tamper_proof_py server daemon |
||||||
|
After=network.target |
||||||
|
|
||||||
|
[Service] |
||||||
|
Type=forking |
||||||
|
ExecStart={$SERVER_PATH}/init.d/tamper_proof_py start |
||||||
|
ExecStop={$SERVER_PATH}/init.d/tamper_proof_py stop |
||||||
|
ExecReload={$SERVER_PATH}/init.d/tamper_proof_py reload |
||||||
|
ExecRestart={$SERVER_PATH}/init.d/tamper_proof_py restart |
||||||
|
KillMode=process |
||||||
|
Restart=on-failure |
||||||
|
|
||||||
|
[Install] |
||||||
|
WantedBy=multi-user.target |
@ -0,0 +1,95 @@ |
|||||||
|
#!/bin/bash |
||||||
|
# chkconfig: 2345 55 25 |
||||||
|
# description:tamper_proof_service |
||||||
|
|
||||||
|
### BEGIN INIT INFO |
||||||
|
# Provides: tamper_proof_service |
||||||
|
# Required-Start: $all |
||||||
|
# Required-Stop: $all |
||||||
|
# Default-Start: 2 3 4 5 |
||||||
|
# Default-Stop: 0 1 6 |
||||||
|
# Short-Description: starts tamper_proof_service |
||||||
|
# Description: starts the tamper_proof_service |
||||||
|
### END INIT INFO |
||||||
|
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin |
||||||
|
mw_path={$SERVER_PATH} |
||||||
|
rootPath=$(dirname "$mw_path") |
||||||
|
PATH=$PATH:$mw_path/bin |
||||||
|
|
||||||
|
if [ -f $rootPath/mdserver-web/bin/activate ];then |
||||||
|
source $rootPath/mdserver-web/bin/activate |
||||||
|
fi |
||||||
|
|
||||||
|
# cd /www/server/mdserver-web && python3 plugins/tamper_proof_py/tamper_proof_service.py start |
||||||
|
sys_start() |
||||||
|
{ |
||||||
|
isStart=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep |grep -v 'tamper_proof_py/tamper_proof_service.py start' | grep -v 'tamper_proof_py/tamper_proof_service.py reload' | grep -v 'tamper_proof_py/tamper_proof_service.py restart' | grep -v systemctl | grep -v '/bin/sh' | grep -v '/bin/bash' | awk '{print $2}'|xargs) |
||||||
|
if [ "$isStart" == '' ];then |
||||||
|
echo -e "Starting tamper_proof_service... \c" |
||||||
|
cd $rootPath/mdserver-web |
||||||
|
nohup python3 plugins/tamper_proof_py/tamper_proof_service.py start &> $mw_path/service.log & |
||||||
|
sleep 0.5 |
||||||
|
isStart=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep|awk '{print $2}'|xargs) |
||||||
|
if [ "$isStart" == '' ];then |
||||||
|
echo -e "\033[31mfailed\033[0m" |
||||||
|
echo '------------------------------------------------------' |
||||||
|
cat $mw_path/service.log |
||||||
|
echo '------------------------------------------------------' |
||||||
|
echo -e "\033[31mError: tamper_proof_service startup failed.\033[0m" |
||||||
|
return; |
||||||
|
fi |
||||||
|
echo -e "\033[32mdone\033[0m" |
||||||
|
else |
||||||
|
echo "Starting tamper_proof_service (pid $isStart) already running" |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
sys_stop() |
||||||
|
{ |
||||||
|
echo -e "Stopping tamper_proof_service... \c"; |
||||||
|
pids=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep|grep -v '/bin/bash'|grep -v systemctl | grep -v 'tamper_proof_py/tamper_proof_service.py stop' | grep -v 'tamper_proof_py/tamper_proof_service.py reload' | grep -v 'tamper_proof_py/tamper_proof_service.py restart' |awk '{print $2}'|xargs) |
||||||
|
arr=($pids) |
||||||
|
for p in ${arr[@]} |
||||||
|
do |
||||||
|
kill -9 $p |
||||||
|
done |
||||||
|
cd $rootPath/mdserver-web |
||||||
|
python3 plugins/tamper_proof_py/tamper_proof_service.py stop |
||||||
|
echo -e "\033[32mdone\033[0m" |
||||||
|
} |
||||||
|
|
||||||
|
sys_status() |
||||||
|
{ |
||||||
|
isStart=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep|grep -v "init.d/tamper_proof_py"|grep -v systemctl|awk '{print $2}'|xargs) |
||||||
|
if [ "$isStart" != '' ];then |
||||||
|
echo -e "\033[32mtamper_proof_service (pid $isStart) already running\033[0m" |
||||||
|
else |
||||||
|
echo -e "\033[32mtamper_proof_service not running\033[0m" |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
case "$1" in |
||||||
|
'start') |
||||||
|
sys_start |
||||||
|
;; |
||||||
|
'stop') |
||||||
|
sys_stop |
||||||
|
;; |
||||||
|
'restart') |
||||||
|
sys_stop |
||||||
|
sleep 0.2 |
||||||
|
sys_start |
||||||
|
;; |
||||||
|
'reload') |
||||||
|
sys_stop |
||||||
|
sleep 0.2 |
||||||
|
sys_start |
||||||
|
;; |
||||||
|
'status') |
||||||
|
sys_status |
||||||
|
;; |
||||||
|
*) |
||||||
|
echo "Usage: systemctl {start|stop|restart|reload} tamper_proof_service" |
||||||
|
;; |
||||||
|
esac |
@ -0,0 +1,52 @@ |
|||||||
|
#!/bin/bash |
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin |
||||||
|
export PATH |
||||||
|
|
||||||
|
curPath=`pwd` |
||||||
|
rootPath=$(dirname "$curPath") |
||||||
|
rootPath=$(dirname "$rootPath") |
||||||
|
serverPath=$(dirname "$rootPath") |
||||||
|
|
||||||
|
pip install pyinotify |
||||||
|
if [ -f ${rootPath}/bin/activate ];then |
||||||
|
source ${rootPath}/bin/activate |
||||||
|
fi |
||||||
|
pip install pyinotify |
||||||
|
|
||||||
|
install_tmp=${rootPath}/tmp/mw_install.pl |
||||||
|
SYSOS=`uname` |
||||||
|
VERSION=$2 |
||||||
|
APP_NAME=tamper_proof_py |
||||||
|
|
||||||
|
Install_App() |
||||||
|
{ |
||||||
|
echo '正在安装脚本文件...' > $install_tmp |
||||||
|
mkdir -p $serverPath/tamper_proof_py |
||||||
|
echo "$VERSION" > $serverPath/tamper_proof_py/version.pl |
||||||
|
echo 'install complete' > $install_tmp |
||||||
|
|
||||||
|
#初始化 |
||||||
|
cd ${serverPath}/mdserver-web && python3 plugins/tamper_proof_py/index.py start $VERSION |
||||||
|
cd ${serverPath}/mdserver-web && python3 plugins/tamper_proof_py/index.py initd_install $VERSION |
||||||
|
} |
||||||
|
|
||||||
|
Uninstall_App() |
||||||
|
{ |
||||||
|
if [ -f /usr/lib/systemd/system/${APP_NAME}.service ] || [ -f /lib/systemd/system/${APP_NAME}.service ] ;then |
||||||
|
systemctl stop ${APP_NAME} |
||||||
|
systemctl disable ${APP_NAME} |
||||||
|
rm -rf /usr/lib/systemd/system/${APP_NAME}.service |
||||||
|
rm -rf /lib/systemd/system/${APP_NAME}.service |
||||||
|
systemctl daemon-reload |
||||||
|
fi |
||||||
|
|
||||||
|
rm -rf $serverPath/tamper_proof_py |
||||||
|
echo "uninstall completed" > $install_tmp |
||||||
|
} |
||||||
|
|
||||||
|
action=$1 |
||||||
|
if [ "${1}" == 'install' ];then |
||||||
|
Install_App |
||||||
|
else |
||||||
|
Uninstall_App |
||||||
|
fi |
@ -0,0 +1,552 @@ |
|||||||
|
|
||||||
|
# +-------------------------------------------------------------------- |
||||||
|
# | 事件型防篡改 |
||||||
|
# +-------------------------------------------------------------------- |
||||||
|
import sys |
||||||
|
import os |
||||||
|
import pyinotify |
||||||
|
import json |
||||||
|
import shutil |
||||||
|
import time |
||||||
|
import psutil |
||||||
|
import threading |
||||||
|
import datetime |
||||||
|
|
||||||
|
sys.path.append(os.getcwd() + "/class/core") |
||||||
|
import mw |
||||||
|
|
||||||
|
|
||||||
|
class MyEventHandler(pyinotify.ProcessEvent): |
||||||
|
_PLUGIN_PATH = '/www/server/tamper_proof_py' |
||||||
|
_CONFIG = '/config.json' |
||||||
|
_SITES = '/sites.json' |
||||||
|
_SITES_DATA = None |
||||||
|
_CONFIG_DATA = None |
||||||
|
_DONE_FILE = None |
||||||
|
bakcupChirdPath = [] |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
self._PLUGIN_PATH = self.getServerDir() |
||||||
|
|
||||||
|
def getPluginName(self): |
||||||
|
return 'tamper_proof_py' |
||||||
|
|
||||||
|
def getPluginDir(self): |
||||||
|
return mw.getPluginDir() + '/' + self.getPluginName() |
||||||
|
|
||||||
|
def getServerDir(self): |
||||||
|
return mw.getServerDir() + '/' + self.getPluginName() |
||||||
|
|
||||||
|
def rmdir(self, filename): |
||||||
|
try: |
||||||
|
shutil.rmtree(filename) |
||||||
|
except: |
||||||
|
pass |
||||||
|
|
||||||
|
def check_site_logs(self, Stiename, datetime_time): |
||||||
|
ret = [] |
||||||
|
cur_month = datetime_time.month |
||||||
|
cur_day = datetime_time.day |
||||||
|
cur_year = datetime_time.year |
||||||
|
cur_hour = datetime_time.hour |
||||||
|
cur_minute = datetime_time.minute |
||||||
|
cur_second = int(datetime_time.second) |
||||||
|
months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', 'Jul': '07', |
||||||
|
'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'} |
||||||
|
logs_data = self.get_site_logs(Stiename) |
||||||
|
if not logs_data: |
||||||
|
return False |
||||||
|
for i2 in logs_data: |
||||||
|
try: |
||||||
|
i = i2.split() |
||||||
|
# 判断状态码是否为200 |
||||||
|
if int(i[8]) not in [200, 500]: |
||||||
|
continue |
||||||
|
# 判断是否为POST |
||||||
|
day_time = i[3].split('/')[0].split('[')[1] |
||||||
|
if int(cur_day) != int(day_time): |
||||||
|
continue |
||||||
|
month_time = i[3].split('/')[1] |
||||||
|
if int(months[month_time]) != int(cur_month): |
||||||
|
continue |
||||||
|
year_time = i[3].split('/')[2].split(':')[0] |
||||||
|
if int(year_time) != int(cur_year): |
||||||
|
continue |
||||||
|
hour_time = i[3].split('/')[2].split(':')[1] |
||||||
|
if int(hour_time) != int(cur_hour): |
||||||
|
continue |
||||||
|
minute_time = i[3].split('/')[2].split(':')[2] |
||||||
|
if int(minute_time) != int(cur_minute): |
||||||
|
continue |
||||||
|
second_time = int(i[3].split('/')[2].split(':')[3]) |
||||||
|
if cur_second - second_time > 10: |
||||||
|
continue |
||||||
|
ret.append(i2) |
||||||
|
except: |
||||||
|
continue |
||||||
|
ret2 = [] |
||||||
|
if len(ret) > 20: |
||||||
|
for i2 in logs_data: |
||||||
|
try: |
||||||
|
i = i2.split() |
||||||
|
if i[6] != 'POST': |
||||||
|
continue |
||||||
|
if int(i[8]) not in [200, 500]: |
||||||
|
continue |
||||||
|
# 判断是否为POST |
||||||
|
day_time = i[3].split('/')[0].split('[')[1] |
||||||
|
if int(cur_day) != int(day_time): |
||||||
|
continue |
||||||
|
month_time = i[3].split('/')[1] |
||||||
|
if int(months[month_time]) != int(cur_month): |
||||||
|
continue |
||||||
|
year_time = i[3].split('/')[2].split(':')[0] |
||||||
|
if int(year_time) != int(cur_year): |
||||||
|
continue |
||||||
|
hour_time = i[3].split('/')[2].split(':')[1] |
||||||
|
if int(hour_time) != int(cur_hour): |
||||||
|
continue |
||||||
|
minute_time = i[3].split('/')[2].split(':')[2] |
||||||
|
if int(minute_time) != int(cur_minute): |
||||||
|
continue |
||||||
|
ret2.append(i2) |
||||||
|
except: |
||||||
|
continue |
||||||
|
if ret2: |
||||||
|
ret = ret2 |
||||||
|
if len(ret) > 20: |
||||||
|
return ret[0:20] |
||||||
|
return ret |
||||||
|
|
||||||
|
def get_site_logs(self, Stiename): |
||||||
|
try: |
||||||
|
pythonV = sys.version_info[0] |
||||||
|
path = '/www/wwwlogs/' + Stiename + '.log' |
||||||
|
num = 500 |
||||||
|
if not os.path.exists(path): |
||||||
|
return [] |
||||||
|
p = 1 |
||||||
|
start_line = (p - 1) * num |
||||||
|
count = start_line + num |
||||||
|
fp = open(path, 'rb') |
||||||
|
buf = "" |
||||||
|
try: |
||||||
|
fp.seek(-1, 2) |
||||||
|
except: |
||||||
|
return [] |
||||||
|
if fp.read(1) == "\n": |
||||||
|
fp.seek(-1, 2) |
||||||
|
data = [] |
||||||
|
b = True |
||||||
|
n = 0 |
||||||
|
c = 0 |
||||||
|
while c < count: |
||||||
|
while True: |
||||||
|
newline_pos = str.rfind(buf, "\n") |
||||||
|
pos = fp.tell() |
||||||
|
if newline_pos != -1: |
||||||
|
if n >= start_line: |
||||||
|
line = buf[newline_pos + 1:] |
||||||
|
if line: |
||||||
|
try: |
||||||
|
data.append(line) |
||||||
|
except: |
||||||
|
c -= 1 |
||||||
|
n -= 1 |
||||||
|
pass |
||||||
|
else: |
||||||
|
c -= 1 |
||||||
|
n -= 1 |
||||||
|
buf = buf[:newline_pos] |
||||||
|
n += 1 |
||||||
|
c += 1 |
||||||
|
break |
||||||
|
else: |
||||||
|
if pos == 0: |
||||||
|
b = False |
||||||
|
break |
||||||
|
to_read = min(4096, pos) |
||||||
|
fp.seek(-to_read, 1) |
||||||
|
t_buf = fp.read(to_read) |
||||||
|
if pythonV == 3: |
||||||
|
t_buf = t_buf.decode('utf-8', errors="ignore") |
||||||
|
buf = t_buf + buf |
||||||
|
fp.seek(-to_read, 1) |
||||||
|
if pos - to_read == 0: |
||||||
|
buf = "\n" + buf |
||||||
|
if not b: |
||||||
|
break |
||||||
|
fp.close() |
||||||
|
except: |
||||||
|
data = [] |
||||||
|
return data |
||||||
|
|
||||||
|
def process_IN_CREATE(self, event): |
||||||
|
siteInfo = self.get_SITE_CONFIG(event.pathname) |
||||||
|
if not self.check_FILE(event, siteInfo, True): |
||||||
|
return False |
||||||
|
self._DONE_FILE = event.pathname |
||||||
|
if event.dir: |
||||||
|
if os.path.exists(event.pathname): |
||||||
|
self.rmdir(event.pathname) |
||||||
|
self.write_LOG('create', siteInfo[ |
||||||
|
'siteName'], event.pathname, datetime.datetime.now()) |
||||||
|
|
||||||
|
else: |
||||||
|
if os.path.exists(event.pathname): |
||||||
|
try: |
||||||
|
src_path = os.path.dirname(event.pathname) |
||||||
|
os.system("chattr -a {}".format(src_path)) |
||||||
|
os.remove(event.pathname) |
||||||
|
os.system("chattr +a {}".format(src_path)) |
||||||
|
self.write_LOG('create', siteInfo[ |
||||||
|
'siteName'], event.pathname, datetime.datetime.now()) |
||||||
|
except: |
||||||
|
pass |
||||||
|
|
||||||
|
def process_IN_MOVED_TO(self, event): |
||||||
|
# 检查是否受保护 |
||||||
|
siteInfo = self.get_SITE_CONFIG(event.pathname) |
||||||
|
if not self.check_FILE(event, siteInfo): |
||||||
|
return False |
||||||
|
|
||||||
|
if not getattr(event, 'src_pathname', None): |
||||||
|
if os.path.isdir(event.pathname): |
||||||
|
self.rmdir(event.pathname) |
||||||
|
else: |
||||||
|
os.remove(event.pathname) |
||||||
|
self.write_LOG('move', siteInfo[ |
||||||
|
'siteName'], '未知 -> ' + event.pathname) |
||||||
|
return True |
||||||
|
|
||||||
|
# 是否为标记文件 |
||||||
|
if event.src_pathname == self._DONE_FILE: |
||||||
|
return False |
||||||
|
|
||||||
|
if not os.path.exists(event.src_pathname): |
||||||
|
# 标记 |
||||||
|
self._DONE_FILE = event.pathname |
||||||
|
# 还原 |
||||||
|
os.renames(event.pathname, event.src_pathname) |
||||||
|
|
||||||
|
# 记录日志 |
||||||
|
self.write_LOG('move', siteInfo['siteName'], |
||||||
|
event.src_pathname + ' -> ' + event.pathname) |
||||||
|
|
||||||
|
def check_FILE(self, event, siteInfo, create=False): |
||||||
|
if not siteInfo: |
||||||
|
return False |
||||||
|
if self.exclude_PATH(event.pathname): |
||||||
|
return False |
||||||
|
if event.dir and create: |
||||||
|
return True |
||||||
|
if not event.dir: |
||||||
|
if not self.protect_EXT(event.pathname): |
||||||
|
return False |
||||||
|
return True |
||||||
|
|
||||||
|
def protect_EXT(self, pathname): |
||||||
|
if pathname.find('.') == -1: |
||||||
|
return False |
||||||
|
extName = pathname.split('.')[-1].lower() |
||||||
|
siteData = self.get_SITE_CONFIG(pathname) |
||||||
|
if siteData: |
||||||
|
if extName in siteData['protectExt']: |
||||||
|
return True |
||||||
|
return False |
||||||
|
|
||||||
|
def exclude_PATH(self, pathname): |
||||||
|
if pathname.find('/') == -1: |
||||||
|
return False |
||||||
|
siteData = self.get_SITE_CONFIG(pathname) |
||||||
|
return self.exclude_PATH_OF_SITE(pathname, siteData['excludePath']) |
||||||
|
|
||||||
|
def exclude_PATH_OF_SITE(self, pathname, excludePath): |
||||||
|
pathname = pathname.lower() |
||||||
|
dirNames = pathname.split('/') |
||||||
|
if excludePath: |
||||||
|
if pathname in excludePath: |
||||||
|
return True |
||||||
|
if pathname + '/' in excludePath: |
||||||
|
return True |
||||||
|
for ePath in excludePath: |
||||||
|
if ePath in dirNames: |
||||||
|
return True |
||||||
|
if pathname.find(ePath) == 0: |
||||||
|
return True |
||||||
|
return False |
||||||
|
|
||||||
|
def get_SITE_CONFIG(self, pathname): |
||||||
|
if not self._SITES_DATA: |
||||||
|
self._SITES_DATA = json.loads( |
||||||
|
mw.readFile(self._PLUGIN_PATH + self._SITES)) |
||||||
|
for site in self._SITES_DATA: |
||||||
|
length = len(site['path']) |
||||||
|
if len(pathname) < length: |
||||||
|
continue |
||||||
|
if site['path'] != pathname[:length]: |
||||||
|
continue |
||||||
|
return site |
||||||
|
return None |
||||||
|
|
||||||
|
def get_CONFIG(self): |
||||||
|
if self._CONFIG_DATA: |
||||||
|
return self._CONFIG_DATA |
||||||
|
self._CONFIG_DATA = json.loads( |
||||||
|
mw.readFile(self._PLUGIN_PATH + self._CONFIG)) |
||||||
|
|
||||||
|
def list_DIR(self, path, siteInfo): # path 站点路径 |
||||||
|
if not os.path.exists(path): |
||||||
|
return |
||||||
|
lock_files = [] |
||||||
|
lock_dirs = [] |
||||||
|
explode_a = ['log', 'logs', 'cache', 'templates', 'template', 'upload', 'img', |
||||||
|
'image', 'images', 'public', 'static', 'js', 'css', 'tmp', 'temp', 'update', 'data'] |
||||||
|
for name in os.listdir(path): |
||||||
|
try: |
||||||
|
filename = "{}/{}".format(path, name).replace('//', '/') |
||||||
|
lower_name = name.lower() |
||||||
|
lower_filename = filename.lower() |
||||||
|
if os.path.isdir(filename): # 是否为目录 |
||||||
|
if lower_name in siteInfo['excludePath']: |
||||||
|
continue # 是否为排除的文件名 |
||||||
|
# 是否为排除目录 |
||||||
|
if not self.exclude_PATH_OF_SITE(filename, siteInfo['excludePath']): |
||||||
|
if not lower_name in explode_a: # 是否为固定不锁定目录 |
||||||
|
lock_dirs.append('"' + name + '"') |
||||||
|
self.list_DIR(filename, siteInfo) |
||||||
|
continue |
||||||
|
|
||||||
|
# 是否为受保护的文件名或文件全路径 |
||||||
|
if not lower_name in siteInfo['protectExt'] and not lower_filename in siteInfo['protectExt']: |
||||||
|
if not self.get_EXT_NAME(lower_name) in siteInfo['protectExt']: |
||||||
|
continue # 是否为受保护文件类型 |
||||||
|
|
||||||
|
if lower_filename in siteInfo['excludePath']: |
||||||
|
continue # 是否为排除文件 |
||||||
|
if lower_name in siteInfo['excludePath']: |
||||||
|
continue # 是否为排除的文件名 |
||||||
|
lock_files.append('"' + name + '"') |
||||||
|
except: |
||||||
|
print(mw.getTracebackInfo()) |
||||||
|
if lock_files: |
||||||
|
self.thread_exec(lock_files, path, 'i') |
||||||
|
if lock_dirs: |
||||||
|
self.thread_exec(lock_dirs, path, 'a') |
||||||
|
|
||||||
|
_thread_count = 0 |
||||||
|
_thread_max = 2 * psutil.cpu_count() |
||||||
|
|
||||||
|
def thread_exec(self, file_list, cwd, i='i'): |
||||||
|
while self._thread_count > self._thread_max: |
||||||
|
time.sleep(0.1) |
||||||
|
|
||||||
|
self._thread_count += 1 |
||||||
|
cmd = "cd {} && chattr +{} {} > /dev/null".format( |
||||||
|
cwd, i, ' '.join(file_list)) |
||||||
|
p = threading.Thread(target=self.run_thread, args=(cmd,)) |
||||||
|
p.start() |
||||||
|
|
||||||
|
def run_thread(self, cmd): |
||||||
|
os.system(cmd) |
||||||
|
self._thread_count -= 1 |
||||||
|
|
||||||
|
def get_EXT_NAME(self, fileName): |
||||||
|
return fileName.split('.')[-1] |
||||||
|
|
||||||
|
def write_LOG(self, eventType, siteName, pathname, datetime): |
||||||
|
# 获取网站时间的top100 记录 |
||||||
|
site_log = '/www/wwwlogs/%s.log' % siteName |
||||||
|
logs_data = [] |
||||||
|
if os.path.exists(site_log): |
||||||
|
logs_data = self.check_site_logs(siteName, datetime) |
||||||
|
dateDay = time.strftime("%Y-%m-%d", time.localtime()) |
||||||
|
logPath = self._PLUGIN_PATH + '/sites/' + \ |
||||||
|
siteName + '/day/' + dateDay |
||||||
|
if not os.path.exists(logPath): |
||||||
|
os.makedirs(logPath) |
||||||
|
logFile = os.path.join(logPath, 'logs.json') |
||||||
|
logVar = [int(time.time()), eventType, pathname, logs_data] |
||||||
|
fp = open(logFile, 'a+') |
||||||
|
fp.write(json.dumps(logVar) + "\n") |
||||||
|
fp.close() |
||||||
|
logFiles = [ |
||||||
|
logPath + '/total.json', |
||||||
|
self._PLUGIN_PATH + '/sites/' + siteName + '/day/total.json', |
||||||
|
self._PLUGIN_PATH + '/sites/total.json' |
||||||
|
] |
||||||
|
|
||||||
|
for totalLogFile in logFiles: |
||||||
|
if not os.path.exists(totalLogFile): |
||||||
|
totalData = {"total": 0, "delete": 0, |
||||||
|
"create": 0, "modify": 0, "move": 0} |
||||||
|
else: |
||||||
|
dataTmp = mw.readFile(totalLogFile) |
||||||
|
if dataTmp: |
||||||
|
totalData = json.loads(dataTmp) |
||||||
|
else: |
||||||
|
totalData = {"total": 0, "delete": 0, |
||||||
|
"create": 0, "modify": 0, "move": 0} |
||||||
|
|
||||||
|
totalData['total'] += 1 |
||||||
|
totalData[eventType] += 1 |
||||||
|
mw.writeFile(totalLogFile, json.dumps(totalData)) |
||||||
|
|
||||||
|
# 设置.user.ini |
||||||
|
def set_user_ini(self, path, up=0): |
||||||
|
os.chdir(path) |
||||||
|
useriniPath = path + '/.user.ini' |
||||||
|
if os.path.exists(useriniPath): |
||||||
|
os.system('chattr +i ' + useriniPath) |
||||||
|
for p1 in os.listdir(path): |
||||||
|
try: |
||||||
|
npath = path + '/' + p1 |
||||||
|
if not os.path.isdir(npath): |
||||||
|
continue |
||||||
|
useriniPath = npath + '/.user.ini' |
||||||
|
if os.path.exists(useriniPath): |
||||||
|
os.system('chattr +i ' + useriniPath) |
||||||
|
if up < 3: |
||||||
|
self.set_user_ini(npath, up + 1) |
||||||
|
except: |
||||||
|
continue |
||||||
|
return True |
||||||
|
|
||||||
|
def unlock(self, path): |
||||||
|
os.system('chattr -R -i {} &> /dev/null'.format(path)) |
||||||
|
os.system('chattr -R -a {} &> /dev/null'.format(path)) |
||||||
|
self.set_user_ini(path) |
||||||
|
|
||||||
|
def close(self, reload=False): |
||||||
|
# 解除锁定 |
||||||
|
sites = self.get_sites() |
||||||
|
print("") |
||||||
|
print("=" * 60) |
||||||
|
print("【{}】正在关闭防篡改,请稍候...".format(mw.formatDate())) |
||||||
|
print("-" * 60) |
||||||
|
for siteInfo in sites: |
||||||
|
tip = self._PLUGIN_PATH + '/tips/' + siteInfo['siteName'] + '.pl' |
||||||
|
if not siteInfo['open'] and not os.path.exists(tip): |
||||||
|
continue |
||||||
|
if reload and siteInfo['open']: |
||||||
|
continue |
||||||
|
if sys.version_info[0] == 2: |
||||||
|
print( |
||||||
|
"【{}】|-解锁网站[{}]".format(mw.formatDate(), siteInfo['siteName'])), |
||||||
|
else: |
||||||
|
os.system( |
||||||
|
"echo -e '【{}】|-解锁网站[{}]\c'".format(mw.formatDate(), siteInfo['siteName'])) |
||||||
|
#print("【{}】|-解锁网站[{}]".format(mw.format_date(),siteInfo['siteName']),end=" ") |
||||||
|
self.unlock(siteInfo['path']) |
||||||
|
if os.path.exists(tip): |
||||||
|
os.remove(tip) |
||||||
|
print("\t=> 完成") |
||||||
|
print("-" * 60) |
||||||
|
print('|-防篡改已关闭') |
||||||
|
print("=" * 60) |
||||||
|
print(">>>>>>>>>>END<<<<<<<<<<") |
||||||
|
|
||||||
|
# 获取网站配置列表 |
||||||
|
def get_sites(self): |
||||||
|
siteconf = self._PLUGIN_PATH + '/sites.json' |
||||||
|
d = mw.readFile(siteconf) |
||||||
|
if not os.path.exists(siteconf) or not d: |
||||||
|
mw.writeFile(siteconf, "[]") |
||||||
|
data = json.loads(mw.readFile(siteconf)) |
||||||
|
|
||||||
|
# 处理多余字段开始 >>>>>>>>>> |
||||||
|
is_write = False |
||||||
|
rm_keys = ['lock', 'bak_open'] |
||||||
|
for i in data: |
||||||
|
i_keys = i.keys() |
||||||
|
if not 'open' in i_keys: |
||||||
|
i['open'] = False |
||||||
|
for o in rm_keys: |
||||||
|
if o in i_keys: |
||||||
|
if i[o]: |
||||||
|
i['open'] = True |
||||||
|
i.pop(o) |
||||||
|
is_write = True |
||||||
|
if is_write: |
||||||
|
mw.writeFile(siteconf, json.dumps(data)) |
||||||
|
# 处理多余字段结束 <<<<<<<<<<<<< |
||||||
|
return data |
||||||
|
|
||||||
|
def __enter__(self): |
||||||
|
self.close() |
||||||
|
|
||||||
|
def __exit__(self, a, b, c): |
||||||
|
self.close() |
||||||
|
|
||||||
|
|
||||||
|
def run(): |
||||||
|
# 初始化inotify对像 |
||||||
|
event = MyEventHandler() |
||||||
|
watchManager = pyinotify.WatchManager() |
||||||
|
starttime = time.time() |
||||||
|
mode = pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO |
||||||
|
|
||||||
|
# 处理网站属性 |
||||||
|
sites = event.get_sites() |
||||||
|
print("=" * 60) |
||||||
|
print("【{}】正在启动防篡改,请稍候...".format(mw.formatDate())) |
||||||
|
print("-" * 60) |
||||||
|
tip_path = event._PLUGIN_PATH + '/tips/' |
||||||
|
if not os.path.exists(tip_path): |
||||||
|
os.makedirs(tip_path) |
||||||
|
speed_file = event._PLUGIN_PATH + '/speed.pl' |
||||||
|
for siteInfo in sites: |
||||||
|
s = time.time() |
||||||
|
tip = tip_path + siteInfo['siteName'] + '.pl' |
||||||
|
if not siteInfo['open']: |
||||||
|
continue |
||||||
|
if sys.version_info[0] == 2: |
||||||
|
print("【{}】|-网站[{}]".format(mw.formatDate(), |
||||||
|
siteInfo['siteName'])), |
||||||
|
else: |
||||||
|
os.system( |
||||||
|
"echo -e '【{}】|-网站[{}]\c'".format(mw.formatDate(), siteInfo['siteName'])) |
||||||
|
# print("【{}】|-网站[{}]".format(public.format_date(),siteInfo['siteName']),end=" ") |
||||||
|
mw.writeFile(speed_file, "正在处理网站[{}],请稍候...".format( |
||||||
|
siteInfo['siteName'])) |
||||||
|
if not os.path.exists(tip): |
||||||
|
event.list_DIR(siteInfo['path'], siteInfo) |
||||||
|
try: |
||||||
|
watchManager.add_watch( |
||||||
|
siteInfo['path'], mode, auto_add=True, rec=True) |
||||||
|
except: |
||||||
|
print(mw.getTracebackInfo()) |
||||||
|
tout = round(time.time() - s, 2) |
||||||
|
mw.writeFile(tip, '1') |
||||||
|
print("\t\t=> 完成,耗时 {} 秒".format(tout)) |
||||||
|
|
||||||
|
# 启动服务 |
||||||
|
endtime = round(time.time() - starttime, 2) |
||||||
|
mw.writeLog('防篡改程序', "网站防篡改服务已成功启动,耗时[%s]秒" % endtime) |
||||||
|
notifier = pyinotify.Notifier(watchManager, event) |
||||||
|
print("-" * 60) |
||||||
|
print('|-防篡改服务已启动') |
||||||
|
print("=" * 60) |
||||||
|
end_tips = ">>>>>>>>>>END<<<<<<<<<<" |
||||||
|
print(end_tips) |
||||||
|
mw.writeFile(speed_file, end_tips) |
||||||
|
notifier.loop() |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
if len(sys.argv) > 1: |
||||||
|
if 'stop' in sys.argv: |
||||||
|
event = MyEventHandler() |
||||||
|
event.close() |
||||||
|
elif 'start' in sys.argv: |
||||||
|
run() |
||||||
|
elif 'unlock' in sys.argv: |
||||||
|
event = MyEventHandler() |
||||||
|
event.unlock(sys.argv[2]) |
||||||
|
elif 'reload' in sys.argv: |
||||||
|
event = MyEventHandler() |
||||||
|
event.close(True) |
||||||
|
else: |
||||||
|
print('error') |
||||||
|
else: |
||||||
|
run() |
Loading…
Reference in new issue