# 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 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 = """ """ 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'(.+?)' 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 ") 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}")