Update to v7.39.0

This commit is contained in:
aapanel.com
2025-06-09 10:50:02 +08:00
parent e65107a251
commit 8bbec64bed
1206 changed files with 98718 additions and 11182 deletions
+134
View File
@@ -0,0 +1,134 @@
# coding: utf-8
# -------------------------------------------------------------------
# aapanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 aapanel(http://www.aapanel.com) All rights reserved.
# -------------------------------------------------------------------
import os
import subprocess
import sys
import threading
def run_cmd(cmd):
try:
subprocess.run(cmd, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return True
except:
return False
def load_iptables():
"""
恢复 iptables 规则
"""
if run_cmd("iptables -C INPUT -j IN_BT"):
print("iptables existed")
else:
if run_cmd("iptables-restore --noflush < /www/server/panel/data/iptablesdata"):
print("iptables restored")
def load_ipset():
"""
恢复 ipset 规则
"""
if run_cmd("ipset restore < /www/server/panel/data/ipsetdata"):
print("ipset restored")
else:
print("ipset existed")
def save_iptables():
"""
保存 iptables 规则
"""
if run_cmd("iptables -C INPUT -j IN_BT"):
if run_cmd(
"iptables-save | grep -E 'IN_BT|OUT_BT|FORWARD_BT|^\*|^COMMIT' | sed 's/^-A INPUT/-I INPUT/; s/^-A OUTPUT/-I OUTPUT/; s/^-A PREROUTING/-I PREROUTING/' > /www/server/panel/data/iptablesdata"):
print("iptables saved")
def save_ipset():
"""
保存 ipset 规则
"""
if run_cmd("ipset save | grep -E '_bt_' > /www/server/panel/data/ipsetdata"):
print("ipset saved")
def dbus_listener():
if not os.path.exists("/sbin/firewalld"):
print("is not Firewalld")
return
cmd = [
"dbus-monitor",
"--system",
"type='signal',path='/org/fedoraproject/FirewallD1',interface='org.fedoraproject.FirewallD1',member='Reloaded'",
"type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.fedoraproject.FirewallD1',arg1=''"
]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
while True:
line = process.stdout.readline().strip()
if not line:
break
if "signal" in line:
if "member=Reloaded" in line:
print("firewalld reload...")
load_iptables()
elif "member=NameOwnerChanged" in line:
print("firewalld restart...")
threading.Timer(3, load_iptables).start()
def main():
import time
if len(sys.argv) < 2:
print("commandstart|reload|stop|save")
sys.exit(1)
command = sys.argv[1]
if command == "start":
load_ipset()
load_iptables()
listener_thread = threading.Thread(target=dbus_listener)
listener_thread.daemon = True
listener_thread.start()
while True:
time.sleep(1)
elif command == "reload":
save_ipset()
save_iptables()
load_ipset()
load_iptables()
elif command == "stop":
save_ipset()
save_iptables()
elif command == "save":
save_ipset()
save_iptables()
elif command == "saveiptables":
save_iptables()
elif command == "saveipset":
save_ipset()
elif command == "loadiptables":
load_iptables()
elif command == "loadipset":
load_ipset()
elif command == "reloadiptables":
save_iptables()
load_iptables()
elif command == "reloadipset":
save_ipset()
load_ipset()
else:
sys.exit(1)
if __name__ == "__main__":
main()
+159
View File
@@ -0,0 +1,159 @@
#!/bin/bash
create_chain() {
# 创建iptables链
# params:表名 链名
local table=$1
local chain=$2
if ! iptables -t "$table" -n -L "$chain" > /dev/null 2>&1; then
iptables -t "$table" -N "$chain"
echo "Created chain $chain in table $table"
else
echo "Chain $chain already exists in table $table"
fi
}
insert_input_output_rules() {
# 在指定的表的链中插入子链
# params"表名:目标链名:需要插入的链"
local rules=("$@")
for rule in "${rules[@]}"; do
IFS=':' read -r table chain target <<< "$rule"
if ! iptables -t "$table" -C "$chain" -j "$target" > /dev/null 2>&1; then
iptables -t "$table" -I "$chain" 1 -j "$target"
echo "Inserted $target to $chain in table $table"
else
echo "$target already in $chain in table $table"
fi
done
}
add_jump_rules() {
# 在指定的表的链中添加跳转规则
# params:表名 目标链名 需要跳转的链
local table=$1
local target_chain=$2
shift 2
local chains=("$@")
for chain in "${chains[@]}"; do
if ! iptables -t "$table" -C "$target_chain" -j "$chain" > /dev/null 2>&1; then
iptables -t "$table" -A "$target_chain" -j "$chain"
echo "Added $chain to $target_chain in table $table"
else
echo "$chain already in $target_chain in table $table"
fi
done
}
create_ipset() {
local ipset_name=$1
if ! ipset list "$ipset_name" > /dev/null 2>&1; then
ipset create "$ipset_name" hash:net maxelem 100000 timeout 0
echo "Created ipset $ipset_name"
else
echo "ipset $ipset_name already exists"
fi
}
add_ipset_rules() {
local rules=("$@")
for rule in "${rules[@]}"; do
IFS=':' read -r chain action direction ipset_name <<< "$rule"
if ! iptables -C "$chain" -m set --match-set "$ipset_name" "$direction" -j "$action" > /dev/null 2>&1; then
iptables -I "$chain" 1 -m set --match-set "$ipset_name" "$direction" -j "$action"
echo "Added $action rule for $ipset_name ($direction) in $chain"
else
echo "$action rule for $ipset_name ($direction) already in $chain"
fi
done
}
# 函数:创建systemd服务
create_systemd_service() {
local exec_path="/www/server/panel/pyenv/bin/python3 /www/server/panel/script/BT-FirewallServices.py"
local service_file="/etc/systemd/system/BT-FirewallServices.service"
if [ ! -f "$service_file" ]; then
/www/server/panel/pyenv/bin/python3 -c "import os,sys; os.chdir('/www/server/panel/'); sys.path.insert(0, 'class/'); sys.path.insert(0, '/www/server/panel/'); import public; public.stop_syssafe();"
cat << EOF > "$service_file"
[Unit]
Description=Firewall and System Event Listener Service
After=network.target
[Service]
ExecStart=$exec_path start
ExecReload=$exec_path reload
ExecStop=$exec_path stop
User=root
Type=simple
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable BT-FirewallServices.service
${exec_path} save
systemctl start BT-FirewallServices.service
echo "Systemd service created and started"
/www/server/panel/pyenv/bin/python3 -c "import os,sys; os.chdir('/www/server/panel/'); sys.path.insert(0, 'class/'); sys.path.insert(0, '/www/server/panel/'); import public; public.start_syssafe();"
else
echo "Systemd service already exists"
fi
}
main() {
# 所有需要创建接管的子链
local chains=(
"filter:IN_BT"
"filter:IN_BT_log"
"filter:IN_BT_user_ip"
"filter:IN_BT_ip"
"filter:IN_BT_user_port"
"filter:OUT_BT"
"filter:OUT_BT_user_ip"
"filter:OUT_BT_user_port"
"filter:IN_BT_Country"
"nat:FORWARD_BT"
)
for chain in "${chains[@]}"; do
IFS=':' read -r table chain_name <<< "$chain"
create_chain "$table" "$chain_name"
done
# 插入接管的子链
local rules=(
"filter:INPUT:IN_BT"
"filter:IN_BT:IN_BT_log"
"filter:IN_BT:IN_BT_user_ip"
"filter:IN_BT:IN_BT_ip"
"filter:IN_BT:IN_BT_user_port"
"filter:IN_BT_ip:IN_BT_Country"
"filter:OUTPUT:OUT_BT"
"filter:OUT_BT:OUT_BT_user_ip"
"filter:OUT_BT:OUT_BT_user_port"
"nat:PREROUTING:FORWARD_BT"
)
insert_input_output_rules "${rules[@]}"
# ipset集合
local ipsets=(
"in_bt_user_accept_ipset"
"in_bt_user_drop_ipset"
"out_bt_user_accept_ipset"
"out_bt_user_drop_ipset"
)
for ipset_name in "${ipsets[@]}"; do
create_ipset "$ipset_name"
done
local ipset_rules=(
"IN_BT_user_ip:ACCEPT:src:in_bt_user_accept_ipset"
"IN_BT_user_ip:DROP:src:in_bt_user_drop_ipset"
"OUT_BT_user_ip:ACCEPT:dst:out_bt_user_accept_ipset"
"OUT_BT_user_ip:DROP:dst:out_bt_user_drop_ipset"
)
add_ipset_rules "${ipset_rules[@]}"
create_systemd_service
systemctl reload BT-FirewallServices
echo "aapanel firewall init finish..."
}
main
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
configure_logging() {
sudo tee /etc/rsyslog.d/ip-daily-log.conf <<EOF
:msg, contains, "DAILY-IP: " -/var/log/IP-DAILY-LOG.log
& stop
EOF
sudo tee /etc/logrotate.d/ip-daily-log <<EOF
/var/log/IP-DAILY-LOG.log {
daily
rotate 3 # 仅保留3天历史
missingok
nocompress # 无需压缩
notifempty
sharedscripts
postrotate
systemctl reload rsyslog >/dev/null 2>&1
endscript
}
EOF
sudo systemctl restart rsyslog
}
sudo iptables -C IN_BT_log -j IN_BT_log_DAILY > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "rules 'iptables -I IN_BT_log -j IN_BT_log_DAILY' Exiting..."
exit 0
else
echo "add rule..."
sudo iptables -N IN_BT_log_DAILY
sudo iptables -I IN_BT_log -j IN_BT_log_DAILY
sudo iptables -A IN_BT_log_DAILY -m recent --name DAILY_IPS --rcheck --seconds 86400 -j RETURN
sudo iptables -A IN_BT_log_DAILY -m recent --name DAILY_IPS --set -j LOG --log-prefix "DAILY-IP: " --log-level 4
ipset create in_bt_malicious_ipset hash:ip maxelem 1000000 timeout 0;
iptables -A IN_BT_ip -m set --match-set in_bt_malicious_ipset src -j DROP
systemctl reload BT-FirewallServices
configure_logging
echo "add rule finish..."
fi
+210 -111
View File
@@ -4,29 +4,22 @@ import os
import subprocess
import sys
import time
from functools import wraps
from typing import Optional, Dict
# import asyncio
import fcntl
os.chdir("/www/server/panel")
sys.path.insert(0, "class/")
sys.path.insert(0, "/www/server/panel/")
sys.path.insert(0, "class_v2/")
import public
if not "class_v2" in sys.path:
sys.path.insert(0, "/www/server/panel/class_v2")
from panel_site_v2 import panelSite
SETUP_PATH = public.get_setup_path()
DATA_PATH = os.path.join(SETUP_PATH, "panel/data")
DAEMON_SERVICE = os.path.join(DATA_PATH, "daemon_service.pl")
if not os.path.exists(DAEMON_SERVICE):
public.writeFile(DAEMON_SERVICE, json.dumps([]))
DAEMON_SERVICE_LOCK = os.path.join(DATA_PATH, "daemon_service_lock.pl")
MANUAL_FLAG = os.path.join(public.get_panel_path(), "data/mod_push_data", "manual_flag.pl")
if not os.path.exists(MANUAL_FLAG):
public.writeFile(MANUAL_FLAG, json.dumps({}))
SERVICES_MAP = {
"apache": (
@@ -56,74 +49,197 @@ SERVICES_MAP = {
}
def add_daemon(service_name: str) -> bool:
if not service_name or service_name not in SERVICES_MAP.keys():
return False
daemon_list = json.loads(public.readFile(DAEMON_SERVICE))
if service_name in daemon_list:
return True
daemon_list.append(service_name)
public.writeFile(DAEMON_SERVICE, json.dumps(list(set(daemon_list))))
return True
def manual_flag(server_name: str = None, open_: str = None) -> Optional[dict]:
if not server_name: # only read
return DaemonManager.safe_read()
# 人为干预
if open_ in ["start", "restart"]: # 激活服务检查
return DaemonManager.active_daemon(server_name)
elif open_ == "stop": # 跳过服务检查
return DaemonManager.skip_daemon(server_name)
return DaemonManager.safe_read()
def del_daemon(service_name: str) -> bool:
if not service_name:
return False
try:
public.writeFile(DAEMON_SERVICE, json.dumps([
x for x in json.loads(public.readFile(DAEMON_SERVICE)) if x != service_name
]))
except:
return False
return True
class DaemonManager:
@classmethod
def __ensure(cls):
if not os.path.exists(DAEMON_SERVICE_LOCK):
with open(DAEMON_SERVICE_LOCK, "w") as _:
pass
if not os.path.exists(MANUAL_FLAG):
public.writeFile(MANUAL_FLAG, json.dumps({}))
if not os.path.exists(DAEMON_SERVICE):
public.writeFile(DAEMON_SERVICE, json.dumps([]))
@staticmethod
def read_lock(func):
@wraps(func)
def wrapper(*args, **kwargs):
DaemonManager.__ensure()
with open(DAEMON_SERVICE_LOCK, "r") as lock_file:
fcntl.flock(lock_file, fcntl.LOCK_SH)
try:
result = func(*args, **kwargs)
finally:
fcntl.flock(lock_file, fcntl.LOCK_UN)
return result
def save_file(path: str, body: str) -> None:
try:
with open(path, "w+") as f:
fcntl.flock(f, fcntl.LOCK_EX)
f.write(body)
fcntl.flock(f, fcntl.LOCK_UN)
except (IOError, OSError) as _:
pass
return wrapper
@staticmethod
def write_lock(func):
@wraps(func)
def wrapper(*args, **kwargs):
DaemonManager.__ensure()
with open(DAEMON_SERVICE_LOCK, "r+") as lock_file:
fcntl.flock(lock_file, fcntl.LOCK_EX)
try:
result = func(*args, **kwargs)
finally:
fcntl.flock(lock_file, fcntl.LOCK_UN)
return result
def read_file(path: str) -> dict:
try:
with open(path, "r") as f:
fcntl.flock(f, fcntl.LOCK_SH)
content = f.read()
fcntl.flock(f, fcntl.LOCK_UN)
return json.loads(content) if content else {}
except json.JSONDecodeError as _:
return {}
except (IOError, OSError) as _:
return {}
return wrapper
@staticmethod
@write_lock
def operate_daemon(service_name: str, flag: int = 0) -> list:
"""
flag: 0 add, 1 del
"""
with open(DAEMON_SERVICE, "r+") as f:
try:
service = json.load(f)
except json.JSONDecodeError:
service = []
if flag == 0:
service.append(service_name)
elif flag == 1:
service = [x for x in service if x != service_name]
service = list(set(service))
f.seek(0)
# noinspection PyTypeChecker
json.dump(service, f)
f.truncate()
return service
def manual_flag(server_name: str = None, open_: str = None) -> dict:
"""人为关闭标记"""
manual = read_file(MANUAL_FLAG)
if server_name and open_ == "stop":
manual[server_name] = 1
save_file(MANUAL_FLAG, json.dumps(manual))
return manual
elif server_name and open_ in ["start", "restart"] and manual.get(server_name) == 1:
manual[server_name] = 0
save_file(MANUAL_FLAG, json.dumps(manual))
return manual
else:
return manual
@staticmethod
@write_lock
def operate_manual_flag(service_name: str, flag: int = 0) -> dict:
"""
flag: 0 normal, 1 manual closed
"""
with open(MANUAL_FLAG, "r+") as f:
try:
service = json.load(f)
except json.JSONDecodeError:
service = {}
service[service_name] = flag
f.seek(0)
# noinspection PyTypeChecker
json.dump(service, f)
f.truncate()
return service
@staticmethod
def remove_daemon(service_name: str) -> list:
"""移除守护进程服务"""
return DaemonManager.operate_daemon(service_name, 1)
@staticmethod
def add_daemon(service_name: str) -> list:
"""添加守护进程服务"""
return DaemonManager.operate_daemon(service_name, 0)
@staticmethod
def skip_daemon(service_name: str) -> dict:
"""跳过服务检查"""
return DaemonManager.operate_manual_flag(service_name, 1)
@staticmethod
def active_daemon(service_name: str) -> dict:
"""激活服务检查"""
return DaemonManager.operate_manual_flag(service_name, 0)
@staticmethod
@read_lock
def safe_read():
try:
res = public.readFile(DAEMON_SERVICE)
return json.loads(res) if res else []
except:
return []
class RestartServices:
COUNT = 30
RECORD: Dict[str, int] = {}
def __init__(self):
self.nick_name = None
self.serviced = None
self.pid_file = None
self.bash = None
def __keep_flag_right(self, manual_info: dict) -> None:
try:
with open(MANUAL_FLAG, "r+") as f:
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
f.seek(0)
# noinspection PyTypeChecker
json.dump(manual_info, f)
f.truncate()
except:
print("Error writing manual flag file")
finally:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
except Exception as e:
print("Error keep_flag_right:", e)
def _overhead(self) -> bool:
if self.nick_name not in self.RECORD:
self.RECORD[self.nick_name] = 0
return True
if self.RECORD[self.nick_name] >= self.COUNT:
return False
self.RECORD[self.nick_name] += 1
return True
def _script(self, act: str) -> None:
try:
if act not in ["start", "stop", "restart", "status"]:
return
# "try to {act} [{self.nick_name}]..."
bash_path = self.bash if self.bash else f"/etc/init.d/{self.serviced}"
if self.pid_file.endswith(".sock"):
try:
os.remove(self.pid_file)
except:
pass
result = subprocess.run(
[bash_path, act],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=10,
)
if result.returncode != 0:
public.WriteLog(
"Service Daemon", f"Failed to {act} {self.nick_name}, error: {result.stderr.strip()}"
)
except subprocess.TimeoutExpired as t:
public.WriteLog(
"Service Daemon", f"Failed to {act} {self.nick_name}, error: time out, {t}"
)
except Exception as e:
print(str(e))
public.WriteLog(
"Service Daemon", f"Failed to {act} {self.nick_name}, error: {e}"
)
def is_support(self) -> bool:
try:
map_info = SERVICES_MAP.get(self.nick_name)
@@ -186,57 +302,43 @@ class RestartServices:
except:
return False
def _script(self, act: str) -> None:
try:
if act not in ["start", "stop", "restart", "status"]:
return
# "try to {act} [{self.nick_name}]..."
bash_path = self.bash if self.bash else f"/etc/init.d/{self.serviced}"
if self.pid_file.endswith(".sock"):
try:
subprocess.run([f"rm -f {self.pid_file}"])
except FileNotFoundError:
pass
except:
pass
subprocess.run(
[bash_path, act],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
except Exception as e:
print(str(e))
@DaemonManager.read_lock
def main(self):
with open(MANUAL_FLAG, 'rb') as fp:
fcntl.flock(fp, fcntl.LOCK_SH)
check_list = json.loads(public.readFile(DAEMON_SERVICE))
manual_info = manual_flag()
manaul = public.readFile(MANUAL_FLAG)
services = public.readFile(DAEMON_SERVICE)
try:
manual_info = json.loads(manaul) if manaul else {}
check_list = json.loads(services) if services else []
except Exception as e:
public.print_log(f"error, {e}")
return
for service in check_list:
self.nick_name = service
if not self.is_support() or not self.is_install_service():
for service in [
x for x in check_list if self.RECORD.get(x, 0) < self.COUNT
]:
self.nick_name = service
if not self.is_support() or not self.is_install_service():
continue
if not self.is_process_running():
if manual_info.get(self.nick_name) == 1:
# service closed maually, skip
continue
public.WriteLog(
"Service Daemon", f"Service [ {self.nick_name} ] is Not Running, Try to start it..."
)
self._overhead()
self._script("start")
time.sleep(3)
if not self.is_process_running():
if manual_info.get(self.nick_name) == 1:
# service closed maually, skip
continue
public.WriteLog(
"Service Daemon", f"Service [ {self.nick_name} ] is Not Running, Try to start it..."
)
self._script("start")
time.sleep(5)
if not self.is_process_running():
self._script("restart")
self._overhead()
self._script("restart")
if manual_info.get(self.nick_name) == 1:
# service is running, remove manual flag
# service is running, fix the wrong flag
manual_info[self.nick_name] = 0
save_file(MANUAL_FLAG, json.dumps(manual_info))
return
return
# under lock file read lock
self.__keep_flag_right(manual_info)
def first_time_installed(data: dict) -> None:
@@ -253,10 +355,7 @@ def first_time_installed(data: dict) -> None:
if setup is False and os.path.exists(pl_name):
os.remove(pl_name)
elif setup is True and not os.path.exists(pl_name):
get = public.dict_obj()
get.name = service
get.status = 1
panelSite().set_restart_task(get)
DaemonManager.add_daemon(service)
public.writeFile(pl_name, "1", mode="w")
else:
pass
+344
View File
@@ -0,0 +1,344 @@
# coding: utf-8
# -------------------------------------------------------------------
# aapanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 aapanel(http://www.aapanel.com) All rights reserved.
# -------------------------------------------------------------------
import os
import sys
os.chdir('/www/server/panel/')
sys.path.insert(0, "class/")
sys.path.insert(0, "class_v2/")
sys.path.insert(0, "/www/server/panel/")
import public
__isFirewalld = False
__isUfw = False
if os.path.exists('/usr/sbin/firewalld') and os.path.exists('/usr/bin/yum'):
__isFirewalld = True
if os.path.exists('/usr/sbin/ufw') and os.path.exists('/usr/bin/apt-get'):
__isUfw = True
def check_ipset_exist(ipset_name):
cmd = "ipset list {}|grep Name".format(ipset_name)
res, err = public.ExecShell(cmd)
if err != "":
return False
return True
def firewalld_process_zone_file(zone_path, rule_dict, zone_name):
"""
处理zone文件(public.xml或trusted.xml),删除匹配的规则。
参数:
zone_path (str): zone文件路径
rule_dict (dict): 规则字典
zone_name (str): Zone名称('public''trusted'
"""
if not os.path.exists(zone_path):
return
import xml.etree.ElementTree as ET
tree = ET.parse(zone_path)
root = tree.getroot()
# 处理<rule>标签
for rule in root.findall('rule'):
source = rule.find('source')
if source is not None:
ip = source.get('address')
action = None
if rule.find('accept') is not None:
action = 'ACCEPT'
elif rule.find('drop') is not None:
action = 'DROP'
if (ip in rule_dict and
rule_dict[ip]['Chain'].upper() == 'INPUT' and
rule_dict[ip]['Zone'] == zone_name and
rule_dict[ip]['Strategy'].upper() == action.upper()):
root.remove(rule)
if zone_name == 'trusted':
for source in root.findall('source'):
ip = source.get('address')
if (ip in rule_dict and
rule_dict[ip]['Chain'].upper() == 'INPUT' and
rule_dict[ip]['Zone'] == 'trusted' and
rule_dict[ip]['Strategy'].upper() == 'ACCEPT'):
root.remove(source)
tree.write(zone_path)
def firewalld_process_direct_file(direct_path, rule_dict):
"""
处理direct.xml文件,删除匹配的OUTPUT规则。
参数:
direct_path (str): direct.xml文件路径
rule_dict (dict): 规则字典
"""
if not os.path.exists(direct_path):
return
import xml.etree.ElementTree as ET
tree = ET.parse(direct_path)
root = tree.getroot()
for rule in root.findall('rule'):
if rule.get('chain') == 'OUTPUT':
rule_text = rule.text.strip()
if rule_text.startswith('-d '):
parts = rule_text.split()
ip = parts[1]
action = parts[-1]
if (ip in rule_dict and
rule_dict[ip]['Chain'].upper() == 'OUTPUT' and
rule_dict[ip]['Strategy'].upper() == action.upper()):
root.remove(rule)
tree.write(direct_path)
def firewalld_batch_remove_ip_rule(rule_dict):
direct_path = '/etc/firewalld/direct.xml'
public_path = '/etc/firewalld/zones/public.xml'
trusted_path = '/etc/firewalld/zones/trusted.xml'
import shutil
if os.path.exists(public_path):
shutil.copyfile(public_path, public_path + '.bak')
firewalld_process_zone_file(public_path, rule_dict, 'public')
if os.path.exists(trusted_path):
shutil.copyfile(trusted_path, trusted_path + '.bak')
firewalld_process_zone_file(trusted_path, rule_dict, 'trusted')
if os.path.exists(direct_path):
shutil.copyfile(direct_path, direct_path + '.bak')
firewalld_process_direct_file(direct_path, rule_dict)
def ufw_batch_remove_ip_rule(rule_dict):
"""
UFW批量删除规则
"""
ufw_config = '/etc/ufw/user.rules'
import shutil
shutil.copy(ufw_config, ufw_config + ".bak" + public.format_date())
lines = public.readFile(ufw_config)
lines = lines.splitlines()
new_lines = []
i = 0
while i < len(lines):
line = lines[i]
if line.startswith('### tuple ###'):
parts = line.split()
action = parts[3] # deny 或 allow
direction = parts[-1] # in 或 out
ip = parts[-2] # IP 地址
if ip == '0.0.0.0/0':
new_lines.extend(lines[i:i + 3]) # 保留这三行
i += 3
continue
chain = 'INPUT' if direction == 'in' else 'OUTPUT' if direction == 'out' else None
strategy = 'DROP' if action == 'deny' else 'ACCEPT' if action == 'allow' else None
if (ip in rule_dict and
chain.upper() == rule_dict[ip]['Chain'].upper() and
strategy.upper() == rule_dict[ip]['Strategy'].upper()):
# 如果匹配,跳过这三行(即删除)
i += 3
else:
# 如果不匹配,保留这三行
new_lines.extend(lines[i:i + 3])
i += 3
else:
# 如果不是规则的开始行,直接保留
new_lines.append(lines[i])
i += 1
with open(ufw_config, 'w') as file:
file.writelines(line if line.endswith('\n') else line + '\n' for line in new_lines)
# 9.5.0-9.6.0
def upgrade_iprule(commodel):
list_address = commodel.firewall.list_address()
if len(list_address) == 0:
return
rule_dict = {}
print("-* IP rule data being migrated...")
for item in list_address:
print("Migration IP rules:{}".format(item))
commodel.iptables.set_chain_rich_ip(item, 'add', item['Chain'])
rule_dict[item['Address']] = {
"Address": item['Address'],
"Family": item['Family'],
"Strategy": item['Strategy'],
"Zone": item.get('Zone'),
"Chain": item['Chain']
}
if __isFirewalld:
firewalld_batch_remove_ip_rule(rule_dict)
elif __isUfw:
ufw_batch_remove_ip_rule(rule_dict)
else:
return
commodel.firewall.reload()
public.ExecShell("systemctl reload BT-FirewallServices")
print("-* IP rule data migration completed...")
# 9.5.0-9.6.0
def upgrade_countrys(commodel):
country_list = public.M('firewall_country').select()
if len(country_list) == 0:
return
print("-* Area rule data being migrated...")
for country in country_list:
ports = country['ports']
brief = country['brief']
types = country['types']
print("Rules for relocation areas:{}".format(brief))
if __isUfw or not __isFirewalld:
if not ports:
public.ExecShell('iptables -D INPUT -m set --match-set ' + brief + ' src -j ' + types.upper())
else:
public.ExecShell(
'iptables -D INPUT -m set --match-set ' + brief + ' src -p tcp --destination-port ' + ports + ' -j ' + types.upper()
)
else:
if not ports:
public.ExecShell(
"firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set {} src -j {}".format(
brief, types.upper())
)
else:
public.ExecShell(
'firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set {} src -p tcp --dport {} -j {}'.format(
brief, ports, types.upper())
)
commodel.firewall.reload()
for country in country_list:
ports = country['ports']
brief = country['brief']
types = country['types']
print("Rules for relocation areas:{}".format(brief))
public.ExecShell("ipset destroy " + brief)
tmp_file = "/tmp/firewall_{}.txt".format(brief)
command = '''grep -q "in_bt_country" {filename} || awk '{{print "add in_bt_country_" $2, $3}}' {filename} > {filename}.tmp && mv {filename}.tmp {filename}'''.format(
filename=tmp_file)
public.ExecShell(command)
_ipset = "in_bt_country_" + brief
public.ExecShell('ipset create {} hash:net maxelem 1000000; ipset restore -f {}'.format(_ipset, tmp_file))
if ports:
public.ExecShell(
'iptables -I IN_BT_Country -m set --match-set {} src -p tcp --destination-port {} -j {}'.format(_ipset,
ports,
types.upper()))
else:
public.ExecShell('iptables -I IN_BT_Country -m set --match-set {} src -j {}'.format(_ipset, types.upper()))
public.ExecShell("systemctl reload BT-FirewallServices")
print("-* Area rule data migration completed...")
# 9.5.0-9.6.0
def upgrade_malicious_ip():
pl_path = "/www/server/panel/config/firewalld_malicious_ip.pl"
if not os.path.exists(pl_path):
return
if not check_ipset_exist("malicious_ipset"):
return
print("-* Migration of malicious IP blocking data in progress...")
# 移除旧规则
if __isUfw or not __isFirewalld:
public.ExecShell('rm -rf /var/log/FIREWALL-ACCESS-LOG*')
public.ExecShell('rm -rf /etc/rsyslog.d/firewall-access-log.conf')
public.ExecShell('rm -rf /etc/logrotate.d/firewall-access-log')
public.ExecShell(
'iptables -D INPUT -m conntrack --ctstate NEW -j LOG --log-prefix "FIREWALL-ACCESS: " --log-level 4')
public.ExecShell('iptables -D INPUT -j IP-DAILY-LOG')
public.ExecShell('iptables -D IP-DAILY-LOG -m recent --name DAILY_IPS --rcheck --seconds 86400 -j RETURN')
public.ExecShell(
'iptables -D IP-DAILY-LOG -m recent --name DAILY_IPS --set -j LOG --log-prefix "DAILY-IP: " --log-level 4')
public.ExecShell('iptables -D IP-DAILY-LOG -j RETURN')
public.ExecShell('iptables -X IP-DAILY-LOG')
public.ExecShell("iptables -D INPUT -m set --match-set malicious_ipset src -j DROP")
else:
public.ExecShell(
"firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 1 -m conntrack --ctstate NEW -j LOG --log-prefix 'FIREWALL-ACCESS: ' --log-level 4")
public.ExecShell("firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 2 -j IP-DAILY-LOG")
public.ExecShell(
"firewall-cmd --permanent --direct --remove-rule ipv4 filter IP-DAILY-LOG 0 -m recent --name DAILY_IPS --rcheck --seconds 86400 -j RETURN")
public.ExecShell(
"firewall-cmd --permanent --direct --remove-rule ipv4 filter IP-DAILY-LOG 1 -m recent --name DAILY_IPS --set -j LOG --log-prefix 'DAILY-IP: ' --log-level 4")
public.ExecShell("firewall-cmd --permanent --direct --remove-rule ipv4 filter IP-DAILY-LOG 2 -j RETURN")
public.ExecShell("firewall-cmd --permanent --direct --remove-chain ipv4 filter IP-DAILY-LOG")
public.ExecShell(
"firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set malicious_ipset src -j DROP")
commodel.firewall.reload()
public.ExecShell("ipset destroy malicious_ipset")
read = public.readFile(pl_path)
if read.strip() == "open":
tmp_file = "/tmp/firewall_malicious_ip.txt"
command = '''grep -q "in_bt" {filename} || awk '{{print "add in_bt_" $2, $3 ,"timeout", 86400}}' {filename} > {filename}.tmp && mv {filename}.tmp {filename}'''.format(
filename=tmp_file)
public.ExecShell(command)
public.ExecShell("sh /www/server/panel/script/open_malicious_ip.sh")
public.ExecShell("ipset restore -f {}".format(tmp_file))
public.ExecShell("systemctl reload BT-FirewallServices")
print("-* Malicious IP blocking data migration completed...")
def upgrade_port_forward(commodel):
"""
升级端口转发规则
"""
port_forward_list = commodel.firewall.list_port_forward()
print("-* Migrating port forwarding rule data...")
for port_forward in port_forward_list:
print("Migrating port forwarding rules:{}".format(port_forward))
info = {
"Family": "ipv4",
"Protocol": port_forward["Protocol"],
"S_Address": port_forward['S_Address'],
"S_Port": port_forward['S_Port'],
"T_Address": port_forward['T_Address'],
"T_Port": port_forward['T_Port'],
}
commodel.firewall.port_forward(info, "remove")
commodel.iptables.port_forward(info, "add")
public.ExecShell("systemctl reload BT-FirewallServices")
commodel.firewall.reload()
print("-* Port forwarding rule data migration complete...")
if __name__ == '__main__':
try:
from firewallModelV2.comModel import main as comModel
import time
commodel = comModel()
upgrade_iprule(commodel)
upgrade_countrys(commodel)
upgrade_malicious_ip()
upgrade_port_forward(commodel)
print("aapanel: FireWall Migrate Service Finish...")
except Exception as e:
import traceback
print("-" * 50)
print(traceback.format_exc())
print("-" * 50)
print(f"Error: FireWall Migrate Error: {e}")