Initial YakPanel commit
This commit is contained in:
378
script/upgrade_firewall.py
Normal file
378
script/upgrade_firewall.py
Normal file
@@ -0,0 +1,378 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.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 chain and strategy and all([
|
||||
ip in rule_dict,
|
||||
chain.upper() == rule_dict[ip]['Chain'].upper(),
|
||||
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
|
||||
|
||||
from safeModelV2.firewallModel import main as firewallModel
|
||||
firewallmodel = firewallModel()
|
||||
|
||||
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:
|
||||
o, e = public.ExecShell(
|
||||
"firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set {} src -j {}".format(
|
||||
brief, types.upper()))
|
||||
if e != '':
|
||||
public.ExecShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule source ipset="{}" {}\''.format(brief,
|
||||
types.upper()))
|
||||
else:
|
||||
o, e = 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()))
|
||||
if e != '':
|
||||
public.ExecShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule source ipset="' + brief + '" port port="' + ports + '" protocol=tcp ' + 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))
|
||||
o, e = public.ExecShell("ipset destroy " + brief)
|
||||
if e != '':
|
||||
public.ExecShell("firewall-cmd --permanent --delete-ipset=" + brief)
|
||||
|
||||
tmp_file = "/tmp/firewall_{}.txt".format(brief)
|
||||
|
||||
if os.path.exists(tmp_file): # bt 9.5.0之后
|
||||
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")
|
||||
|
||||
else: # bt 9.5.0之前
|
||||
public.M("firewall_country").where("id=?", (country['id'],)).delete()
|
||||
get_tmp = public.dict_obj()
|
||||
get_tmp.country = country['country']
|
||||
get_tmp.types = country['types']
|
||||
get_tmp.ports = country['ports']
|
||||
get_tmp.choose = "all"
|
||||
get_tmp.is_update = True
|
||||
res = firewallmodel.create_countrys(get_tmp)
|
||||
if res['status'] is False:
|
||||
print(f"[ {brief} ] Area rule migrated fail : {res['message']}")
|
||||
|
||||
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("yakpanel: 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}")
|
||||
Reference in New Issue
Block a user