Initial YakPanel commit

This commit is contained in:
Niranjan
2026-04-07 02:04:22 +05:30
commit 2826d3e7f3
5359 changed files with 1390724 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: wzz <wzz@yakpanel.com>
# -------------------------------------------------------------------
# ------------------------------
# 系统防火墙模型 - 底层基类
# ------------------------------
import subprocess
import sys
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
class Base(object):
def __init__(self):
pass
# 2024/3/22 下午 3:18 通用返回
def _result(self, status: bool, msg: str) -> dict:
"""
@name 通用返回
@author wzz <2024/3/22 下午 3:19>
@param status: True/False
msg: 提示信息
@return dict{"status":True/False,"msg":"提示信息"}
"""
return {"status": status, "msg": msg}
# 2024/3/22 下午 4:55 检查是否设置了net.ipv4.ip_forward = 1没有则设置
def check_ip_forward(self) -> dict:
"""
@name 检查是否设置了net.ipv4.ip_forward = 1没有则设置
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
import os
if os.path.exists("/etc/default/ufw"):
# ufw需要开启转发
public.ExecShell(
""" grep -q 'DEFAULT_FORWARD_POLICY="DROP"' /etc/default/ufw && sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw && ufw reload """
)
public.ExecShell(
"iptables -t nat -C POSTROUTING -j MASQUERADE 2>/dev/null || sudo iptables -t nat -A POSTROUTING -j MASQUERADE"
)
stdout, stderr = public.ExecShell("sysctl net.ipv4.ip_forward")
if "net.ipv4.ip_forward = 1" not in stdout:
# 2024/3/22 下午 4:56 永久设置
stdout, stderr = public.ExecShell("echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf")
if stderr:
return self._result(False, "Setting net.ipv4.ip_forward failed, err: {}".format(stderr))
stdout, stderr = public.ExecShell("sysctl -p")
if stderr:
return self._result(False, "Setting net.ipv4.ip_forward failed, err: {}".format(stderr))
return self._result(True, "Set net.ipv4.ip_forward successfully")
return self._result(True, "net.ipv4.ip_forward has been set")
# 2024/3/18 上午 11:35 处理192.168.1.100-192.168.1.200这种ip范围
# 返回192.168.1.100,192.168.1.101,192.168.1...,192.168.1.200列表
def handle_ip_range(self, ip):
"""
@name 处理192.168.1.100-192.168.1.200这种ip范围的ip列表
@author wzz <2024/3/19 下午 4:58>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
ip_range = ip.split("-")
ip_start = ip_range[0]
ip_end = ip_range[1]
ip_start = ip_start.split(".")
ip_end = ip_end.split(".")
ip_start = [int(i) for i in ip_start]
ip_end = [int(i) for i in ip_end]
ip_list = []
for i in range(ip_start[0], ip_end[0] + 1):
for j in range(ip_start[1], ip_end[1] + 1):
for k in range(ip_start[2], ip_end[2] + 1):
for l in range(ip_start[3], ip_end[3] + 1):
ip_list.append("{}.{}.{}.{}".format(i, j, k, l))
return ip_list
# 处理70-79这种端口范围
# 返回70,71,72,...79列表
def handle_port_range(self, port):
"""
@name 处理70-79这种端口范围的端口列表
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
port_range = port.split("-")
port_start = int(port_range[0])
port_end = int(port_range[1])
port_list = []
for i in range(port_start, port_end + 1):
port_list.append(i)
return port_list
def run_command(self, command):
try:
result = subprocess.run(
command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)
return result.stdout, result.stderr
except subprocess.CalledProcessError as e:
return e.stdout, e.stderr

View File

@@ -0,0 +1,761 @@
#!/www/server/panel/pyenv/bin/python3.7
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: wzz <wzz@yakpanel.com>
# -------------------------------------------------------------------
# ------------------------------
# 系统防火墙模型 - firewalld封装库
# ------------------------------
import os
import sys
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
from firewallModelV2.app.appBase import Base
class Firewalld(Base):
def __init__(self):
super().__init__()
self.cmd_str = self._set_cmd_str()
def _set_cmd_str(self) -> str:
return "firewall-cmd"
# 2024/3/20 下午 12:00 获取防火墙状态
def status(self) -> bool:
"""
@name 获取防火墙状态
@author wzz <2024/3/20 下午 12:01>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
stdout, stderr = public.ExecShell("systemctl is-active firewalld")
if "not running" in stdout:
return False
return True
except Exception as _:
return False
# 2024/3/20 下午 12:00 获取防火墙版本号
def version(self) -> str:
"""
@name 获取防火墙版本号
@author wzz <2024/3/20 下午 12:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
stdout, stderr = self.run_command("firewall-cmd --version")
if "FirewallD is not running" in stdout:
return "Firewalld has not started, please start it and try again."
if stderr:
return "Failed to obtain firewalld version, err: {}".format(stderr)
return stdout.strip()
# 2024/3/20 下午 12:08 启动防火墙
def start(self) -> dict:
"""
@name 启动防火墙
@author wzz <2024/3/20 下午 12:08>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
stdout, stderr = self.run_command("systemctl start firewalld")
if stderr:
return self._result(False, "Startup failed, err: {}".format(stderr))
return self._result(True, "Started successfully")
# 2024/3/20 下午 12:10 停止防火墙
def stop(self) -> dict:
"""
@name 停止防火墙
@author wzz <2024/3/20 下午 12:10>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
stdout, stderr = self.run_command("systemctl stop firewalld")
if stderr:
return self._result(False, "Stop failed, err: {}".format(stderr))
return self._result(True, "Stop successfully")
# 2024/3/20 下午 12:11 重启防火墙
def restart(self) -> dict:
"""
@name 重启防火墙
@author wzz <2024/3/20 下午 12:11>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
stdout, stderr = self.run_command("systemctl restart firewalld")
if stderr:
return self._result(False, "The reboot failed, err: {}".format(stderr))
return self._result(True, "The reboot was successful")
# 2024/3/20 下午 12:11 重载防火墙
def reload(self) -> dict:
"""
@name 重载防火墙
@author wzz <2024/3/20 下午 12:11>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
stdout, stderr = self.run_command("firewall-cmd --reload")
if stderr:
return self._result(False, "Overload failed, err: {}".format(stderr))
return self._result(True, "The overload was successful")
# 2024/3/20 下午 12:12 获取所有防火墙端口列表
def list_port(self) -> list:
"""
@name 获取所有防火墙端口列表
@author wzz <2024/3/20 下午 12:12>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self.parse_public_zone()["ports"] + self.list_output_port()
# 2024/3/20 下午 12:12 获取防火墙端口INPUT列表
def list_input_port(self) -> list:
"""
@name 获取防火墙端口列表
@author wzz <2024/3/20 下午 12:12>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self.parse_public_zone()["ports"]
# 2024/3/22 上午 11:28 获取所有OUTPUT的direct 端口规则
def list_output_port(self) -> list:
"""
@name 获取所有OUTPUT的direct 端口规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
list_direct_rules = self.parse_direct_xml()["ports"]
datas = []
for rule in list_direct_rules:
if rule.get("Chain") == "OUTPUT":
datas.append(rule)
return datas
# 2024/3/20 下午 12:21 获取防火墙的rule的ip规则列表
def list_address(self) -> list:
"""
@name 获取防火墙的rule的ip规则列表
@author wzz <2024/3/20 下午 2:45>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self.parse_public_zone()["rules"] + self.parse_trusted_zone()["rules"] + self.list_output_address()
# 2024/3/20 下午 12:21 获取防火墙的rule input的ip规则列表
def list_input_address(self) -> list:
"""
@name 获取防火墙的rule input的ip规则列表
@author wzz <2024/3/20 下午 2:45>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self.parse_public_zone()["rules"] + self.parse_trusted_zone()["rules"]
# 2024/3/22 下午 4:07 获取所有OUTPUT的direct ip规则
def list_output_address(self) -> list:
"""
@name 获取所有OUTPUT的direct ip规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
list_direct_rules = self.parse_direct_xml()["rules"]
datas = []
for rule in list_direct_rules:
if rule.get("Chain") == "OUTPUT":
datas.append(rule)
return datas
# 2024/3/20 下午 5:34 添加或删除防火墙端口
def input_port(self, info: dict, operation: str) -> dict:
"""
@name 添加或删除防火墙端口
@author wzz <2024/3/20 下午 5:34>
@param info:{"Port": args[2], "Protocol": args[3]}
operation: add/remove
@return dict{"status":True/False,"msg":"提示信息"}
"""
if operation not in ["add", "remove"]:
return self._result(False, "Unsupported actions: {}".format(operation))
# 2024/3/25 下午 6:00 处理tcp/udp双协议的端口
if info['Protocol'].find("/") != -1:
stdout, stderr = public.ExecShell(
"{cmd_str} --zone=public --{operation}-port={port}/{prot} --permanent"
.format(
cmd_str=self.cmd_str,
operation=operation,
port=info['Port'],
prot="tcp"
)
)
if stderr:
return self._result(False, "Failed to set the port, err: {}".format(stderr))
stdout, stderr = public.ExecShell(
"{cmd_str} --zone=public --{operation}-port={port}/{prot} --permanent"
.format(
cmd_str=self.cmd_str,
operation=operation,
port=info['Port'],
prot="udp"
)
)
if stderr:
return self._result(False, "Failed to set the port, err: {}".format(stderr))
else:
# 2024/3/25 下午 6:00 处理单协议的端口
stdout, stderr = public.ExecShell(
"{cmd_str} --zone=public --{operation}-port={port}/{prot} --permanent"
.format(
cmd_str=self.cmd_str,
operation=operation,
port=info['Port'],
prot=info['Protocol']
)
)
if stderr:
return self._result(False, "Failed to set the port, err: {}".format(stderr))
return self._result(True, "The inbound port was set successfully")
# 2024/3/20 下午 6:02 设置output的防火墙端口规则
def output_port(self, info: dict, operation: str) -> dict:
"""
@name 设置output的防火墙端口规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
if operation not in ["add", "remove"]:
return self._result(False, "Unsupported actions: {}".format(operation))
if info['Strategy'] == "accept":
info['Strategy'] = "ACCEPT"
elif info['Strategy'] == "drop":
info['Strategy'] = "DROP"
elif info['Strategy'] == "reject":
info['Strategy'] = "REJECT"
else:
return self._result(False, "Unsupported policies: {}".format(info['Strategy']))
info['Port'] = info['Port'].replace("-", ":")
if "/" in info['Protocol']:
info['Protocol'] = info['Protocol'].split("/")
for pp in info['Protocol']:
if not pp in ["tcp", "udp"]:
return self._result(False,
"Failed to set outbound port, err: The protocol is not supported {}".format(pp))
stdout, stderr = public.ExecShell(
"{cmd_str} --permanent --direct --{operation}-rule ipv4 filter OUTPUT {priority} -p {prot} --dport {port} -j {strategy}"
.format(
cmd_str=self.cmd_str,
operation=operation,
priority=info['Priority'],
prot=pp,
port=info['Port'],
strategy=info['Strategy']
)
)
if stderr:
return self._result(False, "Failed to set outbound port, err: {}".format(stderr))
else:
stdout, stderr = public.ExecShell(
"{cmd_str} --permanent --direct --{operation}-rule ipv4 filter OUTPUT {priority} -p {prot} --dport {port} -j {strategy}"
.format(
cmd_str=self.cmd_str,
operation=operation,
priority=info['Priority'],
prot=info['Protocol'],
port=info['Port'],
strategy=info['Strategy']
)
)
if stderr:
return self._result(False, "Failed to set outbound port, err: {}".format(stderr))
return self._result(True, "The outbound port was set successfully")
def set_rich_rule(self, info: dict, operation: str) -> dict:
"""
@name 添加或删除复杂规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
rule_str = "rule family={}".format(info['Family'].lower())
if "Address" in info and info["Address"] != "all":
rule_str += " source address={}".format(info['Address'])
if info.get("Port"):
rule_str += " port port={}".format(info['Port'])
if info.get("Protocol"):
rule_str += " protocol={}".format(info['Protocol'])
rule_str += " {}".format(info['Strategy'])
stdout, stderr = public.ExecShell(
"{} --zone=public --{}-rich-rule='{}' --permanent"
.format(self.cmd_str, operation, rule_str))
if stderr:
return self._result(False, "Failed to set the rule:{} , err: {}".format(operation, rule_str, stderr))
return self._result(True, "The rule is set successfully".format(operation))
# 2024/3/22 上午 11:35 添加或删除复杂规则
def rich_rules(self, info: dict, operation: str) -> dict:
"""
@name 添加或删除复杂规则
@author wzz <2024/3/22 上午 11:35>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
if operation not in ["add", "remove"]:
return self._result(False, "Unsupported rule actions: {}".format(operation))
if "Zone" in info and info["Zone"] == "trusted":
return self.rich_trusted_rule(info, operation)
if "Protocol" in info and info["Protocol"] == "all":
info["Protocol"] = "tcp/udp"
if "Protocol" in info and info['Protocol'].find("/") != -1:
result_list = []
for protocol in info['Protocol'].split("/"):
info['Protocol'] = protocol
result_list.append(self.set_rich_rule(info, operation))
return {"status": True, "msg": result_list}
else:
return self.set_rich_rule(info, operation)
# 2024/7/23 下午4:36 设置trusted区域的ip规则
def rich_trusted_rule(self, info: dict, operation: str) -> dict:
"""
@name 设置trusted区域的ip规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if operation not in ["add", "remove"]:
return self._result(False, "Unsupported actions: {}".format(operation))
if not info['Strategy'].lower() in ("accept", "drop", "reject"):
return self._result(False, "Unsupported policies: {}".format(info['Strategy'].lower()))
rich_rules = self.cmd_str + " --zone=trusted"
if info['Strategy'].lower() == "accept":
rich_rules += " --{0}-source='{1}' --permanent".format(operation, info["Address"])
else:
rich_rules += " --{0}-rich-rule='rule family=\"{1}\" source address=\"{2}\" {3}' --permanent".format(
operation,
info['Family'],
info['Address'],
info['Strategy'].lower()
)
stdout, stderr = public.ExecShell(rich_rules)
if "success" not in stdout and stderr:
return self._result(False, "The setup failed, err: {}".format(stderr))
return self._result(True, "The setup was successful")
except:
return self._result(False, "The setup failed")
# 2024/3/24 下午 10:43 设置output的防火墙ip规则
def output_rich_rules(self, info: dict, operation: str) -> dict:
"""
@name 设置output的防火墙ip规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
if operation not in ["add", "remove"]:
return self._result(False, "Unsupported actions: {}".format(operation))
if info['Strategy'] == "accept":
info['Strategy'] = "ACCEPT"
elif info['Strategy'] == "drop":
info['Strategy'] = "DROP"
elif info['Strategy'] == "reject":
info['Strategy'] = "REJECT"
else:
return self._result(False, "Unsupported policies: {}".format(info['Strategy']))
rich_rules = self.cmd_str + " --permanent --direct --{0}-rule ipv4 filter OUTPUT".format(operation)
if "Priority" in info:
rich_rules += " {}".format(info["Priority"])
if "Address" in info:
rich_rules += " -d {}".format(info["Address"])
if "Protocol" in info:
rich_rules += " -p {}".format(info["Protocol"])
if "Port" in info:
info["Port"] = info["Port"].replace("-", ":")
rich_rules += " --dport {}".format(info["Port"])
if "Strategy" in info:
rich_rules += " -j {}".format(info["Strategy"])
stdout, stderr = public.ExecShell(rich_rules)
if "success" not in stdout and stderr:
return self._result(False, "Failed to set an outbound address, err: {}".format(stderr))
if "NOT_ENABLED" in stderr:
return self._result(False, "The rules don't exist")
return self._result(True, "The outbound address was set successfully")
# 2024/3/22 下午 12:22 解析public区域的防火墙规则
def parse_public_zone(self) -> dict:
"""
@name 解析public区域的防火墙规则
@author wzz <2024/3/22 下午 12:22>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"services": services, "ports": ports, "rules": rules} rules是ip规则
"""
try:
import xml.etree.ElementTree as ET
file_path = "/etc/firewalld/zones/public.xml"
if not os.path.exists(file_path):
return {"services": [], "ports": [], "rules": [], "forward_ports": []}
services = []
ports = []
rules = []
forward_ports = []
tree = ET.parse(file_path)
root = tree.getroot()
for elem in root:
# 2024/3/22 下午 3:01 服务规则
if elem.tag == "service":
services.append(elem.attrib['name'])
# 2024/3/22 下午 3:01 端口规则
elif elem.tag == "port":
port = {
"Protocol": elem.attrib["protocol"],
"Port": elem.attrib["port"],
"Strategy": "accept",
"Family": "ipv4",
"Address": "all",
"Chain": "INPUT",
}
ports.append(port)
# 2024/3/22 下午 3:01 复杂的规则配置
elif elem.tag == "rule":
if not "family" in elem.attrib:
continue
rule = {"Family": elem.attrib["family"]}
for subelem in elem:
rule["Strategy"] = "accept"
if subelem.tag == "source":
if "address" in subelem.attrib:
rule["Address"] = subelem.attrib["address"]
else:
continue
rule["Address"] = "all" if rule["Address"] == "Anywhere" else rule["Address"]
elif subelem.tag == "port":
rule["port"] = {"protocol": subelem.attrib["protocol"], "port": subelem.attrib["port"]}
elif subelem.tag == "drop":
rule["Strategy"] = "drop"
elif subelem.tag == "accept":
rule["Strategy"] = "accept"
elif subelem.tag == "forward-port":
rule["forward-port"] = {
"protocol": subelem.attrib["protocol"],
"S_Port": subelem.attrib["port"],
"T_Address": subelem.attrib["to-addr"],
"T_Port": subelem.attrib["to-port"],
}
# 2024/3/22 下午 3:02 如果端口在里面,就放到端口规则列表中,否则就是ip规则
if "port" in rule:
ports.append({
"Protocol": rule["port"]["protocol"] if "protocol" in rule["port"] else "tcp",
"Port": rule["port"]["port"],
"Strategy": rule["Strategy"] if "Strategy" in rule else "accept",
"Family": rule["Family"] if "Family" in rule else "ipv4",
"Address": rule["Address"] if "Address" in rule else "all",
"Chain": "INPUT",
})
# 2024/3/25 下午 5:01 处理带源ip的端口转发规则
elif "forward-port" in rule:
forward_ports.append({
"type": "port_forward",
"number": len(forward_ports) + 1,
"Protocol": rule["forward-port"]["protocol"],
"S_Address": rule["Address"],
"S_Port": rule["forward-port"]["S_Port"],
"T_Address": rule["forward-port"]["T_Address"],
"T_Port": rule["forward-port"]["T_Port"],
})
else:
if "Address" not in rule:
continue
rule["Chain"] = "INPUT"
rule["Zone"] = "public"
rules.append(rule)
# 2024/3/25 下午 2:57 端口转发规则
elif elem.tag == "forward-port":
port = {
"type": "port_forward",
"number": len(forward_ports) + 1,
"Protocol": elem.attrib["protocol"] if "protocol" in elem.attrib else "tcp",
"S_Address": "",
"S_Port": elem.attrib["port"] if "port" in elem.attrib else "",
"T_Address": elem.attrib["to-addr"] if "to-addr" in elem.attrib else "",
"T_Port": elem.attrib["to-port"] if "to-port" in elem.attrib else "",
}
forward_ports.append(port)
return {"services": services, "ports": ports, "rules": rules, "forward_ports": forward_ports}
except Exception as e:
return {"services": [], "ports": [], "rules": [], "forward_ports": []}
# 2024/3/22 下午 2:32 解析direct.xml的防火墙规则
def parse_direct_xml(self) -> dict:
"""
@name 解析direct.xml的防火墙规则
@author wzz <2024/3/22 下午 2:32>
@param "data":{"参数名":""} <数据类型> 参数描述
@return list[dict{}...]
"""
try:
import xml.etree.ElementTree as ET
file_path = "/etc/firewalld/direct.xml"
if not os.path.exists(file_path):
return {"ports": [], "rules": []}
ports = []
rules = []
tree = ET.parse(file_path)
root = tree.getroot()
for elem in root:
if elem.tag == "rule":
protocol = "tcp"
port = ""
strategy = ""
address = ""
elem_t = elem.text.split(" ")
# 2024/3/22 下午 4:14 解析 Options 得到端口,策略,地址,协议
for i in elem_t:
if i == "-p":
protocol = elem_t[elem_t.index(i) + 1] # 如果找到匹配项,结果为索引+1的值-p tcp,值为tcp
elif i == "--dport":
port = elem_t[elem_t.index(i) + 1]
elif i == "-j":
strategy = elem_t[elem_t.index(i) + 1]
elif i == "-d":
address = elem_t[elem_t.index(i) + 1]
rule = {
"Family": elem.attrib["ipv"],
"Chain": elem.attrib["chain"],
"Strategy": strategy.lower(),
"Address": address if address != "" else "all",
"Zone": "direct",
# "Options": elem.text
}
# 2024/3/22 下午 4:13 如果端口不为空,就是端口规则
if port != "":
rule["Port"] = port
rule["Protocol"] = protocol
ports.append(rule)
# 2024/3/22 下午 4:14 如果端口为空,就是ip规则
else:
rules.append(rule)
return {"ports": ports, "rules": rules}
except Exception as e:
return {"ports": [], "rules": []}
# 2024/7/17 下午3:29 解析trusted区域的防火墙规则
def parse_trusted_zone(self) -> dict:
"""
@name 解析trusted区域的防火墙规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"services": services, "ports": ports, "rules": rules, "forward_ports": forward_ports} rules是ip规则
"""
try:
import xml.etree.ElementTree as ET
file_path = "/etc/firewalld/zones/trusted.xml"
if not os.path.exists(file_path):
return {"services": [], "ports": [], "rules": [], "forward_ports": []}
services = []
ports = []
rules = []
forward_ports = []
tree = ET.parse(file_path)
root = tree.getroot()
for elem in root:
if elem.tag == "source":
rule = {
"Family": "ipv4",
"Strategy": "accept",
"Address": elem.attrib["address"],
"Chain": "INPUT",
"Zone": "trusted",
}
rules.append(rule)
elif elem.tag == "rule":
rule = {
"Family": "ipv4",
"Chain": "INPUT",
"Zone": "trusted",
"Strategy": "accept",
"Address": "",
}
for sb in elem:
if sb.tag == "source":
rule["Address"] = sb.attrib["address"]
elif sb.tag == "drop":
rule["Strategy"] = "drop"
if rule["Address"] != "":
rules.append(rule)
return {"services": services, "ports": ports, "rules": rules, "forward_ports": forward_ports}
except Exception as _:
return {"services": [], "ports": [], "rules": [], "forward_ports": []}
# 2024/3/22 下午 4:54 检查是否开启了masquerade没有则开启
def check_masquerade(self) -> dict:
"""
@name 检查是否开启了masquerade没有则开启
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
stdout, stderr = public.ExecShell("firewall-cmd --query-masquerade")
if "no" in stdout:
stdout, stderr = public.ExecShell("firewall-cmd --add-masquerade")
if stderr:
return self._result(False, "Failed to open masquerade, err: {}".format(stderr))
return self._result(True, "Open masquerade successfully")
return self._result(True, "masquerade is already on")
# 2024/3/22 下午 4:57 设置端口转发
def port_forward(self, info: dict, operation: str) -> dict:
"""
@name 设置端口转发
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
if operation not in ["add", "remove"]:
return self._result(False, "Unsupported actions: {}".format(operation))
if operation == "add":
check_masquerade = self.check_masquerade()
if not check_masquerade["status"]:
return check_masquerade
# 2024/3/25 下午 6:07 处理有源地址的情况
if "S_Address" in info and info["S_Address"] != "":
# 2024/3/25 下午 6:05 处理tcp/udp双协议的情况
if info['Protocol'].find("/") != -1:
rich_rules = self.cmd_str + " --zone=public"
rich_rules += " --{0}-rich-rule='rule family=\"{1}\" source address=\"{2}\" forward-port port=\"{3}\" protocol=\"tcp\" to-port=\"{4}\" to-addr=\"{5}\"' --permanent".format(
operation,
info['Family'],
info['S_Address'],
info['S_Port'],
info['T_Port'],
info['T_Address'],
)
stdout, stderr = public.ExecShell(rich_rules)
if "success" not in stdout and stderr:
if "ALREADY_ENABLED" in stderr:
return self._result(True, "Port forwarding rules already exist")
return self._result(False, "Failed to set port forwarding, err: {}".format(stderr))
rich_rules = self.cmd_str + " --zone=public"
rich_rules += " --{0}-rich-rule='rule family=\"{1}\" source address=\"{2}\" forward-port port=\"{3}\" protocol=\"udp\" to-port=\"{4}\" to-addr=\"{5}\"' --permanent".format(
operation,
info['Family'],
info['S_Address'],
info['S_Port'],
info['T_Port'],
info['T_Address'],
)
stdout, stderr = public.ExecShell(rich_rules)
if "success" not in stdout and stderr:
if "ALREADY_ENABLED" in stderr:
return self._result(True, "Port forwarding rules already exist")
return self._result(False, "Failed to set port forwarding, err: {}".format(stderr))
# 2024/3/25 下午 6:05 处理单协议的情况
else:
rich_rules = self.cmd_str + " --zone=public"
rich_rules += " --{0}-rich-rule='rule family=\"{1}\" source address=\"{2}\" forward-port port=\"{3}\" protocol=\"{4}\" to-port=\"{5}\" to-addr=\"{6}\"'".format(
operation,
info['Family'],
info['S_Address'],
info['S_Port'],
info['Protocol'],
info['T_Port'],
info['T_Address'],
)
rich_rules += " --permanent"
stdout, stderr = public.ExecShell(rich_rules)
if "success" not in stdout and stderr:
if "ALREADY_ENABLED" in stderr:
return self._result(True, "Port forwarding rules already exist")
return self._result(False, "Failed to set port forwarding, err: {}".format(stderr))
# 2024/3/25 下午 6:08 处理没有源地址的情况
else:
# 2024/3/25 下午 6:05 处理tcp/udp双协议的情况
if info['Protocol'].find("/") != -1:
stdout, stderr = public.ExecShell(
"{} --zone=public --{}-forward-port='port={}:proto={}:toport={}:toaddr={}' --permanent"
.format(self.cmd_str, operation, info['S_Port'], "udp", info['T_Port'], info['T_Address'])
)
if "success" not in stdout and stderr:
if "ALREADY_ENABLED" in stderr:
return self._result(True, "Port forwarding rules already exist")
return self._result(False, "Failed to set port forwarding, err: {}".format(stderr))
stdout, stderr = public.ExecShell(
"{} --zone=public --{}-forward-port='port={}:proto={}:toport={}:toaddr={}' --permanent"
.format(self.cmd_str, operation, info['S_Port'], "tcp", info['T_Port'], info['T_Address'])
)
if "success" not in stdout and stderr:
if "ALREADY_ENABLED" in stderr:
return self._result(True, "Port forwarding rules already exist")
return self._result(False, "Failed to set port forwarding, err: {}".format(stderr))
# 2024/3/25 下午 6:09 处理单协议的情况
else:
stdout, stderr = public.ExecShell(
"{} --zone=public --{}-forward-port='port={}:proto={}:toport={}:toaddr={}' --permanent"
.format(self.cmd_str, operation, info['S_Port'], info['Protocol'], info['T_Port'],
info['T_Address'])
)
if "success" not in stdout and stderr:
if "ALREADY_ENABLED" in stderr:
return self._result(True, "Port forwarding rules already exist")
return self._result(False, "Failed to set port forwarding, err: {}".format(stderr))
return self._result(True, "Port forwarding is set successfully")
# 2024/3/25 下午 2:37 获取所有端口转发规则
def list_port_forward(self) -> list:
"""
@name 获取所有端口转发规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return list[dict{}...]
"""
return self.parse_public_zone()["forward_ports"]

View File

@@ -0,0 +1,940 @@
#!/www/server/panel/pyenv/bin/python3.7
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: wzz <wzz@yakpanel.com>
# -------------------------------------------------------------------
# ------------------------------
# 系统防火墙模型 - iptables封装库
# ------------------------------
import re
import subprocess
import sys
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
from firewallModelV2.app.appBase import Base
class Iptables(Base):
def __init__(self):
super().__init__()
self.cmd_str = self._set_cmd_str()
self.protocol = {
"6": "tcp",
"17": "udp",
"0": "all"
}
def _set_cmd_str(self):
return "iptables"
# 2024/3/19 下午 5:00 获取系统防火墙的运行状态
def status(self):
"""
@name 获取系统防火墙的运行状态
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return "running"
# 2024/3/19 下午 5:00 获取系统防火墙的版本号
def version(self):
"""
@name 获取系统防火墙的版本号
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = public.ExecShell("iptables -v 2>&1|awk '{print $2}'|head -1")[0].replace("\n", "")
if result == "":
return "Unknown version of iptables"
return result
except Exception as _:
return "Unknown version"
# 2024/3/19 下午 5:00 启动防火墙
def start(self):
"""
@name 启动防火墙
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self._result(True, public.lang("The current system firewall is iptables and does not support setting status."))
# 2024/3/19 下午 5:00 停止防火墙
def stop(self):
"""
@name 停止防火墙
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self._result(True, public.lang("The current system firewall is iptables and does not support stopping."))
# 2024/3/19 下午 4:59 重启防火墙
def restart(self):
"""
@name 重启防火墙
@author wzz <2024/3/19 下午 4:59>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self._result(True, public.lang("The current system firewall is iptables and does not support restarting."))
# 2024/3/19 下午 4:59 重载防火墙
def reload(self):
"""
@name 重载防火墙
@author wzz <2024/3/19 下午 4:59>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self._result(True, public.lang("The current system firewall is iptables,which does not support reloading. "))
# 2024/3/19 下午 3:36 检查表名是否合法
def check_table_name(self, table_name):
"""
@name 检查表名是否合法
@param "table_name": "filter/nat/mangle/raw/security"
@return dict{"status":True/False,"msg":"提示信息"}
"""
table_names = ['filter', 'nat', 'mangle', 'raw', 'security']
if table_name not in table_names:
return False
return True
# 2024/3/19 下午 3:55 解析规则列表输出,返回规则列表字典
def parse_rules(self, stdout):
"""
@name 解析规则列表输出,返回规则列表字典
@author wzz <2024/3/19 下午 3:53>
字段含义:
"number": 规则编号,对应规则在链中的顺序。
"chain": 规则所属的链的名称。
"pkts": 规则匹配的数据包数量。
"bytes": 规则匹配的数据包字节数。
"target": 规则的目标动作,表示数据包匹配到该规则后应该执行的操作。
"prot": 规则适用的协议类型。
"opt": 规则的选项,包括规则中使用的匹配条件或特定选项。
"in": 规则匹配的数据包的输入接口。
"out": 规则匹配的数据包的输出接口。
"source": 规则匹配的数据包的源地址。
"destination": 规则匹配的数据包的目标地址。
"options": 规则的其他选项或说明,通常是规则中的注释或附加信息。
protocol(port协议头中数字对应的协议类型):
0: 表示所有协议
1: ICMPInternet 控制消息协议)
6: TCP传输控制协议
17: UDP用户数据报协议
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
lines = stdout.strip().split('\n')
rules = []
current_chain = None
for line in lines:
if line.startswith("Chain"):
current_chain = line.split()[1]
elif (line.startswith("target") or line.strip() == "" or "source" in line or
"Warning: iptables-legacy tables present" in line):
# 过滤表头,空行,警告
continue
else:
rule_info = line.split()
rule = {
"number": rule_info[0],
"chain": current_chain,
"pkts": rule_info[1],
"bytes": rule_info[2],
"target": rule_info[3],
"prot": rule_info[4],
"opt": rule_info[5],
"in": rule_info[6],
"out": rule_info[7],
"source": rule_info[8],
"destination": rule_info[9],
"options": " ".join(rule_info[10:]).strip()
}
rules.append(rule)
return rules
# 2024/3/19 下午 3:02 列出指定表的指定链的规则
def list_rules(self, parm):
"""
@name 列出指定表的指定链的规则
@author wzz <2024/3/19 下午 3:02>
@param
@return
"""
try:
if not self.check_table_name(parm['table']):
return "Error: Unsupported table name."
stdout = subprocess.check_output(
[self.cmd_str, '-t', parm['table'], '-L', parm['chain_name'], '-nv', '--line-numbers'],
stderr=subprocess.STDOUT, universal_newlines=True
)
return self.parse_rules(stdout)
except Exception as _:
return []
# 2024/4/29 下午12:16 列出iptables中所有INPUT和OUTPUT的端口规则
def list_port(self):
"""
@name 列出iptables中所有INPUT和OUTPUT的端口规则
@return [{
"Protocol": "tcp",
"Port": "8888",
"Strategy": "accept",
"Family": "ipv4",
"Address": "all",
"Chain": "INPUT"
}]
"""
try:
list_port = self.list_input_port() + self.list_output_port()
for i in list_port:
i["Strategy"] = i["Strategy"].lower()
return list_port
except Exception as e:
return []
# 2024/4/29 下午2:39 列出防火墙中所有的INPUT端口规则
def list_input_port(self):
"""
@name 列出防火墙中所有的INPUT端口规则
@return [{
"Protocol": "tcp",
"Port": "8888",
"Strategy": "accept",
"Family": "ipv4",
"Address": "all",
"Chain": "INPUT"
}]
"""
try:
list_port = self.get_chain_port("INPUT")
for i in list_port:
i["Strategy"] = i["Strategy"].lower()
return list_port
except Exception as e:
return []
# 2024/4/29 下午2:39 列出防火墙中所有的OUTPUT端口规则
def list_output_port(self):
"""
@name 列出防火墙中所有的OUTPUT端口规则
@return [{
"Protocol": "tcp",
"Port": "8888",
"Strategy": "accept",
"Family": "ipv4",
"Address": "all",
"Chain": "OUTPUT"
}]
"""
try:
list_port = self.get_chain_port("OUTPUT")
for i in list_port:
i["Strategy"] = i["Strategy"].lower()
return list_port
except Exception as e:
return []
# 2024/4/29 下午3:28 根据链来获取端口规则暂时只支持INPUT/OUTPUT链
def get_chain_port(self, chain):
"""
@name 根据链来获取端口规则
@author wzz <2024/4/29 下午3:29>
@param chain = INPUT/OUTPUT
@return [{
"Protocol": "tcp",
"Port": "8888",
"Strategy": "accept",
"Family": "ipv4",
"Address": "all",
"Chain": "OUTPUT"
}]
"""
if chain not in ["INPUT", "OUTPUT"]:
return []
try:
stdout = self.get_chain_data(chain)
if stdout == "":
return []
lines = stdout.strip().split('\n')
rules = []
for line in lines:
if line.startswith("Chain"):
continue
if not "dpt:" in line and not "multiport sports" in line:
continue
rule_info = line.split()
if rule_info[0] == "num":
continue
if not rule_info[3] in ["ACCEPT", "DROP", "REJECT"]:
continue
if not rule_info[4] in self.protocol:
continue
if not "dpt" in rule_info[-1] and not "-" in rule_info[-1] and not ":" in rule_info[-1]:
continue
if ":" in rule_info[-1] and not "dpt" in rule_info[-1]:
Port = rule_info[-1]
elif "-" in rule_info[-1]:
Port = rule_info[-5].split(":")[1]
else:
Port = rule_info[-1].split(":")[1]
if "source IP range" in line and "multiport sports" in line:
Address = rule_info[-4]
elif not "0.0.0.0/0" in rule_info[8]:
Address = rule_info[8]
elif "-" in rule_info[-1]:
Address = rule_info[-1]
else:
Address = "all"
rule = {
"Protocol": self.protocol[rule_info[4]],
"Port": Port,
"Strategy": rule_info[3],
"Family": "ipv4",
"Address": Address,
"Chain": chain,
}
rules.append(rule)
return rules
except Exception as e:
return []
# 2024/4/29 下午3:28 根据链来获取IP规则暂时只支持INPUT/OUTPUT链
def get_chain_ip(self, chain):
"""
@name 根据链来获取端口规则
@author wzz <2024/4/29 下午3:29>
@param chain = INPUT/OUTPUT
@return [
{
"Family": "ipv4",
"Address": "192.168.1.190",
"Strategy": "accept",
"Chain": "INPUT"
}
]
"""
if chain not in ["INPUT", "OUTPUT"]:
return []
try:
stdout = self.get_chain_data(chain)
if stdout == "":
return []
lines = stdout.strip().split('\n')
rules = []
for line in lines:
if line.startswith("Chain"):
continue
if "dpt:" in line or "multiport sports" in line:
continue
rule_info = line.split()
if rule_info[0] == "num":
continue
if not rule_info[3] in ["ACCEPT", "DROP", "REJECT"]:
continue
if not rule_info[4] in self.protocol:
continue
Address = ""
if not "0.0.0.0/0" in rule_info[8]:
Address = rule_info[8]
elif "0.0.0.0/0" in rule_info[8] and "-" in rule_info[-1]:
Address = rule_info[-1]
if Address == "":
continue
rule = {
"Family": "ipv4",
"Address": Address,
"Strategy": rule_info[3],
"Chain": chain,
}
rules.append(rule)
return rules
except Exception as _:
return []
# 2024/4/29 下午4:01 获取指定链的数据
def get_chain_data(self, chain):
"""
@name 获取指定链的数据
@author wzz <2024/4/29 下午4:01>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
cmd = "{} -t filter -L {} -nv --line-numbers".format(self.cmd_str, chain)
stdout, stderr = public.ExecShell(cmd)
return stdout
except Exception as _:
return ""
# 2024/4/29 下午2:46 列出防火墙中所有的INPUT和OUTPUT的ip规则
def list_address(self):
"""
@name 列出防火墙中所有的ip规则
@author wzz <2024/4/29 下午2:47>
@param "data":{"参数名":""} <数据类型> 参数描述
@return
"""
try:
list_address = self.get_chain_ip("INPUT") + self.get_chain_ip("OUTPUT")
for i in list_address:
i["Strategy"] = i["Strategy"].lower()
return list_address
except Exception as _:
return []
# 2024/4/29 下午2:48 列出防火墙中所有input的ip规则
def list_input_address(self):
"""
@name 列出防火墙中所有input的ip规则
@author wzz <2024/4/29 下午2:48>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
list_address = self.get_chain_ip("INPUT")
for i in list_address:
i["Strategy"] = i["Strategy"].lower()
return list_address
except Exception as _:
return []
# 2024/4/29 下午2:49 列出防火墙中所有output的ip规则
def list_output_address(self):
"""
@name 列出防火墙中所有output的ip规则
@author wzz <2024/4/29 下午2:49>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
list_address = self.get_chain_ip("OUTPUT")
for i in list_address:
i["Strategy"] = i["Strategy"].lower()
return list_address
except Exception as _:
return []
# 2024/4/29 下午2:49 添加INPUT端口规则
def input_port(self, info, operation):
"""
@name 添加INPUT端口规则
@author wzz <2024/4/29 下午2:50>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
return self.set_chain_port(info, operation, "INPUT")
except Exception as e:
return self._result(False, public.lang("Failed to set a port rule:{}", str(e)))
# 2024/4/29 下午2:50 设置output端口策略
def output_port(self, info, operation):
"""
@name 设置output端口策略
@author wzz <2024/4/29 下午2:50>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
return self.set_chain_port(info, operation, "OUTPUT")
except Exception as e:
return self._result(False, public.lang("Failed to set a port rule:{}", str(e)))
# 2024/4/29 下午4:49 添加/删除指定链的端口规则
def set_chain_port(self, info, operation, chain):
"""
@name 添加/删除指定链的端口规则
@author wzz <2024/4/29 下午4:49>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if not chain in ["INPUT", "OUTPUT"]:
return self._result(False, public.lang("Failed to set a port rule:{}", "Unsupported chain types"))
if info['Protocol'] not in ["tcp", "udp"]:
return self._result(False, public.lang("Failed to set a port rule:{}", "Unsupported protocol types"))
if info["Strategy"] == "accept":
info["Strategy"] = "ACCEPT"
elif info["Strategy"] == "drop":
info["Strategy"] = "DROP"
elif info["Strategy"] == "reject":
info["Strategy"] = "REJECT"
else:
return self._result(False, public.lang("Failed to set a port rule:{}",
"The type of policy that is not supported"))
if operation == "add":
operation = "-I"
elif operation == "remove":
operation = "-D"
rule = "{} -t filter {} {} -p {} --dport {} -j {}".format(
self.cmd_str,
operation,
chain,
info['Protocol'],
info['Port'],
info['Strategy']
)
stdout, stderr = public.ExecShell(rule)
if stderr and "setlocale: LC_ALL: cannot change locale (en_US.UTF-8)" not in stderr:
return self._result(False, public.lang("Failed to set a port rule:{}", stderr))
return self._result(True, public.lang("The port rule was successfully configured"))
except Exception as e:
return self._result(False, public.lang("Failed to set a port rule:{}", str(e)))
# 2024/4/29 下午5:01 添加/删除指定链的复杂端口规则
def set_chain_rich_port(self, info, operation, chain):
"""
@name 添加/删除指定链的复杂端口规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if not chain in ["INPUT", "OUTPUT"]:
return self._result(False, public.lang("Failed to set a port rule:{}", "Unsupported chain types"))
if "Address" in info and info["Address"] == "":
info["Address"] = "all"
if "Address" in info and public.is_ipv6(info['Address']):
return self._result(False, public.lang("Failed to set a port rule:{}",
"IPV 6 addresses that are not supported"))
if info['Protocol'] not in ["tcp", "udp"]:
return self._result(False, public.lang("Failed to set a port rule:{}", "Unsupported protocol types"))
if info["Strategy"] == "accept":
info["Strategy"] = "ACCEPT"
elif info["Strategy"] == "drop":
info["Strategy"] = "DROP"
elif info["Strategy"] == "reject":
info["Strategy"] = "REJECT"
else:
return self._result(False, public.lang("Failed to set a port rule:{}",
"The type of policy that is not supported"))
if operation == "add":
operation = "-I"
elif operation == "remove":
operation = "-D"
info['Port'] = info['Port'].replace("-", ":")
info["Address"] = info["Address"].replace(":", "-")
if ":" in info['Port'] or "-" in info['Port']:
if ":" in info["Address"] or "-" in info["Address"]:
# iptables -t filter -I INPUT -m iprange --src-range 192.168.1.100-192.168.1.200 -p tcp -m multiport --sports 8000:9000 -j ACCEPT
rule = "{} -t filter {} {} -m iprange --src-range {} -p {} -m multiport --sports {} -j {}".format(
self.cmd_str,
operation,
chain,
info['Address'],
info['Protocol'],
info['Port'],
info['Strategy']
)
else:
# iptables -t filter -I INPUT -p tcp -m multiport --sports 8000:9000 -s 192.168.1.100 -j ACCEPT
rule = "{} -t filter {} {} -p {} -m multiport --sports {} -s {} -j {}".format(
self.cmd_str,
operation,
chain,
info['Protocol'],
info['Port'],
info['Address'],
info['Strategy']
)
else:
if ":" in info["Address"] or "-" in info["Address"]:
# iptables -t filter -I OUTPUT -p tcp --dport 22333 -m iprange --src-range 192.168.1.100-192.168.1.200 -j ACCEPT
rule = "{} -t filter {} {} -p {} --dport {} -m iprange --src-range {} -j {}".format(
self.cmd_str,
operation,
chain,
info['Protocol'],
info['Port'],
info['Address'],
info['Strategy']
)
else:
# iptables -t filter -I OUTPUT -p tcp --dport 22333 -s 192.168.1.0/24 -j ACCEPT
rule = "{} -t filter {} {} -p {} --dport {} -s {} -j {}".format(
self.cmd_str,
operation,
chain,
info['Protocol'],
info['Port'],
info['Address'],
info['Strategy']
)
stdout, stderr = public.ExecShell(rule)
if stderr and "setlocale: LC_ALL: cannot change locale (en_US.UTF-8)" not in stderr:
return self._result(False, public.lang("Failed to set a port rule:{}", stderr))
return self._result(True, public.lang("The port rule was successfully configured"))
except Exception as e:
return self._result(False, public.lang("Failed to set a port rule:{}", str(e)))
# 2024/4/29 下午5:01 添加/删除指定链的复杂ip规则
def set_chain_rich_ip(self, info, operation, chain):
"""
@name 添加/删除指定链的复杂ip规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if not chain in ["INPUT", "OUTPUT"]:
return self._result(False, public.lang("Failed to set the rule:{}", "Unsupported chain types"))
if "Address" in info and info["Address"] == "":
info["Address"] = "all"
if "Address" in info and public.is_ipv6(info['Address']):
return self._result(False, public.lang("Failed to set the rule:{}", "IPV 6 addresses that are not supported"))
if info["Strategy"] == "accept":
info["Strategy"] = "ACCEPT"
elif info["Strategy"] == "drop":
info["Strategy"] = "DROP"
elif info["Strategy"] == "reject":
info["Strategy"] = "REJECT"
else:
return self._result(False, public.lang("Failed to set the rule:{}",
"The type of policy that is not supported"))
if operation == "add":
operation = "-I"
elif operation == "remove":
operation = "-D"
if ":" in info["Address"] or "-" in info["Address"]:
# iptables -t filter -I INPUT -m iprange --src-range 192.168.1.100-192.168.1.200 -j ACCEPT
rule = "{} -t filter {} {} -m iprange --src-range {} -j {}".format(
self.cmd_str,
operation,
chain,
info['Address'],
info['Strategy']
)
else:
# iptables -t filter -I INPUT -s 192.168.1.100 -j ACCEPT
rule = "{} -t filter {} {} -s {} -j {}".format(
self.cmd_str,
operation,
chain,
info['Address'],
info['Strategy']
)
stdout, stderr = public.ExecShell(rule)
if stderr and "setlocale: LC_ALL: cannot change locale (en_US.UTF-8)" not in stderr:
return self._result(False, public.lang("Failed to set the rule:{}", stderr))
return self._result(True, public.lang("The rule is set successfully"))
except Exception as e:
return self._result(False, public.lang("Failed to set the rule:{}", str(e)))
# 2024/4/29 下午2:51 INPUT复杂一些的规则管理
def rich_rules(self, info, operation):
"""
@name
@author wzz <2024/4/29 下午2:51>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if "Priority" in info and not "Port" in info:
return self.set_chain_rich_ip(info, operation, "INPUT")
else:
return self.set_chain_rich_port(info, operation, "INPUT")
except Exception as e:
return self._result(False, public.lang("Failed to set a port rule:{}", str(e)))
# 2024/4/29 下午2:52 OUTPUT复杂一些的规则管理
def output_rich_rules(self, info, operation):
"""
@name OUTPUT复杂一些的规则管理
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if "Priority" in info and not "Port" in info:
return self.set_chain_rich_ip(info, operation, "OUTPUT")
else:
return self.set_chain_rich_port(info, operation, "OUTPUT")
except Exception as e:
return self._result(False, public.lang("Failed to set a port rule:{}", str(e)))
# 2024/3/19 下午 3:03 清空指定链中的所有规则
def flush_chain(self, chain_name):
"""
@name 清空指定链中的所有规则
@author wzz <2024/3/19 下午 3:03>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
subprocess.check_output(
[self.cmd_str, '-F', chain_name], stderr=subprocess.STDOUT, universal_newlines=True
)
return chain_name + " chain flushed successfully."
except Exception as _:
return "Failed to flush " + chain_name + " chain."
# 2024/3/19 下午 3:03 获取当前系统中可用的链的名称列表
def get_chain_names(self, parm):
"""
@name 获取当前系统中可用的链的名称列表
@author wzz <2024/3/19 下午 3:03>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if not self.check_table_name(parm['table']):
return "Error: Unsupported table name."
stdout = subprocess.check_output(
[self.cmd_str, '-t', parm['table'], '-L'], stderr=subprocess.STDOUT, universal_newlines=True
)
chain_names = re.findall(r"Chain\s([A-Z]+)", stdout)
return chain_names
except Exception as e:
return []
# 2024/3/19 下午 3:17 构造端口转发规则然后调用insert_rule方法插入规则
def port_forward(self, info, operation):
"""
@name 构造端口转发规则然后调用insert_rule方法插入规则
@param "info": {
"Protocol": "tcp/udp",
"S_Port": "80",
"T_Address": "0.0.0.0/0",
"T_Port": "8080"
}
@param "operation": "add" or "remove"
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
rule = " -p {}".format(info['Protocol'])
if "S_Address" in info and info['S_Address'] != "":
rule += " -s {}".format(info['S_Address'])
rule += " --dport {0} -j DNAT --to-destination {1}:{2}".format(
info['S_Port'],
info['T_Address'],
info['T_Port'],
)
parm = {
"table": "nat",
"chain_name": "PREROUTING",
"rule": rule
}
if operation not in ["add", "remove"]:
return "Please enter the correct type of operation. (add/remove)"
if operation == "add":
parm['type'] = "-I"
elif operation == "remove":
parm['type'] = "-D"
return self.rule_manage(parm)
except Exception as e:
return self._result(False, public.lang("Failed to set a port forwarding rule:{}", str(e)))
# 2024/3/19 下午 3:03 在指定链中管理规则
def rule_manage(self, parm):
"""
@name 在指定链中管理规则
@author wzz <2024/3/19 下午 3:03>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if not self.check_table_name(parm['table']):
return self._result(False, public.lang("Unsupported table names: {}", parm['table']))
rule = "{} -t {} {} {} {}".format(
self.cmd_str, parm['table'], parm['type'], parm['chain_name'], parm['rule']
)
stdout, stderr = public.ExecShell(rule)
if stderr and "setlocale: LC_ALL: cannot change locale (en_US.UTF-8)" not in stderr:
return self._result(False, public.lang("The rule setup failed:{}", stderr))
return self._result(True, public.lang("The rule is set successfully"))
except Exception as e:
return self._result(False, public.lang("The rule setup failed: {}", str(e)))
# 2024/4/29 下午5:55 获取所有端口转发列表
def list_port_forward(self):
"""
@name 获取所有端口转发列表
@author wzz <2024/4/29 下午5:55>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
return self.get_nat_prerouting_rules()
# 2024/3/19 下午 4:00 调用list_rules获取所有nat表中的PREROUTING链的规则(端口转发规则),并分析成字典返回
def get_nat_prerouting_rules(self):
"""
@name 调用list_rules获取所有nat表中的PREROUTING链的规则(端口转发规则),并分析成字典返回
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
port_forward_rules = self.list_rules({"table": "nat", "chain_name": "PREROUTING"})
rules = []
for rule in port_forward_rules:
if rule["target"] != "DNAT": continue
options = rule["options"].split(' ')
protocol = "TCP"
if rule["prot"] == "6" or rule["prot"] == "tcp":
protocol = "TCP"
elif rule["prot"] == "17" or rule["prot"] == "udp":
protocol = "UDP"
elif rule["prot"] == "0" or rule["prot"] == "all":
protocol = "TCP/UDP"
rules.append({
"type": "port_forward",
"number": rule["number"],
"S_Address": rule["source"],
"S_Port": options[1].split("dpt:")[1],
"T_Address": options[2].split("to:")[1].split(":")[0],
"T_Port": options[2].split("to:")[1].split(":")[1],
"Protocol": protocol.lower()
})
return rules
except Exception as _:
return []
# 2024/4/29 下午2:43 格式化输出json
def format_json(self, data):
"""
@name 格式化输出json
@param "data": json数据
@return json字符串
"""
import json
from pygments import highlight, lexers, formatters
formatted_json = json.dumps(data, indent=3)
colorful_json = highlight(formatted_json.encode('utf-8'), lexers.JsonLexer(),
formatters.TerminalFormatter())
return colorful_json
if __name__ == '__main__':
args = sys.argv
firewall = Iptables()
if len(args) < 2:
print("Welcome to the iptables command-line interface!")
print()
print("Available options:")
print("list_rules: list_rules <table> <chain_name>")
print("flush_chain: flush_chain <table>")
print("get_chain_names: get_chain_names <table>")
print("port_forward: port_forward <S_Address> <S_Port> <T_Address> <T_Port> <Protocol> <Operation>")
print("get_nat_prerouting_rules: get_nat_prerouting_rules")
print()
sys.exit(1)
if args[1] == "list_rules":
# firewall.list_rules({"table": args[2], "chain_name": args[3]})
print(firewall.list_rules({"table": args[2], "chain_name": args[3]}))
elif args[1] == "flush_chain":
print(firewall.flush_chain(args[2]))
elif args[1] == "get_chain_names":
table = args[2] if len(args) > 2 else "filter"
print(firewall.get_chain_names({"table": table}))
elif args[1] == "port_forward":
if len(args) < 8:
print("传参使用方法: port_forward <S_Address> <S_Port> <T_Address> <T_Port> <Protocol> <Operation>")
sys.exit(1)
info = {
"S_Address": args[2],
"S_Port": args[3],
"T_Address": args[4],
"T_Port": args[5],
"Protocol": args[6]
}
print(firewall.port_forward(info, args[7]))
elif args[1] == "get_nat_prerouting_rules":
import json
from pygments import highlight, lexers, formatters
formatted_json = json.dumps(firewall.get_nat_prerouting_rules(), indent=3)
colorful_json = highlight(formatted_json.encode('utf-8'), lexers.JsonLexer(),
formatters.TerminalFormatter())
print(colorful_json)
# print(firewall.get_nat_prerouting_rules())
elif args[1] == "list_port":
print(firewall.format_json(firewall.list_port()))
elif args[1] == "list_input_port":
print(firewall.format_json(firewall.list_input_port()))
elif args[1] == "list_output_port":
print(firewall.format_json(firewall.list_output_port()))
elif args[1] == "list_address":
print(firewall.format_json(firewall.list_address()))
elif args[1] == "list_input_address":
print(firewall.format_json(firewall.list_input_address()))
elif args[1] == "list_output_address":
print(firewall.format_json(firewall.list_output_address()))
elif args[1] == "input_port":
info = {
"Protocol": args[2],
"Port": args[3],
"Strategy": args[4]
}
print(firewall.input_port(info, args[5]))
elif args[1] == "output_port":
info = {
"Protocol": args[2],
"Port": args[3],
"Strategy": args[4]
}
print(firewall.output_port(info, args[5]))
elif args[1] == "rich_rules":
info = {
"Protocol": args[2],
"Port": args[3],
"Address": args[4],
"Strategy": args[5]
}
print(firewall.rich_rules(info, args[6]))
elif args[1] == "output_rich_rules":
info = {
"Protocol": args[2],
"Port": args[3],
"Address": args[4],
"Strategy": args[5]
}
print(firewall.output_rich_rules(info, args[6]))
else:
print("Unsupported parameters: " + args[1])
sys.exit(1)

View File

@@ -0,0 +1,760 @@
#!/www/server/panel/pyenv/bin/python3.7
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: wzz <wzz@yakpanel.com>
# -------------------------------------------------------------------
# ------------------------------
# 系统防火墙模型 - ufw封装库
# ------------------------------
import subprocess
import sys
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
from firewallModelV2.app.appBase import Base
class Ufw(Base):
def __init__(self):
super().__init__()
self.cmd_str = self._set_cmd_str()
def _set_cmd_str(self):
return "ufw"
# 2024/3/19 下午 5:00 获取系统防火墙的运行状态
def status(self):
"""
@name 获取系统防火墙的运行状态
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run([self.cmd_str, "status"], capture_output=True, text=True, check=True)
if "Status: active" in result.stdout:
return "running"
elif "状态: 激活" in result.stdout:
return "running"
else:
return "not running"
except subprocess.CalledProcessError:
return "not running"
except Exception as _:
return "not running"
# 2024/3/19 下午 5:00 获取系统防火墙的版本号
def version(self):
"""
@name 获取系统防火墙的版本号
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run([self.cmd_str, "version"], capture_output=True, text=True, check=True)
info = result.stdout.replace("\n", "")
return info.replace("ufw ", "")
except Exception as _:
return "Unknown version"
# 2024/3/19 下午 5:00 启动防火墙
def start(self):
"""
@name 启动防火墙
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
stdout, stderr = public.ExecShell("echo y | {} enable".format(self.cmd_str))
if stderr and "setlocale: LC_ALL: cannot change locale (en_US.UTF-8)" not in stderr:
return self._result(False, public.lang("Failed to start firewall:{}", stderr))
return self._result(True, public.lang("The firewall was started successfully"))
except Exception as e:
return self._result(False, public.lang("Failed to start firewall:{}", str(e)))
# 2024/3/19 下午 5:00 停止防火墙
def stop(self):
"""
@name 停止防火墙
@author wzz <2024/3/19 下午 5:00>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
stdout, stderr = public.ExecShell("{} disable".format(self.cmd_str))
if stderr and "setlocale: LC_ALL: cannot change locale (en_US.UTF-8)" not in stderr:
return self._result(False, public.lang("Failed to stop the firewall:{}", stderr))
return self._result(True, public.lang("The firewall was stopped"))
except Exception as e:
return self._result(False, public.lang("Failed to stop the firewall:{}", str(e)))
# 2024/3/19 下午 4:59 重启防火墙
def restart(self):
"""
@name 重启防火墙
@author wzz <2024/3/19 下午 4:59>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
self.stop()
self.start()
except Exception as e:
return self._result(False, public.lang("Failed to restart the firewall:{}", str(e)))
# 2024/3/19 下午 4:59 重载防火墙
def reload(self):
"""
@name 重载防火墙
@author wzz <2024/3/19 下午 4:59>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
subprocess.run([self.cmd_str, "reload"], check=True, stdout=subprocess.PIPE)
except Exception as e:
return self._result(False, public.lang("Overloaded firewall failed:{}", str(e)))
# 2024/3/19 上午 10:39 列出防火墙中所有端口规则
def list_port(self):
"""
@name 列出防火墙中所有端口规则
@author wzz <2024/3/19 上午 10:39>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run(
[self.cmd_str, "status", "verbose"], capture_output=True, text=True, check=True
)
port_infos = result.stdout.split("\n")
datas = []
is_start = False
for line in port_infos:
if "fail2ban" in line.lower():
continue
if line.startswith("-"):
is_start = True
continue
if not is_start:
continue
item_fire = self._load_info(line, "port")
if item_fire.get("Port") and item_fire["Port"] != "Anywhere" and "." not in item_fire["Port"]:
item_fire["Port"] = item_fire["Port"].replace(":", "-")
item_fire["Address"] = "all" if item_fire["Address"] == "Anywhere" else item_fire["Address"]
datas.append(item_fire)
return datas
except Exception as _:
return []
# 2024/3/19 上午 10:39 列出防火墙中所有input端口规则
def list_input_port(self):
"""
@name 列出防火墙中所有input端口规则
@author wzz <2024/3/19 上午 10:39>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run(
[self.cmd_str, "status", "verbose"], capture_output=True, text=True, check=True
)
port_infos = result.stdout.split("\n")
datas = []
is_start = False
for line in port_infos:
if "fail2ban" in line.lower():
continue
if line.startswith("-"):
is_start = True
continue
if not is_start:
continue
item_fire = self._load_info(line, "port")
if item_fire.get("Port") and item_fire["Port"] != "Anywhere" and "." not in item_fire["Port"]:
item_fire["Port"] = item_fire["Port"].replace(":", "-")
if item_fire["Chain"] == "INPUT":
datas.append(item_fire)
return datas
except Exception as _:
return []
# 2024/3/19 上午 10:39 列出防火墙中所有output端口规则
def list_output_port(self):
"""
@name 列出防火墙中所有output端口规则
@author wzz <2024/3/19 上午 10:39>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run(
[self.cmd_str, "status", "verbose"], capture_output=True, text=True, check=True
)
port_infos = result.stdout.split("\n")
datas = []
is_start = False
for line in port_infos:
if "fail2ban" in line.lower():
continue
if line.startswith("-"):
is_start = True
continue
if not is_start:
continue
item_fire = self._load_info(line, "port")
if item_fire.get("Port") and item_fire["Port"] != "Anywhere" and "." not in item_fire["Port"]:
item_fire["Port"] = item_fire["Port"].replace(":", "-")
if item_fire["Chain"] == "OUTPUT":
datas.append(item_fire)
return datas
except Exception as _:
return []
# 2024/3/19 上午 10:39 列出防火墙中所有的ip规则
def list_address(self):
"""
@name 列出防火墙中所有的ip规则
@author wzz <2024/3/19 上午 10:39>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run(
[self.cmd_str, "status", "verbose"], capture_output=True, text=True, check=True
)
port_infos = result.stdout.split("\n")
datas = []
is_start = False
for line in port_infos:
if "fail2ban" in line.lower():
continue
if line.startswith("-"):
is_start = True
continue
if not is_start:
continue
item_fire = self._load_info(line, "address")
if "Port" in item_fire: continue
if item_fire.get("Address"):
datas.append(item_fire)
return datas
except Exception as _:
return []
# 2024/3/19 上午 10:39 列出防火墙中所有input的ip规则
def list_input_address(self):
"""
@name 列出防火墙中所有input的ip规则
@author wzz <2024/3/19 上午 10:39>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run(
[self.cmd_str, "status", "verbose"], capture_output=True, text=True, check=True
)
port_infos = result.stdout.split("\n")
datas = []
is_start = False
for line in port_infos:
if "fail2ban" in line.lower():
continue
if line.startswith("-"):
is_start = True
continue
if not is_start:
continue
if " IN" not in line:
continue
item_fire = self._load_info(line, "address")
if "Port" in item_fire: continue
if item_fire.get("Address"):
datas.append(item_fire)
return datas
except Exception as _:
return []
# 2024/3/19 上午 10:39 列出防火墙中所有output的ip规则
def list_output_address(self):
"""
@name 列出防火墙中所有output的ip规则
@author wzz <2024/3/19 上午 10:39>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
result = subprocess.run(
[self.cmd_str, "status", "verbose"], capture_output=True, text=True, check=True
)
port_infos = result.stdout.split("\n")
datas = []
is_start = False
for line in port_infos:
if "fail2ban" in line.lower():
continue
if line.startswith("-"):
is_start = True
continue
if not is_start:
continue
if " OUT" not in line:
continue
item_fire = self._load_info(line, "address")
if "Port" in item_fire: continue
if item_fire.get("Address"):
datas.append(item_fire)
return datas
except Exception as _:
return []
# 2024/3/19 下午 4:59 添加端口规则
def input_port(self, info, operation):
"""
@name 添加端口规则
@author wzz <2024/3/19 下午 4:59>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if info["Strategy"] == "accept":
info["Strategy"] = "allow"
elif info["Strategy"] == "drop":
info["Strategy"] = "deny"
if "Port" in info and info["Port"].find('-') != -1:
info["Port"] = info["Port"].replace('-', ':')
if operation == "add":
if info['Protocol'] == "tcp/udp":
cmd = "{cmd_str} allow {port}/tcp;{cmd_str} allow {port}/udp".format(cmd_str=self.cmd_str, port=info['Port'])
stdout, stderr = public.ExecShell(cmd)
else:
stdout, stderr = public.ExecShell(self.cmd_str + " allow " + info['Port'] + "/" + info['Protocol'])
else:
if info['Protocol'] == "tcp/udp":
cmd = "{cmd_str} delete allow {port}/tcp;{cmd_str} delete allow {port}/udp".format(cmd_str=self.cmd_str, port=info['Port'])
stdout, stderr = public.ExecShell(cmd)
else:
stdout, stderr = public.ExecShell(self.cmd_str + " delete allow " + info['Port'] + "/" + info['Protocol'])
if stderr:
if "setlocale" in stderr:
return self._result(True, public.lang("The port rule was successfully configured"))
return self._result(False, public.lang("Failed to set a port rule:{}", stderr))
return self._result(True, public.lang("The port rule was successfully configured"))
except Exception as e:
if "setlocale" in str(e):
return self._result(True, public.lang("The port rule was successfully configured"))
return self._result(False, public.lang("Failed to set a port rule:{}", str(e)))
# 2024/3/24 下午 11:28 设置output端口策略
def output_port(self, info, operation):
"""
@name 设置output端口策略
@param info: 端口号
@param operation: 操作
@return None
"""
try:
if info["Strategy"] == "accept":
info["Strategy"] = "allow"
elif info["Strategy"] == "drop":
info["Strategy"] = "deny"
if "Port" in info and info["Port"].find('-') != -1:
info["Port"] = info["Port"].replace('-', ':')
if operation == "add":
if info['Protocol'].find('/') != -1:
cmd = "{cmd_str} {strategy} out {port}/tcp;{cmd_str} {strategy} out {port}/udp".format(cmd_str=self.cmd_str,strategy = info['Strategy'],port=info['Port'])
else:
cmd = "{} {} out {}/{}".format(self.cmd_str, info['Strategy'], info['Port'], info['Protocol'])
else:
if info['Protocol'].find('/') != -1:
cmd = "{cmd_str} delete {strategy} out {port}/tcp;{cmd_str} delete {strategy} out {port}/udp".format(cmd_str=self.cmd_str, strategy=info['Strategy'], port=info['Port'])
else:
cmd = "{} delete {} out {}/{}".format(self.cmd_str, info['Strategy'], info['Port'], info['Protocol'])
stdout, stderr = public.ExecShell(cmd)
if stderr:
if "setlocale" in stderr:
return self._result(True, public.lang("The port rule was successfully configured"))
return self._result(False, public.lang("Failed to set the output port rule:{}", stderr))
return self._result(True, public.lang("The output port rule is set successfully"))
except Exception as e:
if "setlocale" in str(e):
return self._result(True, public.lang("The output port rule is set successfully"))
return self._result(False, public.lang("Failed to set the output port rule:{}", str(e)))
# 2024/3/19 下午 4:58 复杂一些的规则管理
def rich_rules(self, info, operation):
"""
@name 复杂一些的规则管理
@author wzz <2024/3/19 下午 4:58>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if info["Strategy"] == "accept":
info["Strategy"] = "allow"
elif info["Strategy"] == "drop":
info["Strategy"] = "deny"
else:
return self._result(False, public.lang("Unknown policy parameters:{}", info["Strategy"]))
if "Port" in info and info["Port"].find('-') != -1:
info["Port"] = info["Port"].replace('-', ':')
rule_str = "{} insert 1 {} ".format(self.cmd_str, info["Strategy"])
if "Address" in info and public.is_ipv6(info['Address']):
rule_str = "{} {} ".format(self.cmd_str, info["Strategy"])
if operation == "remove":
rule_str = "{} delete {} ".format(self.cmd_str, info["Strategy"])
if "Address" in info and info['Address'] != "all":
rule_str += "from {} ".format(info['Address'])
if len(info.get("Protocol", "")) != 0 and "/" not in info['Protocol']:
rule_str += "proto {} ".format(info['Protocol'])
if len(info.get("Protocol", "")) != 0 and "/" in info['Protocol']:
if "Address" in info and info['Address'] != "all":
if not "from {} ".format(info['Address']) in rule_str:
rule_str += "from any "
if len(info.get("Port", "")) != 0:
rule_str += "to any port {} ".format(info['Port'])
if len(info.get("Protocol", "")) != 0 and "/" in info['Protocol']:
for i in ["tcp", "udp"]:
cmd_str = rule_str + "proto {}".format(i)
stdout, stderr = public.ExecShell(cmd_str)
if stderr:
if "Rule added" in stdout or "Rule deleted" in stdout or "Rule updated" in stdout or "Rule inserted" in stdout or "Skipping adding existing rule" in stdout:
return self._result(True, public.lang("The rule is set successfully"))
if "setlocale" in stderr:
return self._result(True, public.lang("The rule is set successfully"))
return self._result(False, public.lang(f"The rule setup failed:{stderr}"))
return self._result(True, public.lang("The rule is set successfully"))
stdout, stderr = public.ExecShell(rule_str)
if stderr:
if "Rule added" in stdout or "Rule deleted" in stdout or "Rule updated" in stdout or "Rule inserted" in stdout or "Skipping adding existing rule" in stdout:
return self._result(True, public.lang("The rule is set successfully"))
if "setlocale" in stderr:
return self._result(True, public.lang("The rule is set successfully"))
return self._result(False, public.lang("The rule setup failed:{}", stderr))
return self._result(True, public.lang("The rule is set successfully"))
except Exception as e:
if "setlocale" in str(e):
return self._result(True, public.lang("The rule is set successfully"))
return self._result(False, public.lang("The rule setup failed:{}", e))
# 2024/3/24 下午 11:29 设置output rich_rules
def output_rich_rules(self, info, operation):
"""
@name 设置output rich_rules
@param info: 规则
@param operation: 操作
@return None
"""
try:
if info["Strategy"] == "accept":
info["Strategy"] = "allow"
elif info["Strategy"] == "drop":
info["Strategy"] = "deny"
else:
return self._result(False, public.lang("Unknown strategy: {}", info["Strategy"]))
rule_str = "{} insert 1 {} ".format(self.cmd_str, info["Strategy"])
if "Address" in info and public.is_ipv6(info['Address']):
rule_str = "{} {} ".format(self.cmd_str, info["Strategy"])
if operation == "remove":
rule_str = "{} delete {} ".format(self.cmd_str, info["Strategy"])
if len(info.get("Address", "")) != 0:
rule_str += "out from {} ".format(info['Address'])
if len(info.get("Protocol", "")) != 0:
rule_str += "proto {} ".format(info['Protocol'])
if len(info.get("Port", "")) != 0:
rule_str += "to any port {} ".format(info['Port'])
stdout, stderr = public.ExecShell(rule_str)
if stderr:
if "Rule added" in stdout or "Rule deleted" in stdout or "Rule updated" in stdout or "Rule inserted" in stdout or "Skipping adding existing rule" in stdout:
return self._result(True, public.lang("The output rule is set successfully"))
if "setlocale" in stderr:
return self._result(True, public.lang("The rule is set successfully"))
return self._result(False, public.lang("outpuThe rule setup failed:{}", stderr))
return self._result(True, public.lang("The output rule is set successfully"))
except Exception as e:
if "setlocale" in str(e):
return self._result(True, public.lang("The output rule is set successfully"))
return self._result(False, public.lang("outpuThe rule setup failed:{}", e))
# 2024/3/19 下午 5:01 解析防火墙规则信息,返回字典格式数据,用于添加或删除防火墙规则
def _load_info(self, line, fire_type):
"""
@name 解析防火墙规则信息,返回字典格式数据,用于添加或删除防火墙规则
@author wzz <2024/3/19 上午 10:38>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
fields = line.split()
item_info = {}
if "LIMIT" in line or "ALLOW FWD" in line:
return item_info
if len(fields) < 4:
return item_info
if fields[0] == "Anywhere" and fire_type != "port":
item_info["Strategy"] = "drop"
if fields[1] != "(v6)":
if fields[1] == "ALLOW":
item_info["Strategy"] = "accept"
if fields[2] == "IN":
item_info["Chain"] = "INPUT"
elif fields[2] == "OUT":
item_info["Chain"] = "OUTPUT"
item_info["Address"] = fields[3] if fields[3] != "Anywhere" else "all"
item_info["Family"] = "ipv4"
else:
if fields[2] == "ALLOW":
item_info["Strategy"] = "accept"
if fields[3] == "IN":
item_info["Chain"] = "INPUT"
elif fields[3] == "OUT":
item_info["Chain"] = "OUTPUT"
item_info["Address"] = fields[4] if fields[4] != "Anywhere" else "all"
item_info["Family"] = "ipv6"
return item_info
if "/" in fields[0]:
item_info["Port"] = fields[0].split("/")[0]
item_info["Protocol"] = fields[0].split("/")[1]
else:
item_info["Port"] = fields[0]
item_info["Protocol"] = "tcp/udp"
if "v6" in fields[1]:
item_info["Family"] = "ipv6"
if fields[2] == "ALLOW":
item_info["Strategy"] = "accept"
else:
item_info["Strategy"] = "drop"
if fields[3] == "IN":
item_info["Chain"] = "INPUT"
elif fields[3] == "OUT":
item_info["Chain"] = "OUTPUT"
item_info["Address"] = fields[4] if fields[4] != "Anywhere" else "all"
else:
item_info["Family"] = "ipv4" if ":" not in fields[3] else "ipv6"
if fields[1] == "ALLOW":
item_info["Strategy"] = "accept"
else:
item_info["Strategy"] = "drop"
if fields[2] == "IN":
item_info["Chain"] = "INPUT"
elif fields[2] == "OUT":
item_info["Chain"] = "OUTPUT"
item_info["Address"] = fields[3] if fields[3] != "Anywhere" else "all"
return item_info
# 2024/3/25 下午 2:29 设置端口转发
def port_forward(self, info, operation):
"""
@name 设置端口转发
@param port: 端口号
@param ip: ip地址
@param operation: 操作
@return None
"""
from firewallModelV2.app.iptables import Iptables
self.firewall = Iptables()
return self.firewall.port_forward(info, operation)
# 2024/3/25 下午 2:34 获取所有端口转发列表
def list_port_forward(self):
"""
@name 获取所有端口转发列表
@return None
"""
from firewallModelV2.app.iptables import Iptables
self.firewall = Iptables()
return self.firewall.get_nat_prerouting_rules()
if __name__ == '__main__':
args = sys.argv
firewall = Ufw()
ufw_status = firewall.status()
if len(args) < 2:
print("Welcome to the UFW (Uncomplicated Firewall) command-line interface!")
print("Firewall status is :", ufw_status)
print("Firewall version: ", firewall.version())
if ufw_status == "not running":
print("ufw is not started, please start ufw and then execute the command!")
print("Start the command: start")
print()
sys.exit(1)
print()
print("Available options:")
print("1. Check Firewall Status: status")
print("2. Check Firewall Version: version")
print("3. Start Firewall: start")
print("4. Stop Firewall: stop")
print("5. Restart Firewall: restart")
print("6. Reload Firewall: reload")
print("7. List All Ports: list_port")
print("8. List All IP Addresses: list_address")
print("9. Add Port: add_port <port> <protocol>")
print("10. Remove Port: remove_port <port> <protocol>")
print("11. Add Port Rule: add_port_rule <address> <port> <protocol> <strategy> <operation>")
print("12. Remove Port Rule: remove_port_rule <address> <port> <protocol> <strategy> <operation>")
print("13. Add IP Rule: add_ip_rule <address> <strategy> <operation>")
print("14. Remove IP Rule: remove_ip_rule <address> <strategy> <operation>")
print()
sys.exit(1)
if args[1] == "status":
print(firewall.status())
elif args[1] == "version":
print(firewall.version())
elif args[1] == "start":
error = firewall.start()
if error:
print(f"Error: {error}")
else:
print("Firewall started successfully.")
elif args[1] == "stop":
error = firewall.stop()
if error:
print(f"Error: {error}")
else:
print("Firewall stopped successfully.")
elif args[1] == "restart":
error = firewall.restart()
if error:
print(f"Error: {error}")
else:
print("Firewall restarted successfully.")
elif args[1] == "reload":
error = firewall.reload()
if error:
print(f"Error: {error}")
else:
print("Firewall reloaded successfully.")
elif args[1] == "list_input_port":
ports = firewall.list_input_port()
for p in ports:
print(p)
elif args[1] == "list_output_port":
ports = firewall.list_output_port()
for p in ports:
print(p)
elif args[1] == "list_input_address":
addresses = firewall.list_input_address()
for a in addresses:
print(a)
elif args[1] == "list_output_address":
addresses = firewall.list_output_address()
for a in addresses:
print(a)
elif args[1] == "add_port":
port = args[2]
protocol = args[3]
error = firewall.input_port(f"{port}/{protocol}", "allow")
if error:
print(f"Error: {error}")
else:
print(f"Port {port}/{protocol} added successfully.")
elif args[1] == "remove_port":
port = args[2]
protocol = args[3]
error = firewall.input_port(f"{port}/{protocol}", "remove")
if error:
print(f"Error: {error}")
else:
print(f"Port {port}/{protocol} removed successfully.")
elif args[1] == "add_port_rule":
address = args[2]
port = args[3]
protocol = args[4]
strategy = args[5]
operation = args[6]
error = firewall.rich_rules(
{"Address": address, "Port": port, "Protocol": protocol, "Strategy": strategy}, operation)
if error:
print(f"Error: {error}")
else:
print("Rich rule added successfully.")
elif args[1] == "remove_port_rule":
address = args[2]
port = args[3]
protocol = args[4]
strategy = args[5]
operation = args[6]
error = firewall.rich_rules(
{"Address": address, "Port": port, "Protocol": protocol, "Strategy": strategy}, operation)
if error:
print(f"Error: {error}")
else:
print("Rich rule removed successfully.")
elif args[1] == "add_ip_rule":
address = args[2]
strategy = args[3]
operation = args[4]
error = firewall.rich_rules(
{"Address": address, "Strategy": strategy}, operation)
if error:
print(f"Error: {error}")
else:
print("Rich rule added successfully.")
elif args[1] == "remove_ip_rule":
address = args[2]
strategy = args[3]
operation = args[4]
error = firewall.rich_rules(
{"Address": address, "Strategy": strategy}, operation)
if error:
print(f"Error: {error}")
else:
print("Rich rule removed successfully.")
elif args[1] == "output_port":
port = args[2]
operation = args[3]
error = firewall.output_port(port, operation)
if error:
print(f"Error: {error}")
else:
print(f"Output port {port} {operation} successfully.")
elif args[1] == "output_rich_rules":
address = args[2]
port = args[3]
protocol = args[4]
strategy = args[5]
operation = args[6]
error = firewall.output_rich_rules(
{"Address": address, "Port": port, "Protocol": protocol, "Strategy": strategy}, operation)
if error:
print(f"Error: {error}")
else:
print("Output rich rule added successfully.")
else:
print("Invalid args")
sys.exit(1)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,251 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 yakpanel(https://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: wzz <wzz@yakpanel.com>
# -------------------------------------------------------------------
# ------------------------------
# 系统防火墙模型 - 基类
# ------------------------------
import os
import re
from typing import Dict, Union, Any
import public
class Base(object):
def __init__(self):
self.config_path = "{}/class_v2/firewallModelV2/config".format(public.get_panel_path())
self.m_time_file = "/www/server/panel/data/firewall/geoip_mtime.pl"
self._isUfw = False
self._isFirewalld = False
self._isIptables = False
if os.path.exists('/usr/sbin/firewalld') or os.path.exists('/etc/redhat-release'):
self._isFirewalld = True
from firewallModelV2.app.firewalld import Firewalld
self.firewall = Firewalld()
elif os.path.exists('/usr/bin/apt-get'):
self._isUfw = True
from firewallModelV2.app.ufw import Ufw
self.firewall = Ufw()
elif not self._isUfw and not self._isFirewalld:
self._isIptables = True
from firewallModelV2.app.iptables import Iptables
self.firewall = Iptables()
_months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', 'Jul': '07',
'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
# 2024/3/14 上午 11:27 获取防火墙运行状态
def get_firewall_status(self) -> bool:
"""
@name 获取防火墙运行状态
@author wzz <2024/3/14 上午 11:27>
@param
@return bool True/False
"""
if self._isUfw:
res = public.ExecShell("systemctl is-active ufw")[0]
if res == "active": return True
res = public.ExecShell("systemctl list-units | grep ufw")[0]
if res.find('active running') != -1: return True
res = public.ExecShell('/lib/ufw/ufw-init status')[0]
if res.find("Firewall is not running") != -1: return False
res = public.ExecShell('ufw status verbose')[0]
if res.find('inactive') != -1: return False
return True
if self._isFirewalld:
res = public.ExecShell("ps -ef|grep firewalld|grep -v grep")[0]
if res: return True
res = public.ExecShell("systemctl is-active firewalld")[0]
if res == "active": return True
res = public.ExecShell("systemctl list-units | grep firewalld")[0]
if res.find('active running') != -1: return True
return False
else:
res = public.ExecShell("/etc/init.d/iptables status")[0]
if res.find('not running') != -1: return False
res = public.ExecShell("systemctl is-active iptables")[0]
if res == "active": return True
return True
# 2024/3/14 上午 11:30 设置禁ping
def set_ping(self, get) -> dict:
"""
@name 设置禁ping
@author wzz <2024/3/14 上午 11:31>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
get.status = get.get("status", "1")
get.status = str(get.status) if str(get.status) in ['0', '1'] else '1'
filename = '/etc/sysctl.conf'
conf = public.readFile(filename)
if conf.find('net.ipv4.icmp_echo') != -1:
rep = r"net\.ipv4\.icmp_echo.*"
conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + get.status + "\n", conf)
else:
conf += "\nnet.ipv4.icmp_echo_ignore_all=" + get.status + "\n"
if public.writeFile(filename, conf):
public.ExecShell('sysctl -p')
return public.return_message(0, 0, public.lang("SUCCESS"))
else:
return public.return_message(-1, 0,
'<a style="color:red;">Error: Setting failed, sysctl.conf is not writable! </a><br>'
'1. If YakPanel [System Hardening] is installed, please close it first<br>'
'2. If Cloud Lock is installed, please turn off the [System Collagen] function<br>'
'3. If a security dog is installed, please turn off the [System Protection] function<br>'
'4. If you use other security software, please uninstall it first<br>'
)
# 2024/3/14 上午 11:37 获取网站日志目录的大小
def get_www_logs_size(self, get) -> Dict[str, Union[str, Any]]:
"""
@name 获取网站日志目录的大小
@author wzz <2024/3/14 上午 11:37>
@param
@return dict{"status":True/False,"msg":"提示信息"}
"""
log_path = "/www/wwwlogs"
if not os.path.exists(log_path):
return public.return_message(0, 0, {"log_path": log_path, "size": "0B"})
path_size = public.to_size(public.get_path_size(log_path))
size = path_size if path_size else "0B"
return public.return_message(0, 0, {"log_path": log_path, "size": size})
# 2024/3/25 上午 10:50 获取防火墙类型firewall或ufw
def _get_firewall_type(self) -> str:
"""
@name 获取防火墙类型firewall或ufw
@return str firewall/ufw
"""
import os
if os.path.exists('/usr/sbin/ufw'):
return 'ufw'
if os.path.exists('/usr/sbin/firewalld'):
return 'firewall'
return 'iptables'
# 2024/3/26 下午 5:01 获取指定域名的A记录
def get_a_ip(self, domain: str) -> str:
"""
@name 获取指定域名的A记录
@param domain: 域名
@return str
"""
try:
import socket
return socket.gethostbyname(domain)
except Exception as e:
return ""
# 2024/3/26 下午 5:40 检查是否已添加计划任务,如果没有则添加
def check_resolve_crontab(self):
"""
@name 检查是否已添加计划任务
@author wzz <2024/3/26 下午 5:41>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
python_path = "{}/pyenv/bin/python".format(public.get_panel_path())
if not public.M('crontab').where(
'name=?',
('[Do not delete] system firewall domain name resolution detection tasks',)
).count():
cmd = '{} {}'.format(python_path, '/www/server/panel/script/firewall_domain.py')
args = {
"name": "[Do not delete] system firewall domain name resolution detection tasks",
"type": 'minute-n',
"where1": '5',
"hour": '',
"minute": '',
"sName": "",
"sType": 'toShell',
"notice": '',
"notice_channel": '',
"save": '',
"save_local": '1',
"backupTo": '',
"sBody": cmd,
"urladdress": ''
}
import crontab
res = crontab.crontab().AddCrontab(args)
if res and "id" in res.keys():
return True
return False
return True
# 2024/3/26 下午 11:37 当没有域名解析时,删除域名解析的计划任务
def remove_resolve_crontab(self):
"""
@name 当没有域名解析时,删除域名解析的计划任务
@author wzz <2024/3/26 下午 11:37>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
if not public.M('firewall_domain').count():
pdata = public.M('crontab').where(
'name=?',
'[Do not delete] system firewall domain name resolution detection tasks'
).select()
if pdata:
import crontab
for i in pdata:
args = {"id": i['id']}
crontab.crontab().DelCrontab(args)
# 2024/3/26 下午 6:22 端口扫描
def CheckPort(self, port, protocol):
"""
@name 端口扫描
@author wzz <2024/3/26 下午 6:22>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
import socket
localIP = '127.0.0.1'
temp = {}
temp['port'] = port
temp['local'] = True
try:
if 'tcp' in protocol.lower():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.01)
s.connect((localIP, port))
s.close()
if 'udp' in protocol.lower():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0.01)
s.sendto(b'', (localIP, port))
s.close()
except:
temp['local'] = False
result = 0
if temp['local']: result += 2
return result
# 2024/12/18 11:06 构造返回的分页数据
def return_page(self, data, get):
"""
@name 构造返回的分页数据
"""
count = len(data)
if count == 0:
return {'data': [], 'count': 0, 'p': 1, 'row': int(get.row)}
result = public.get_page(count, int(get.p), int(get.row))
result['data'] = data[(int(get.p) - 1) * int(get.row):(int(get.p)) * int(get.row)]
return result

View File

@@ -0,0 +1,269 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
import subprocess
import sys
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
from firewallModelV2.app.appBase import Base
class IptablesServices(Base):
def __init__(self):
super().__init__()
def set_chain_rich_ip(self, info, operation, chain):
"""
@name 添加/删除指定链的复杂ip规则
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
if "Address" in info and info["Address"] == "":
return self._result(False, public.lang("Failed to set up rule: IP address cannot be empty"))
if "Address" in info and public.is_ipv6(info['Address']):
return self._result(False, public.lang("Failed to set up rule Unsupported IPV6 addresses"))
if "Timeout" not in info or info["Timeout"] == "":
info["Timeout"] = 0
if chain == "INPUT":
chain = 'in'
else:
chain = 'out'
if operation == "add":
exec_cmd = "ipset add {}_bt_user_{}_ipset {} timeout {}".format(chain, info["Strategy"],
info["Address"], info["Timeout"])
else:
exec_cmd = "ipset del {}_bt_user_{}_ipset {}".format(chain, info["Strategy"], info["Address"])
stdout, stderr = public.ExecShell(exec_cmd)
if stderr:
return self._result(False, public.lang(f"Failed to set up rule:{stderr}"))
return self._result(True, public.lang("Failed to set up rule"))
except Exception as e:
return self._result(False, public.lang(f"Failed to set up rule:{str(e)}"))
def rich_rules(self, info, operation, chain):
"""
@name
@author csj <2025/3/12 上午9:28>
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
if "Priority" in info and not "Port" in info:
return self.set_chain_rich_ip(info, operation, chain)
else:
pass
# 端口类型暂时不用新版
# return self.set_chain_rich_port(info, operation, "INPUT")
# 2024/4/29 下午4:01 获取指定链的数据
def get_chain_data(self, chain):
"""
@name 获取指定链的数据
@author wzz <2024/4/29 下午4:01>
@param "data":{"参数名":""} <数据类型> 参数描述
@return [
{
"Family": "ipv4",
"Address": "192.168.1.190",
"Strategy": "accept",
"Chain": "INPUT",
"Timeout": timeout
}
]
"""
if chain == "INPUT":
ipset_chain = "in"
elif chain == "OUTPUT":
ipset_chain = "out"
else:
return []
rules = []
try:
for strategy in ["accept", "drop"]:
ipset_cmd = "ipset list {}_bt_user_{}_ipset | awk '/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ && /timeout/ {{print $1, $3}}'".format(
ipset_chain, strategy
)
stdout, stderr = public.ExecShell(ipset_cmd)
iplist = stdout.split("\n")
for line in iplist:
if line == "": continue
address = line.strip().split(" ")[0]
timeout = line.strip().split(" ")[1]
rule = {
"Family": "ipv4",
"Address": address,
"Strategy": strategy,
"Chain": chain,
"Timeout": timeout
}
rules.append(rule)
return rules
except Exception as _:
return ""
def list_address(self, chains: list = None):
"""
获取指定链的ipset列表
:param chains: 链列表 ["INPUT","OUTPUT"]
"""
if chains is None:
chains = ["INPUT", "OUTPUT"]
result = []
for chain in chains:
result = result + self.get_chain_data(chain)
return result
def parse_rules(self, stdout):
"""
@name 解析规则列表输出,返回规则列表字典
@author wzz <2024/3/19 下午 3:53>
字段含义:
"number": 规则编号,对应规则在链中的顺序。
"chain": 规则所属的链的名称。
"pkts": 规则匹配的数据包数量。
"bytes": 规则匹配的数据包字节数。
"target": 规则的目标动作,表示数据包匹配到该规则后应该执行的操作。
"prot": 规则适用的协议类型。
"opt": 规则的选项,包括规则中使用的匹配条件或特定选项。
"in": 规则匹配的数据包的输入接口。
"out": 规则匹配的数据包的输出接口。
"source": 规则匹配的数据包的源地址。
"destination": 规则匹配的数据包的目标地址。
"options": 规则的其他选项或说明,通常是规则中的注释或附加信息。
protocol(port协议头中数字对应的协议类型):
0: 表示所有协议
1: ICMPInternet 控制消息协议)
6: TCP传输控制协议
17: UDP用户数据报协议
@param "data":{"参数名":""} <数据类型> 参数描述
@return dict{"status":True/False,"msg":"提示信息"}
"""
lines = stdout.strip().split('\n')
rules = []
current_chain = None
for line in lines:
if line.startswith("Chain"):
current_chain = line.split()[1]
elif (line.startswith("target") or line.strip() == "" or "source" in line or
"Warning: iptables-legacy tables present" in line):
# 过滤表头,空行,警告
continue
else:
rule_info = line.split()
rule = {
"number": rule_info[0],
"chain": current_chain,
"pkts": rule_info[1],
"bytes": rule_info[2],
"target": rule_info[3],
"prot": rule_info[4],
"opt": rule_info[5],
"in": rule_info[6],
"out": rule_info[7],
"source": rule_info[8],
"destination": rule_info[9],
"options": " ".join(rule_info[10:]).strip()
}
rules.append(rule)
return rules
def list_rules(self, parm):
"""
@name 列出指定表的指定链的规则
@author wzz <2024/3/19 下午 3:02>
@param
@return
"""
try:
stdout = subprocess.check_output(
['iptables', '-t', parm['table'], '-L', parm['chain_name'], '-nv', '--line-numbers'],
stderr=subprocess.STDOUT, universal_newlines=True
)
return self.parse_rules(stdout)
except Exception as _:
return []
def list_port_forward(self):
"""
@name 调用list_rules获取所有nat表中的PREROUTING链的规则(端口转发规则),并分析成字典返回
@return dict{"status":True/False,"msg":"提示信息"}
"""
try:
port_forward_rules = self.list_rules({"table": "nat", "chain_name": "FORWARD_BT"})
rules = []
for rule in port_forward_rules:
rule_item = {
"type": "port_forward",
"number": rule["number"],
"S_Address": rule["source"],
"S_Port": "",
"T_Address": "",
"T_Port": "",
"Protocol": "TCP"
}
options = rule["options"].split(' ')
if rule["target"] == "DNAT": # 外部转发 有IP
rule_item["S_Port"] = options[1].split("dpt:")[1]
rule_item["T_Address"] = options[2].split("to:")[1].split(":")[0]
rule_item["T_Port"] = options[2].split("to:")[1].split(":")[1]
elif rule["target"] == "REDIRECT": # 内部转发无IP
rule_item["S_Port"] = options[1].split("dpt:")[1]
rule_item["T_Address"] = "127.0.0.1"
rule_item["T_Port"] = options[-1]
else:
continue
if rule["prot"] == "6" or rule["prot"] == "tcp":
rule_item["Protocol"] = "TCP"
elif rule["prot"] == "17" or rule["prot"] == "udp":
rule_item["Protocol"] = "UDP"
rules.append(rule_item)
return rules
except Exception as _:
return []
def port_forward(self, info, operation):
"""
设置端口转发规则
:param info: 规则信息
:param operation: 操作类型 add/del
"""
to_addr = info["T_Address"]
if operation == "add":
operation = "A"
else:
operation = "D"
is_lo = False
if to_addr == "" or to_addr == "0.0.0.0" or to_addr == "127.0.0.1" or to_addr == "0.0.0.0/0":
is_lo = True
if is_lo:
exec_cmd = "iptables -t nat -{operation} FORWARD_BT -p {proto} --dport {sport} -j REDIRECT --to-port {tport}".format(
operation=operation, proto=info["Protocol"], sport=info["S_Port"], tport=info["T_Port"])
else:
exec_cmd = "iptables -t nat -{operation} FORWARD_BT -p {proto} --dport {sport} -j DNAT --to-destination {taddr}:{tport}".format(
operation=operation, proto=info["Protocol"], addr=info["S_Address"], sport=info["S_Port"],
taddr=to_addr, tport=info["T_Port"])
stdout, stderr = public.ExecShell(exec_cmd)
if stderr:
return self._result(False, public.lang(f"Failed to set up rule:{stderr}"))
public.ExecShell("systemctl reload BT-FirewallServices")
return self._result(True, public.lang("Setting up the rule was successful"))