1178 lines
44 KiB
Python
1178 lines
44 KiB
Python
# coding: utf-8
|
||
# -------------------------------------------------------------------
|
||
# YakPanel
|
||
# -------------------------------------------------------------------
|
||
# Copyright (c) 2014-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||
# -------------------------------------------------------------------
|
||
# Author: yakpanel
|
||
# -------------------------------------------------------------------
|
||
# ------------------------------
|
||
# task script app
|
||
# ------------------------------
|
||
|
||
import functools
|
||
import os
|
||
|
||
import sys
|
||
|
||
os.chdir("/www/server/panel")
|
||
sys.path.insert(0, os.path.abspath("/www/server/panel"))
|
||
sys.path.insert(0, "/www/server/panel/class/")
|
||
sys.path.insert(0, "/www/server/panel/class_v2/")
|
||
|
||
from YakTask.conf import logger, CURRENT_TASK_VERSION
|
||
from public.hook_import import hook_import
|
||
|
||
try:
|
||
hook_import()
|
||
except:
|
||
pass
|
||
|
||
|
||
def task():
|
||
def decorator(func):
|
||
@functools.wraps(func)
|
||
def wrapper(*args, **kwargs):
|
||
import warnings
|
||
os.environ["PYTHONWARNINGS"] = "ignore"
|
||
warnings.simplefilter("ignore")
|
||
|
||
pid_dir = "/tmp/brain_task_pids/"
|
||
os.makedirs(pid_dir, exist_ok=True)
|
||
pid = os.path.join(pid_dir, f"{func.__name__}.pid")
|
||
with open(pid, "w") as pf:
|
||
pf.write(str(os.getpid()))
|
||
try:
|
||
return func(*args, **kwargs)
|
||
except Exception:
|
||
import traceback
|
||
traceback.print_exc(file=sys.stderr)
|
||
sys.exit(1)
|
||
finally:
|
||
if os.path.exists(pid):
|
||
os.remove(pid)
|
||
|
||
return wrapper
|
||
|
||
return decorator
|
||
|
||
|
||
# ======================================================
|
||
# 使用task装饰器
|
||
# 所有导入请在函数中进行, 异常由顶层处理记录task.log
|
||
# ======================================================
|
||
|
||
# 项目守护进程
|
||
@task()
|
||
def project_daemon_service():
|
||
from script.project_daemon import main as daemon_main
|
||
daemon_main()
|
||
|
||
# ssl服务
|
||
@task()
|
||
def make_suer_ssl_task():
|
||
from ssl_domainModelV2.service import make_suer_ssl_task
|
||
make_suer_ssl_task()
|
||
|
||
|
||
# push msg 服务
|
||
@task()
|
||
def push_msg():
|
||
from script.push_msg import main
|
||
main()
|
||
|
||
|
||
# site 图标
|
||
@task()
|
||
def find_favicons():
|
||
from data_v2 import data as data_v2_cls
|
||
data_v2_cls().find_stored_favicons()
|
||
|
||
|
||
# 放爆破检查任务
|
||
@task()
|
||
def breaking_through():
|
||
import class_v2.breaking_through as breaking_through
|
||
_breaking_through_obj = breaking_through.main()
|
||
_breaking_through_obj.del_cron()
|
||
_breaking_through_obj.cron_method()
|
||
del _breaking_through_obj
|
||
|
||
|
||
# 更新WAF配置
|
||
@task()
|
||
def update_waf_config():
|
||
import class_v2.data_v2 as data_v2
|
||
dataObject = data_v2.data()
|
||
dataObject.getSiteWafConfig()
|
||
del dataObject
|
||
|
||
|
||
@task()
|
||
def update_monitor_requests():
|
||
import class_v2.data_v2 as data_v2
|
||
dataObject = data_v2.data()
|
||
dataObject.getSiteThirtyTotal(get=None)
|
||
del dataObject
|
||
|
||
|
||
@task()
|
||
def malicious_file_scanning():
|
||
from projectModelV2 import safecloudModel
|
||
safecloud = safecloudModel.main()
|
||
# 调用 webshell_detection 函数
|
||
safecloud.webshell_detection({'is_task': 'true'})
|
||
del safecloud
|
||
|
||
|
||
@task()
|
||
def check_site_monitor():
|
||
import time
|
||
import public
|
||
site_total_uninstall = '{}/data/site_total_uninstall.pl'.format(public.get_panel_path())
|
||
site_total_install_path = '{}/site_total'.format(public.get_setup_path())
|
||
site_total_service = '/etc/systemd/system/site_total.service'
|
||
install_name = ''
|
||
execstr = ""
|
||
if not os.path.exists(site_total_uninstall):
|
||
if not os.path.exists(site_total_install_path): public.ExecShell("rm -f {}".format(site_total_service))
|
||
if public.GetWebServer() != "openlitespeed" and not os.path.exists(
|
||
site_total_service) and not os.path.exists(
|
||
os.path.join(public.get_panel_path(), "plugin/monitor/info.json")) and public.M('tasks').where(
|
||
'name=? and status=?', ('Install [site_total_monitor]', '0')).count() < 1:
|
||
if not public.is_self_hosted():
|
||
execstr = "curl -fsSL " + public.OfficialDownloadBase().rstrip('/') + "/site_total/install.sh|bash"
|
||
install_name = 'Install [site_total_monitor]'
|
||
# sleep_time = 86400
|
||
else:
|
||
if os.path.exists(site_total_service):
|
||
install_name = 'Uninstall [site_total_monitor]'
|
||
execstr = "bash /www/server/site_total/scripts/uninstall.sh"
|
||
if install_name and execstr:
|
||
public.M('tasks').add('id,name,type,status,addtime,execstr',
|
||
(None, install_name, 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr))
|
||
public.writeFile('/tmp/panelTask.pl', 'True')
|
||
|
||
|
||
# 节点监控
|
||
@task()
|
||
def node_monitor():
|
||
# cmd ='nohup {} /www/server/panel/script/node_monitor.py > /dev/null 2>&1 &'.format(python_bin)
|
||
import time
|
||
import traceback
|
||
try:
|
||
from mod.project.node.nodeutil import monitor_all_node_status
|
||
monitor_all_node_status()
|
||
except Exception:
|
||
with open('/tmp/node_monitor.pl', 'w') as f:
|
||
f.write(str(int(time.time())))
|
||
f.write("{}".format(traceback.format_exc()))
|
||
|
||
|
||
# 节点监控依赖包检测
|
||
@task()
|
||
def node_monitor_check():
|
||
import public
|
||
# 定义处理h11的命令变量
|
||
cmd_h11 = "cd /www/server/panel/pyenv/bin && source activate && H11_VERSION=$(./pip3 show h11 | grep -i Version | awk '{print $2}') && if [ \"$H11_VERSION\" != \"0.14.0\" ]; then ./pip3 uninstall h11 -y; fi; ./pip3 install h11==0.14.0"
|
||
|
||
# 定义处理wsproto的命令变量
|
||
cmd_wsproto = "cd /www/server/panel/pyenv/bin && source activate && WSPROTO_VERSION=$(./pip3 show wsproto | grep -i Version | awk '{print $2}') && if [ \"$WSPROTO_VERSION\" != \"1.2.0\" ]; then ./pip3 uninstall wsproto -y; fi; ./pip3 install wsproto==1.2.0"
|
||
public.ExecShell(cmd_h11)
|
||
public.ExecShell(cmd_wsproto)
|
||
|
||
|
||
# 多web
|
||
@task()
|
||
def multi_web_server_daemon():
|
||
import public
|
||
import psutil
|
||
if public.get_multi_webservice_status():
|
||
from panel_site_v2 import panelSite
|
||
from script.restart_services import DaemonManager
|
||
|
||
obj = panelSite()
|
||
pid_paths = {
|
||
'nginx': "/www/server/nginx/logs/nginx.pid",
|
||
'apache': "/www/server/apache/logs/httpd.pid",
|
||
'openlitespeed': "/tmp/lshttpd/lshttpd.pid"
|
||
}
|
||
|
||
for service_name, pid_path in pid_paths.items():
|
||
sys.path.append("../..")
|
||
daemon_info = DaemonManager.safe_read()
|
||
if service_name not in daemon_info:
|
||
continue
|
||
|
||
is_running = False
|
||
if os.path.exists(pid_path):
|
||
pid = public.readFile(pid_path)
|
||
|
||
if pid:
|
||
try:
|
||
psutil.Process(int(pid))
|
||
is_running = True
|
||
except:
|
||
pass
|
||
|
||
if not is_running:
|
||
public.WriteLog("Service Daemon",
|
||
f"Multi-WebServer: An error occurred in {service_name}. Initiate the repair")
|
||
obj.cheak_port_conflict('enable')
|
||
obj.ols_update_config('enable')
|
||
obj.apache_update_config('enable')
|
||
public.webservice_operation('nginx')
|
||
public.WriteLog("Service Daemon", f"Multi-WebServer: The {service_name} repair was successful")
|
||
break
|
||
|
||
|
||
# 日志事件loop
|
||
@task()
|
||
def maillog_event():
|
||
from power_mta.maillog_stat import maillog_event
|
||
maillog_event()
|
||
|
||
|
||
# 聚合日志
|
||
@task()
|
||
def aggregate_maillogs_task():
|
||
from power_mta.maillog_stat import aggregate_maillogs_task_once
|
||
aggregate_maillogs_task_once()
|
||
|
||
|
||
@task()
|
||
def schedule_automations():
|
||
from power_mta.automations import Task
|
||
Task().schedule_once()
|
||
|
||
|
||
# 自动回复mail
|
||
@task()
|
||
def auto_reply_tasks():
|
||
try:
|
||
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py') or not os.path.exists(
|
||
'/www/vmail'):
|
||
return
|
||
if os.path.exists("/www/server/panel/plugin/mail_sys"):
|
||
sys.path.insert(1, "/www/server/panel/plugin/mail_sys")
|
||
try:
|
||
from plugin.mail_sys.mail_sys_main import mail_sys_main
|
||
mail_sys_main().auto_reply_tasks()
|
||
except Exception:
|
||
raise
|
||
except:
|
||
pass
|
||
|
||
|
||
@task()
|
||
def auto_scan_abnormal_mail():
|
||
import time
|
||
import public
|
||
try:
|
||
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists(
|
||
'/www/vmail'):
|
||
return
|
||
# 检查授权
|
||
endtime = public.get_pd()[1]
|
||
curtime = int(time.time())
|
||
if endtime != 0 and endtime < curtime:
|
||
return
|
||
|
||
# 检查是否关闭了自动扫描
|
||
path = '/www/server/panel/plugin/mail_sys/data/abnormal_mail_check_switch'
|
||
if os.path.exists(path):
|
||
return
|
||
|
||
if os.path.exists("/www/server/panel/plugin/mail_sys"):
|
||
sys.path.insert(1, "/www/server/panel/plugin/mail_sys")
|
||
# 导入并执行扫描
|
||
import public.PluginLoader as plugin_loader
|
||
bulk = plugin_loader.get_module('{}/plugin/mail_sys/mail_send_bulk.py'.format(public.get_panel_path()))
|
||
SendMailBulk = bulk.SendMailBulk
|
||
try:
|
||
SendMailBulk().check_abnormal_emails()
|
||
except Exception:
|
||
raise
|
||
except:
|
||
pass
|
||
|
||
|
||
@task()
|
||
def submit_email_statistics():
|
||
import public
|
||
from datetime import datetime, timedelta
|
||
|
||
def _get_yesterday_count2():
|
||
# 获取昨天的开始时间和结束时间(本地时间)
|
||
today = datetime.now()
|
||
yesterday = today - timedelta(days=1)
|
||
|
||
# 昨天 00:00:00
|
||
yesterday_start = datetime(yesterday.year, yesterday.month, yesterday.day, 0, 0, 0)
|
||
|
||
# 昨天 23:59:59
|
||
yesterday_end = datetime(yesterday.year, yesterday.month, yesterday.day, 23, 59, 59)
|
||
|
||
# 转为时间戳
|
||
start_time = int(yesterday_start.timestamp())
|
||
end_time = int(yesterday_end.timestamp())
|
||
|
||
try:
|
||
query = public.S('send_mails').alias('rm').prefix('')
|
||
query.inner_join('senders s', 'rm.postfix_message_id=s.postfix_message_id')
|
||
query.where('s.postfix_message_id is not null')
|
||
if start_time > 0:
|
||
query.where('rm.log_time > ?', start_time - 1)
|
||
if end_time > 0:
|
||
query.where('rm.log_time < ?', end_time + 1)
|
||
|
||
query.where('rm.status =?', 'sent')
|
||
from power_mta.maillog_stat import query_maillog_with_time_section
|
||
ret = query_maillog_with_time_section(query, start_time, end_time)
|
||
allnum = len(ret)
|
||
except:
|
||
allnum = 0
|
||
return allnum
|
||
|
||
if os.path.exists("/www/server/panel/plugin/mail_sys"):
|
||
sys.path.insert(1, "/www/server/panel/plugin/mail_sys")
|
||
|
||
# 添加提交标记 每次提交昨天的 标记存在跳过 不存在添加 删除前天标记
|
||
yesterday = datetime.now() - timedelta(days=1)
|
||
yesterday = yesterday.strftime('%Y-%m-%d')
|
||
cloud_yesterday_submit = f'/www/server/panel/data/{yesterday}_submit_email_statistics.pl'
|
||
if os.path.exists(cloud_yesterday_submit):
|
||
return
|
||
# 判断是否有邮局
|
||
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists('/www/vmail'):
|
||
return
|
||
# 处理昨天数据
|
||
all_data = _get_yesterday_count2()
|
||
if not all_data:
|
||
return
|
||
|
||
# 记录昨日发件总数
|
||
public.set_module_logs('sys_mail', 'sent', all_data)
|
||
|
||
# 添加标记
|
||
public.writeFile(cloud_yesterday_submit, '1')
|
||
# 删除前天标记
|
||
before_yesterday = datetime.now() - timedelta(days=2)
|
||
before_yesterday = before_yesterday.strftime('%Y-%m-%d')
|
||
cloud_before_yesterday_submit = f'/www/server/panel/data/{before_yesterday}_submit_email_statistics.pl'
|
||
if os.path.exists(cloud_before_yesterday_submit):
|
||
os.remove(cloud_before_yesterday_submit)
|
||
|
||
|
||
@task()
|
||
def submit_module_call_statistics():
|
||
import json
|
||
import requests
|
||
import public
|
||
from datetime import datetime, timedelta
|
||
from YakTask.conf import logger
|
||
|
||
# 获取系统时间与utc时间的差值
|
||
def _get_utc_offset_modele():
|
||
# 系统时间戳
|
||
current_local_time = datetime.now()
|
||
current_local_timestamp = int(current_local_time.timestamp())
|
||
|
||
# 获取当前 UTC 时间的时间戳
|
||
# noinspection PyDeprecation
|
||
current_utc_time = datetime.utcnow()
|
||
current_utc_timestamp = int(current_utc_time.timestamp())
|
||
|
||
# 计算时区差值(秒)
|
||
timezone_offset = current_local_timestamp - current_utc_timestamp
|
||
offset = timezone_offset / 3600
|
||
|
||
return offset
|
||
|
||
def _submit_to_cloud(data_submit):
|
||
"""提交用户统计数据 接口调用 安装量等 """
|
||
import panelAuth
|
||
cloudUrl = '{}/api/panel/submit_feature_invoked_bulk'.format(public.OfficialApiBase())
|
||
pdata = panelAuth.panelAuth().create_serverid(None)
|
||
url_headers = {}
|
||
if 'token' in pdata:
|
||
url_headers = {"authorization": "bt {}".format(pdata['token'])}
|
||
|
||
pdata['environment_info'] = json.dumps(public.fetch_env_info())
|
||
|
||
pdata['data'] = data_submit
|
||
pdata['utc_offset'] = _get_utc_offset_modele()
|
||
requests.post(cloudUrl, json=pdata, headers=url_headers)
|
||
return
|
||
|
||
# 添加提交标记 每次提交昨天的 标记存在跳过 不存在添加 删除前天标记 提交过的数据在统计文件里删除
|
||
yesterday = datetime.now() - timedelta(days=1)
|
||
yesterday = yesterday.strftime('%Y-%m-%d')
|
||
cloud_yesterday_submit = '{}/data/{}_submit_module_call_statistics.pl'.format(public.get_panel_path(), yesterday)
|
||
if os.path.exists(cloud_yesterday_submit):
|
||
return
|
||
|
||
# 取文件中yesterday和yesterday以前的数据
|
||
datainfo = {}
|
||
path = '{}/data/mod_log.json'.format(public.get_panel_path())
|
||
if os.path.exists(path):
|
||
try:
|
||
datainfo = json.loads(public.readFile(path))
|
||
except:
|
||
pass
|
||
else:
|
||
return
|
||
|
||
if type(datainfo) != dict:
|
||
datainfo = {}
|
||
# 需要提交的
|
||
data_submit = {}
|
||
# 当天数据
|
||
data_reserve = {}
|
||
|
||
try:
|
||
for date, modules in datainfo.items():
|
||
# 如果日期小于昨天,则提交数据
|
||
if date.strip() <= yesterday.strip():
|
||
data_submit[date] = modules
|
||
else:
|
||
data_reserve[date] = modules
|
||
except:
|
||
logger.error(public.get_error_info())
|
||
|
||
if not data_submit:
|
||
return
|
||
_submit_to_cloud(data_submit)
|
||
|
||
# 将data_reserve 重新写入
|
||
public.writeFile(path, json.dumps(data_reserve))
|
||
|
||
# 添加标记
|
||
public.writeFile(cloud_yesterday_submit, '1')
|
||
# 删除前天标记
|
||
before_yesterday = datetime.now() - timedelta(days=2)
|
||
before_yesterday = before_yesterday.strftime('%Y-%m-%d')
|
||
cloud_before_yesterday_submit = '{}/data/{}_submit_module_call_statistics.pl'.format(
|
||
public.get_panel_path(), before_yesterday
|
||
)
|
||
if os.path.exists(cloud_before_yesterday_submit):
|
||
os.remove(cloud_before_yesterday_submit)
|
||
return
|
||
|
||
|
||
# 邮箱域名黑名单检查报警
|
||
@task()
|
||
def mailsys_domain_blecklisted_alarm():
|
||
import public
|
||
from datetime import datetime, timedelta
|
||
from YakTask.conf import logger
|
||
|
||
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists('/www/vmail'):
|
||
return
|
||
|
||
yesterday = datetime.now() - timedelta(days=1)
|
||
yesterday = yesterday.strftime('%Y-%m-%d')
|
||
cloud_yesterday_submit = '{}/data/{}_mailsys_domain_blecklisted_alarm.pl'.format(
|
||
public.get_panel_path(), yesterday
|
||
)
|
||
if os.path.exists(cloud_yesterday_submit):
|
||
return
|
||
|
||
if os.path.exists("/www/server/panel/plugin/mail_sys"):
|
||
sys.path.insert(1, "/www/server/panel/plugin/mail_sys")
|
||
|
||
# 检查版本 检查是否能查询额度 剩余额度
|
||
import public.PluginLoader as plugin_loader
|
||
bulk = plugin_loader.get_module('{}/plugin/mail_sys/mail_send_bulk.py'.format(public.get_panel_path()))
|
||
SendMailBulk = bulk.SendMailBulk
|
||
try:
|
||
SendMailBulk().check_domain_blacklist_corn()
|
||
|
||
except:
|
||
logger.error(public.get_error_info())
|
||
return
|
||
|
||
# 添加标记
|
||
public.writeFile(cloud_yesterday_submit, '1')
|
||
# 删除前天标记
|
||
before_yesterday = datetime.now() - timedelta(days=2)
|
||
before_yesterday = before_yesterday.strftime('%Y-%m-%d')
|
||
cloud_before_yesterday_submit = '{}/data/{}_mailsys_domain_blecklisted_alarm.pl'.format(
|
||
public.get_panel_path(), before_yesterday
|
||
)
|
||
if os.path.exists(cloud_before_yesterday_submit):
|
||
os.remove(cloud_before_yesterday_submit)
|
||
return
|
||
|
||
|
||
# 更新wordpress漏洞库
|
||
@task()
|
||
def update_vulnerabilities():
|
||
import time
|
||
import public
|
||
import requests, json
|
||
if "/www/server/panel/class_v2/wp_toolkit/" not in sys.path:
|
||
sys.path.insert(1, "/www/server/panel/class_v2/wp_toolkit/")
|
||
# noinspection PyUnresolvedReferences
|
||
import totle_db
|
||
# noinspection PyUnresolvedReferences
|
||
requests.packages.urllib3.disable_warnings()
|
||
|
||
def auto_scan():
|
||
"""
|
||
@name 自动扫描
|
||
@msg 一天一次
|
||
:return:
|
||
"""
|
||
path_time = "/www/server/panel/data/auto_scan.pl"
|
||
if not os.path.exists(path_time):
|
||
public.writeFile(path_time, json.dumps({"time": int(time.time())}))
|
||
share_ip_info = {"time": 0}
|
||
else:
|
||
try:
|
||
share_ip_info = json.loads(public.readFile(path_time))
|
||
except Exception:
|
||
public.ExecShell("rm -f {}".format(path_time))
|
||
share_ip_info = {"time": 0}
|
||
if (int(time.time()) - share_ip_info["time"]) < 86400:
|
||
return public.returnMsg(False, "未达到时间")
|
||
share_ip_info["time"] = int(time.time())
|
||
public.writeFile(path_time, json.dumps(share_ip_info))
|
||
# noinspection PyUnresolvedReferences
|
||
import wordpress_scan
|
||
# noinspection PyInconsistentReturns
|
||
wordpress_scan.wordpress_scan().auto_scan()
|
||
|
||
def M(table, db="wordpress_vulnerabilities"):
|
||
"""
|
||
@name 获取数据库对象
|
||
@param table 表名
|
||
@param db 数据库名
|
||
"""
|
||
with totle_db.Sql(db) as sql:
|
||
return sql.table(table)
|
||
|
||
# noinspection PyInconsistentReturns
|
||
def check_vlun():
|
||
path_time = "/www/server/panel/data/wordpress_check_vlun.pl"
|
||
if not os.path.exists(path_time):
|
||
public.writeFile(path_time, json.dumps({"time": int(time.time())}))
|
||
share_ip_info = {"time": 0}
|
||
else:
|
||
try:
|
||
share_ip_info = json.loads(public.readFile(path_time))
|
||
except Exception:
|
||
public.ExecShell("rm -f {}".format(path_time))
|
||
share_ip_info = {"time": 0}
|
||
if (int(time.time()) - share_ip_info["time"]) < 86400:
|
||
return public.returnMsg(False, "未达到时间")
|
||
share_ip_info["time"] = int(time.time())
|
||
public.writeFile(path_time, json.dumps(share_ip_info))
|
||
load_time = M("wordpress_vulnerabilities", "wordpress_vulnerabilities").order("data_time desc").limit(
|
||
"1").field(
|
||
"data_time").find()
|
||
if type(load_time) != dict: return
|
||
|
||
# noinspection PyInconsistentReturns
|
||
def get_yun_infos(page):
|
||
url = "https://wafapi2.yakpanel.com/api/bt_waf/get_wordpress_scan?size=100&p=" + str(page)
|
||
yun_infos = requests.get(url, verify=False, timeout=60).json()
|
||
for i in yun_infos['res']:
|
||
if i['data_time'] > load_time['data_time']:
|
||
del i['id']
|
||
M("wordpress_vulnerabilities", "wordpress_vulnerabilities").insert(i)
|
||
else:
|
||
return True
|
||
|
||
for i in range(1, 20):
|
||
time.sleep(26)
|
||
if get_yun_infos(i):
|
||
return
|
||
|
||
def check_plugin_close():
|
||
"""
|
||
@name 检查插件是否关闭
|
||
@return True 关闭
|
||
@return False 开启
|
||
"""
|
||
path_time = "/www/server/panel/data/wordpress_check_plugin_close.pl"
|
||
if not os.path.exists(path_time):
|
||
public.writeFile(path_time, json.dumps({"time": int(time.time())}))
|
||
share_ip_info = {"time": 0}
|
||
else:
|
||
try:
|
||
share_ip_info = json.loads(public.readFile(path_time))
|
||
except Exception:
|
||
public.ExecShell("rm -f {}".format(path_time))
|
||
share_ip_info = {"time": 0}
|
||
if (int(time.time()) - share_ip_info["time"]) < 86400:
|
||
return public.returnMsg(False, "未达到时间")
|
||
share_ip_info["time"] = int(time.time())
|
||
public.writeFile(path_time, json.dumps(share_ip_info))
|
||
check_sql = M("plugin_error", "plugin_error").order("id desc").limit("1").field("id").find()
|
||
if type(check_sql) != dict: return None
|
||
time.sleep(30)
|
||
url = "https://wafapi2.yakpanel.com/api/bt_waf/plugin_error_list"
|
||
try:
|
||
res = requests.get(url, verify=False, timeout=60).json()
|
||
except:
|
||
return False
|
||
res_list = res['res']
|
||
for i in res_list:
|
||
if M("plugin_error", "plugin_error").where("slug=? and status=0", i['slug']).count() > 0:
|
||
M("plugin_error", "plugin_error").where("slug=?", i['slug']).update({"status": 1})
|
||
return None
|
||
|
||
# noinspection PyInconsistentReturns
|
||
def get_plugin_update_time():
|
||
"""
|
||
@name 获取插件更新时间
|
||
@ps 一周更新一次
|
||
"""
|
||
path_time = "/www/server/panel/data/wordpress_get_plugin_update_time.pl"
|
||
if not os.path.exists(path_time):
|
||
public.writeFile(path_time, json.dumps({"time": int(time.time())}))
|
||
# 如果第一次运行则三天后再运行
|
||
share_ip_info = {"time": int(time.time()) - 86400 * 2}
|
||
else:
|
||
try:
|
||
share_ip_info = json.loads(public.readFile(path_time))
|
||
except Exception:
|
||
public.ExecShell("rm -f {}".format(path_time))
|
||
share_ip_info = {"time": 0}
|
||
if (int(time.time()) - share_ip_info["time"]) < 259200:
|
||
return public.returnMsg(False, "未达到时间")
|
||
share_ip_info["time"] = int(time.time())
|
||
public.writeFile(path_time, json.dumps(share_ip_info))
|
||
|
||
check_sql = M("wordpress_not_update", "wordpress_not_update").order("id desc").limit("1").field("id").find()
|
||
if type(check_sql) != dict: return
|
||
import random
|
||
def get_plugin_time(id):
|
||
time.sleep(30)
|
||
url = "https://wafapi2.yakpanel.com/api/bt_waf/get_wordpress_not_update?p=" + str(id)
|
||
try:
|
||
res = requests.get(url, verify=False, timeout=60).json()
|
||
if len(res['res']) == 0:
|
||
return True
|
||
for i in res['res']:
|
||
if M("wordpress_not_update", "wordpress_not_update").where("slug=?", i['slug']).count() > 0:
|
||
if M("wordpress_not_update", "wordpress_not_update").where("slug=? and last_time=?", (
|
||
i['slug'], i['last_time'])).count() == 0:
|
||
M("wordpress_not_update", "wordpress_not_update").where("slug=?", i['slug']).update(
|
||
{"last_time": i['last_time']})
|
||
# 随机时间
|
||
except:
|
||
pass
|
||
|
||
for i in range(1, 50):
|
||
time.sleep(random.randint(10, 30))
|
||
if get_plugin_time(i):
|
||
return
|
||
|
||
try:
|
||
auto_scan()
|
||
if not public.is_self_hosted():
|
||
check_vlun()
|
||
check_plugin_close()
|
||
get_plugin_update_time()
|
||
except Exception:
|
||
import traceback
|
||
public.print_log(traceback.format_exc())
|
||
raise
|
||
|
||
|
||
# 更新docker app
|
||
@task()
|
||
def refresh_dockerapps():
|
||
from mod.project.docker.app.appManageMod import AppManage
|
||
AppManage().refresh_apps_list()
|
||
|
||
|
||
# 更新软件商店列表
|
||
@task()
|
||
def update_software_list():
|
||
import public
|
||
import panelPlugin
|
||
# noinspection PyUnresolvedReferences
|
||
get = public.dict_obj()
|
||
get.force = 1
|
||
panelPlugin.panelPlugin().get_cloud_list(get)
|
||
|
||
|
||
# 每10分钟, 502错误检查线程 (夹杂其他任务)
|
||
@task()
|
||
def check502task():
|
||
import json
|
||
import time
|
||
import psutil
|
||
import public
|
||
from YakTask.conf import logger, BASE_PATH, PYTHON_BIN
|
||
|
||
def siteEdate():
|
||
mEdate = time.strftime('%Y-%m-%d', time.localtime())
|
||
edate_pl = '/www/server/panel/data/edate.pl'
|
||
try:
|
||
oldEdate = public.ReadFile(edate_pl)
|
||
if not oldEdate:
|
||
oldEdate = '0000-00-00'
|
||
mEdate = time.strftime('%Y-%m-%d', time.localtime())
|
||
if oldEdate == mEdate:
|
||
return False
|
||
# os.system("nohup " + PYTHON_BIN + " /www/server/panel/script/site_task.py > /dev/null 2>&1 &")
|
||
edateSites = public.M('sites').where(
|
||
'edate>? AND edate<? AND status=?', ('0000-00-00', mEdate, 1)
|
||
).field('id,name').select()
|
||
import panelSite
|
||
siteObject = panelSite.panelSite()
|
||
for site in edateSites:
|
||
get = public.dict_obj()
|
||
get.id = site['id']
|
||
get.name = site['name']
|
||
siteObject.SiteStop(get)
|
||
public.writeFile('data/edate.pl', mEdate)
|
||
except Exception:
|
||
logger.info(public.get_error_info())
|
||
finally:
|
||
del oldEdate
|
||
public.writeFile(edate_pl, mEdate)
|
||
|
||
def sess_expire():
|
||
# session过期处理
|
||
try:
|
||
sess_path = '{}/data/session'.format(BASE_PATH)
|
||
if not os.path.exists(sess_path):
|
||
return
|
||
s_time = time.time()
|
||
f_list = os.listdir(sess_path)
|
||
f_num = len(f_list)
|
||
sess_expired_path = f"{public.get_panel_path()}/data/session_timeout.pl"
|
||
sess_expired = 86400 # default 24H
|
||
if os.path.exists(sess_expired_path):
|
||
try:
|
||
sess_expired = int(public.readFile(sess_expired_path).strip())
|
||
except:
|
||
pass
|
||
for fname in f_list:
|
||
filename = os.path.join(sess_path, fname)
|
||
fstat = os.stat(filename)
|
||
f_time = s_time - fstat.st_mtime
|
||
if f_time > sess_expired:
|
||
os.remove(filename)
|
||
continue
|
||
if fstat.st_size < 256 and len(fname) == 32:
|
||
if f_time > 60 or f_num > 30:
|
||
os.remove(filename)
|
||
continue
|
||
del f_list
|
||
except Exception as ex:
|
||
logger.info(str(ex))
|
||
|
||
# MySQL配额检查
|
||
def mysql_quota_check():
|
||
os.system("nohup " + PYTHON_BIN + " /www/server/panel/script/mysql_quota.py > /dev/null 2>&1 &")
|
||
|
||
# 检查502错误
|
||
def check502():
|
||
# 处理指定PHP版本
|
||
def startPHPVersion(version):
|
||
try:
|
||
fpm = '/etc/init.d/php-fpm-' + version
|
||
php_path = '/www/server/php/' + version + '/sbin/php-fpm'
|
||
if not os.path.exists(php_path):
|
||
if os.path.exists(fpm): os.remove(fpm)
|
||
return False
|
||
|
||
# 尝试重载服务
|
||
os.system(fpm + ' start')
|
||
os.system(fpm + ' reload')
|
||
if checkPHPVersion(version): return True
|
||
|
||
# 尝试重启服务
|
||
cgi = '/tmp/php-cgi-' + version + '.sock'
|
||
pid = '/www/server/php/' + version + '/var/run/php-fpm.pid'
|
||
os.system('pkill -9 php-fpm-' + version)
|
||
time.sleep(0.5)
|
||
if os.path.exists(cgi):
|
||
os.remove(cgi)
|
||
if os.path.exists(pid):
|
||
os.remove(pid)
|
||
os.system(fpm + ' start')
|
||
if checkPHPVersion(version):
|
||
return True
|
||
# 检查是否正确启动
|
||
if os.path.exists(cgi):
|
||
return True
|
||
return False
|
||
except Exception as ex:
|
||
logger.info(ex)
|
||
return True
|
||
|
||
# 检查指定PHP版本
|
||
def checkPHPVersion(version):
|
||
try:
|
||
cgi_file = '/tmp/php-cgi-{}.sock'.format(version)
|
||
if os.path.exists(cgi_file):
|
||
init_file = '/etc/init.d/php-fpm-{}'.format(version)
|
||
if os.path.exists(init_file):
|
||
init_body = public.ReadFile(init_file)
|
||
if not init_body: return True
|
||
uri = "/phpfpm_" + version + "_status?json"
|
||
result = public.request_php(version, uri, '')
|
||
json.loads(result)
|
||
return True
|
||
except:
|
||
logger.info("PHP-{} unreachable detected".format(version))
|
||
return False
|
||
|
||
try:
|
||
phpversions = public.get_php_versions()
|
||
for version in phpversions:
|
||
if version in ['52', '5.2']: continue
|
||
php_path = '/www/server/php/' + version + '/sbin/php-fpm'
|
||
if not os.path.exists(php_path):
|
||
continue
|
||
if checkPHPVersion(version):
|
||
continue
|
||
if startPHPVersion(version):
|
||
public.WriteLog('PHP daemon',
|
||
'PHP-' + version + 'processing exception was detected and has been automatically fixed!',
|
||
not_web=True)
|
||
except Exception as ex:
|
||
logger.info(ex)
|
||
|
||
def auto_backup_panel():
|
||
public.auto_backup_panel()
|
||
|
||
# 更新 GeoLite2-Country.json
|
||
def flush_geoip():
|
||
"""
|
||
@name 检测如果大小小于3M或大于1个月则更新
|
||
@author wzz <2024/5/21 下午5:33>
|
||
@param "data":{"参数名":""} <数据类型> 参数描述
|
||
@return dict{"status":True/False,"msg":"提示信息"}
|
||
"""
|
||
_ips_path = "/www/server/panel/data/firewall/GeoLite2-Country.json"
|
||
m_time_file = "/www/server/panel/data/firewall/geoip_mtime.pl"
|
||
if not os.path.exists(_ips_path):
|
||
os.system("mkdir -p /www/server/panel/data/firewall")
|
||
os.system("touch {}".format(_ips_path))
|
||
|
||
try:
|
||
if not os.path.exists(_ips_path):
|
||
public.downloadFile('{}/install/lib/{}'.format(public.get_url(), os.path.basename(_ips_path)),
|
||
_ips_path)
|
||
public.writeFile(m_time_file, str(int(time.time())))
|
||
return
|
||
|
||
_ips_size = os.path.getsize(_ips_path)
|
||
if os.path.exists(m_time_file):
|
||
_ips_mtime = int(public.readFile(m_time_file))
|
||
else:
|
||
_ips_mtime = 0
|
||
|
||
if _ips_size < 3145728 or time.time() - _ips_mtime > 2592000:
|
||
core = psutil.cpu_count()
|
||
delay = round(1 / (core if core > 0 else 1), 2)
|
||
os.system("rm -f {}".format(_ips_path))
|
||
os.system("rm -f {}".format(m_time_file))
|
||
public.downloadFile('{}/install/lib/{}'.format(public.get_url(), os.path.basename(_ips_path)),
|
||
_ips_path)
|
||
public.writeFile(m_time_file, str(int(time.time())))
|
||
if os.path.exists(_ips_path):
|
||
try:
|
||
import json
|
||
from xml.etree.ElementTree import ElementTree, Element
|
||
from safeModelV2.firewallModel import main as firewall
|
||
|
||
firewallobj = firewall()
|
||
ips_list = json.loads(public.readFile(_ips_path))
|
||
if ips_list:
|
||
bash = os.path.exists('/usr/bin/apt-get') and not os.path.exists("/etc/redhat-release")
|
||
if bash:
|
||
btsh_path = "/etc/ufw/btsh"
|
||
if not os.path.exists(btsh_path):
|
||
os.makedirs(btsh_path)
|
||
|
||
write_map = {}
|
||
for ip_dict in ips_list:
|
||
tmp_path = '{}/{}.sh'.format(btsh_path, ip_dict['brief'])
|
||
commands = [
|
||
f'ipset add {ip_dict["brief"]} {ip}'
|
||
for ip in ip_dict.get("ips", [])
|
||
if firewallobj.verify_ip(ip)
|
||
]
|
||
if commands:
|
||
script_content = "#!/bin/bash\n" + "\n".join(commands) + "\n"
|
||
write_map[tmp_path] = script_content
|
||
time.sleep(delay)
|
||
|
||
for path, content in write_map.items():
|
||
public.writeFile(path, content)
|
||
time.sleep(0.05)
|
||
else:
|
||
for ip_dict in ips_list:
|
||
xml_path = "/etc/firewalld/ipsets/{}.xml.old".format(ip_dict['brief'])
|
||
xml_body = """<?xml version="1.0" encoding="utf-8"?>
|
||
<ipset type="hash:net">
|
||
<option name="maxelem" value="1000000"/>
|
||
</ipset>
|
||
"""
|
||
if os.path.exists(xml_path):
|
||
public.writeFile(xml_path, xml_body)
|
||
else:
|
||
os.makedirs(os.path.dirname(xml_path), exist_ok=True)
|
||
public.writeFile(xml_path, xml_body)
|
||
|
||
tree = ElementTree()
|
||
tree.parse(xml_path)
|
||
root = tree.getroot()
|
||
for ip in ip_dict['ips']:
|
||
if firewallobj.verify_ip(ip):
|
||
entry = Element("entry")
|
||
entry.text = ip
|
||
root.append(entry)
|
||
|
||
firewallobj.format(root)
|
||
tree.write(xml_path, 'utf-8', xml_declaration=True)
|
||
time.sleep(delay)
|
||
except:
|
||
pass
|
||
except:
|
||
try:
|
||
public.downloadFile(
|
||
'{}/install/lib/{}'.format(public.get_url(), os.path.basename(_ips_path)), _ips_path
|
||
)
|
||
public.writeFile(m_time_file, str(int(time.time())))
|
||
except:
|
||
pass
|
||
|
||
auto_backup_panel()
|
||
check502()
|
||
mysql_quota_check()
|
||
siteEdate()
|
||
sess_expire()
|
||
flush_geoip()
|
||
|
||
|
||
@task()
|
||
def count_ssh_logs():
|
||
"""
|
||
@name 统计SSH登录日志
|
||
@return None
|
||
"""
|
||
|
||
import json
|
||
import public
|
||
import re
|
||
from datetime import datetime
|
||
|
||
def parse_journal_disk_usage(output):
|
||
# 使用正则表达式来提取数字和单位
|
||
match = re.search(r'take up (\d+(\.\d+)?)\s*([KMGTP]?)', output)
|
||
total_bytes = 0
|
||
if match:
|
||
value = float(match.group(1)) # 数字
|
||
unit = match.group(3) # 单位
|
||
# 将所有单位转换为字节
|
||
if unit == '':
|
||
unit_value = 1
|
||
elif unit == 'K':
|
||
unit_value = 1024
|
||
elif unit == 'M':
|
||
unit_value = 1024 * 1024
|
||
elif unit == 'G':
|
||
unit_value = 1024 * 1024 * 1024
|
||
elif unit == 'T':
|
||
unit_value = 1024 * 1024 * 1024 * 1024
|
||
elif unit == 'P':
|
||
unit_value = 1024 * 1024 * 1024 * 1024 * 1024
|
||
else:
|
||
unit_value = 0
|
||
|
||
# 计算总字节数
|
||
total_bytes = value * unit_value
|
||
return total_bytes
|
||
|
||
if os.path.exists("/etc/debian_version"):
|
||
version = public.readFile('/etc/debian_version')
|
||
if not version:
|
||
return
|
||
version = version.strip()
|
||
if 'bookworm' in version or 'jammy' in version or 'impish' in version:
|
||
version = 12
|
||
else:
|
||
try:
|
||
version = float(version)
|
||
except:
|
||
version = 11
|
||
|
||
if version >= 12:
|
||
while True:
|
||
filepath = "/www/server/panel/data/ssh_login_counts.json"
|
||
|
||
# 获取今天的日期
|
||
today = datetime.now().strftime('%Y-%m-%d')
|
||
result = {
|
||
'date': today, # 添加日期字段
|
||
'error': 0,
|
||
'success': 0,
|
||
'today_error': 0,
|
||
'today_success': 0
|
||
}
|
||
try:
|
||
filedata = public.readFile(filepath) if os.path.exists(filepath) else public.writeFile(filepath,
|
||
"[]")
|
||
try:
|
||
data_list = json.loads(filedata)
|
||
except:
|
||
data_list = []
|
||
|
||
# 检查是否已有今天的记录,避免重复统计
|
||
found_today = False
|
||
for day in data_list:
|
||
if day['date'] == today:
|
||
found_today = True
|
||
break
|
||
|
||
if found_today:
|
||
break # 如果找到今天的记录,跳出while循环
|
||
|
||
today_err_num1 = int(public.ExecShell(
|
||
"journalctl -u ssh --no-pager -S today |grep -a 'Failed password for' |grep -v 'invalid' |wc -l")[
|
||
0])
|
||
|
||
today_err_num2 = int(public.ExecShell(
|
||
"journalctl -u ssh --no-pager -S today |grep -a 'Connection closed by authenticating user' |grep -a 'preauth' |wc -l")[
|
||
0])
|
||
|
||
today_success = int(
|
||
public.ExecShell("journalctl -u ssh --no-pager -S today |grep -a 'Accepted' |wc -l")[0])
|
||
|
||
# 查看文件大小 判断是否超过5G
|
||
is_bigfile = False
|
||
|
||
res, err = public.ExecShell("journalctl --disk-usage")
|
||
total_bytes = parse_journal_disk_usage(res)
|
||
limit_bytes = 5 * 1024 * 1024 * 1024
|
||
if total_bytes > limit_bytes:
|
||
is_bigfile = True
|
||
|
||
if is_bigfile:
|
||
err_num1 = int(public.ExecShell(
|
||
"journalctl -u ssh --since '30 days ago' --no-pager | grep -a 'Failed password for' | grep -v 'invalid' | wc -l")[
|
||
0])
|
||
err_num2 = int(public.ExecShell(
|
||
"journalctl -u ssh --since '30 days ago' --no-pager --grep='Connection closed by authenticating user|preauth' | wc -l")[
|
||
0])
|
||
success = int(public.ExecShell(
|
||
"journalctl -u ssh --since '30 days ago' --no-pager | grep -a 'Accepted' | wc -l")[0])
|
||
else:
|
||
# 统计失败登陆次数
|
||
err_num1 = int(public.ExecShell(
|
||
"journalctl -u ssh --no-pager |grep -a 'Failed password for' |grep -v 'invalid' |wc -l")[0])
|
||
err_num2 = int(public.ExecShell(
|
||
"journalctl -u ssh --no-pager --grep='Connection closed by authenticating user|preauth' |wc -l")[
|
||
0])
|
||
success = int(public.ExecShell("journalctl -u ssh --no-pager|grep -a 'Accepted' |wc -l")[0])
|
||
result['error'] = err_num1 + err_num2
|
||
# 统计成功登录次数
|
||
result['success'] = success
|
||
result['today_error'] = today_err_num1 + today_err_num2
|
||
result['today_success'] = today_success
|
||
|
||
data_list.insert(0, result)
|
||
data_list = data_list[:7]
|
||
public.writeFile(filepath, json.dumps(data_list))
|
||
except:
|
||
public.writeFile(filepath, json.dumps([{
|
||
'date': today, # 添加日期字段
|
||
'error': 0,
|
||
'success': 0,
|
||
'today_error': 0,
|
||
'today_success': 0
|
||
}]))
|
||
finally:
|
||
del filedata, data_list, result
|
||
|
||
|
||
def task_version_part():
|
||
import public
|
||
import shutil
|
||
import re
|
||
BASE_PATH = public.get_panel_path()
|
||
|
||
def _run_post_update_tasks(from_version, to_version):
|
||
"""
|
||
在版本更新后执行一次性任务。
|
||
:param from_version: 旧版本号
|
||
:param to_version: 新版本号
|
||
:return: bool, 任务是否成功
|
||
"""
|
||
try:
|
||
if from_version == to_version:
|
||
logger.info("Current task version is the same as the last version, no update tasks to run.")
|
||
return True
|
||
|
||
logger.info(
|
||
f"Detected update program start, {from_version} -> {to_version}. Executing one-time update tasks..."
|
||
)
|
||
|
||
if from_version < '1.0.0':
|
||
dirs_to_clean = [
|
||
os.path.join(BASE_PATH, 'logs/sqlite_easy'),
|
||
os.path.join(BASE_PATH, 'logs/sql_log')
|
||
]
|
||
|
||
for dir_path in dirs_to_clean:
|
||
if os.path.isdir(dir_path):
|
||
try:
|
||
shutil.rmtree(dir_path)
|
||
logger.info(f"Removed directory: {dir_path}")
|
||
except Exception as e:
|
||
logger.error(f"Removing directory {dir_path} failed: {str(e)}")
|
||
else:
|
||
logger.info(f"Directory not exists, skipped: {dir_path}")
|
||
|
||
if from_version < '1.0.1':
|
||
sites = public.M('sites').field('id,name,path').select()
|
||
pattern = r'<a class="btlink" href="https://www\.yakpanel\.com/new/download\.html\?invite_code=yakpanele" target="_blank">(.+?)</a>'
|
||
for site in sites:
|
||
temo_dir = [
|
||
os.path.join(site['path'], '404.html'),
|
||
os.path.join(site['path'], '502.html'),
|
||
os.path.join(site['path'], 'index.html'),
|
||
]
|
||
for d in temo_dir:
|
||
if os.path.exists(d):
|
||
html = public.readFile(d)
|
||
result = re.sub(pattern, r'\1', html)
|
||
public.writeFile(d, result.strip())
|
||
|
||
logger.info("All one-time update tasks executed successfully.")
|
||
return True
|
||
except Exception as e:
|
||
logger.error(f"Executing one-time update task failed: {str(e)}")
|
||
return False
|
||
|
||
# run_post_update_tasks
|
||
version_file = '{}/data/task_version.pl'.format(public.get_panel_path())
|
||
last_version = "0.0.0" # 默认为一个很旧的版本
|
||
|
||
if os.path.exists(version_file):
|
||
last_version = public.readFile(version_file) or "0.0.0"
|
||
if _run_post_update_tasks(last_version, CURRENT_TASK_VERSION):
|
||
public.writeFile(version_file, CURRENT_TASK_VERSION)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
if len(sys.argv) < 2:
|
||
print("Usage: python task_script.py <method_name>")
|
||
sys.exit(1)
|
||
|
||
method_name = sys.argv[1]
|
||
if method_name in globals() and callable(globals()[method_name]):
|
||
globals()[method_name]()
|
||
else:
|
||
print(f"Unknown or non-callable method: {method_name}")
|