InPanel 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inpanel/__init__.py +29 -0
- inpanel/acme.py +289 -0
- inpanel/aliyuncs.py +269 -0
- inpanel/app.py +514 -0
- inpanel/apt.py +223 -0
- inpanel/base.py +200 -0
- inpanel/config.py +139 -0
- inpanel/data/config.ini +29 -0
- inpanel/data/history +5 -0
- inpanel/data/runlogs.ini +12 -0
- inpanel/data/update_info.ini +4 -0
- inpanel/dnf.py +225 -0
- inpanel/lib/__init__.py +9 -0
- inpanel/lib/async_process.py +98 -0
- inpanel/lib/filelock.py +82 -0
- inpanel/lib/magic.py +233 -0
- inpanel/lib/pxssh.py +310 -0
- inpanel/lib/pyDes.py +867 -0
- inpanel/mod/__init__.py +0 -0
- inpanel/mod/apt.py +316 -0
- inpanel/mod/certificate.py +268 -0
- inpanel/mod/config.py +372 -0
- inpanel/mod/cron.py +315 -0
- inpanel/mod/disk.py +322 -0
- inpanel/mod/dnf.py +239 -0
- inpanel/mod/file.py +722 -0
- inpanel/mod/firewall.py +199 -0
- inpanel/mod/firewall_base.py +137 -0
- inpanel/mod/firewall_firewalld.py +170 -0
- inpanel/mod/firewall_iptables.py +176 -0
- inpanel/mod/firewall_macos.py +213 -0
- inpanel/mod/firewall_ufw.py +128 -0
- inpanel/mod/ftp.py +231 -0
- inpanel/mod/httpd.py +1205 -0
- inpanel/mod/lighttpd.py +26 -0
- inpanel/mod/login.py +98 -0
- inpanel/mod/mysql.py +577 -0
- inpanel/mod/named.py +26 -0
- inpanel/mod/nginx.py +2327 -0
- inpanel/mod/package.py +33 -0
- inpanel/mod/package_apt.py +62 -0
- inpanel/mod/package_base.py +77 -0
- inpanel/mod/package_dnf.py +62 -0
- inpanel/mod/package_map.py +366 -0
- inpanel/mod/package_yum.py +64 -0
- inpanel/mod/php.py +289 -0
- inpanel/mod/process.py +291 -0
- inpanel/mod/proftpd.py +26 -0
- inpanel/mod/pureftpd.py +26 -0
- inpanel/mod/query.py +108 -0
- inpanel/mod/server.py +1319 -0
- inpanel/mod/service.py +262 -0
- inpanel/mod/setting.py +228 -0
- inpanel/mod/shell.py +78 -0
- inpanel/mod/ssh.py +280 -0
- inpanel/mod/system.py +220 -0
- inpanel/mod/task.py +1337 -0
- inpanel/mod/ufw.py +14 -0
- inpanel/mod/user.py +306 -0
- inpanel/mod/vsftpd.py +67 -0
- inpanel/mod/yum.py +243 -0
- inpanel/plugins/__init__.py +10 -0
- inpanel/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
- inpanel/plugins/acme/static/index.html +10 -0
- inpanel/plugins/acme/static/index.js +13 -0
- inpanel/plugins/shadowsocks-libev/__pycache__/info.cpython-313.pyc +0 -0
- inpanel/plugins/shadowsocks-libev/info.py +73 -0
- inpanel/plugins/shadowsocks-libev/static/index.html +70 -0
- inpanel/plugins/shadowsocks-libev/static/index.js +18 -0
- inpanel/public/css/global.css +138 -0
- inpanel/public/favicon.ico +0 -0
- inpanel/public/images/alipay.jpg +0 -0
- inpanel/public/images/loading.gif +0 -0
- inpanel/public/images/weixin.jpg +0 -0
- inpanel/public/index.html +94 -0
- inpanel/public/js/controllers/controllers.js +566 -0
- inpanel/public/js/controllers/database.js +590 -0
- inpanel/public/js/controllers/ecs.js +624 -0
- inpanel/public/js/controllers/file.js +940 -0
- inpanel/public/js/controllers/plugins.js +105 -0
- inpanel/public/js/controllers/service.js +1460 -0
- inpanel/public/js/controllers/site.js +1241 -0
- inpanel/public/js/controllers/utils.js +2319 -0
- inpanel/public/js/core.js +221 -0
- inpanel/public/js/directives.js +784 -0
- inpanel/public/js/filters.js +395 -0
- inpanel/public/js/services.js +257 -0
- inpanel/public/lib/angular/1.0.2/angular-resource.min.js +10 -0
- inpanel/public/lib/angular/1.0.2/angular.min.js +158 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.css +587 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css +6 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap.css +6757 -0
- inpanel/public/lib/bootstrap/css/bootstrap.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap.min.css +6 -0
- inpanel/public/lib/bootstrap/css/bootstrap.min.css.map +1 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
- inpanel/public/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
- inpanel/public/lib/bootstrap/img/glyphicons-halflings.png +0 -0
- inpanel/public/lib/bootstrap/js/bootstrap.js +2377 -0
- inpanel/public/lib/bootstrap/js/bootstrap.min.js +7 -0
- inpanel/public/lib/bootstrap/js/npm.js +13 -0
- inpanel/public/lib/codemirror/codemirror-compressed.js +1 -0
- inpanel/public/lib/codemirror/codemirror.css +173 -0
- inpanel/public/lib/jquery/jquery-1.12.4.min.js +5 -0
- inpanel/public/lib/md5/md5.js +379 -0
- inpanel/public/lib/md5/md5.min.js +1 -0
- inpanel/public/partials/database/index.html +172 -0
- inpanel/public/partials/database/mysql/dbedit.html +462 -0
- inpanel/public/partials/database/mysql/dbnew.html +348 -0
- inpanel/public/partials/database/mysql/useredit.html +310 -0
- inpanel/public/partials/database/mysql/usernew.html +97 -0
- inpanel/public/partials/directives/message.html +20 -0
- inpanel/public/partials/directives/navbar.html +25 -0
- inpanel/public/partials/directives/selector.html +57 -0
- inpanel/public/partials/directives/srvbase.html +72 -0
- inpanel/public/partials/directives/srvext.html +34 -0
- inpanel/public/partials/directives/srvfile.html +23 -0
- inpanel/public/partials/directives/srvinstall.html +34 -0
- inpanel/public/partials/directives/srvlog.html +23 -0
- inpanel/public/partials/directives/srvminiop.html +28 -0
- inpanel/public/partials/directives/srvuninstall.html +38 -0
- inpanel/public/partials/directives/srvupdate.html +30 -0
- inpanel/public/partials/ecs/account.html +156 -0
- inpanel/public/partials/ecs/ecs.html +198 -0
- inpanel/public/partials/ecs/index.html +40 -0
- inpanel/public/partials/ecs/setings.html +459 -0
- inpanel/public/partials/file/file.html +501 -0
- inpanel/public/partials/file/trash.html +95 -0
- inpanel/public/partials/ftp.html +36 -0
- inpanel/public/partials/log.html +83 -0
- inpanel/public/partials/login.html +79 -0
- inpanel/public/partials/logout.html +22 -0
- inpanel/public/partials/main.html +534 -0
- inpanel/public/partials/plugins/index.html +55 -0
- inpanel/public/partials/plugins/plugins.html +13 -0
- inpanel/public/partials/secure.html +13 -0
- inpanel/public/partials/service/apache.html +147 -0
- inpanel/public/partials/service/cron.html +71 -0
- inpanel/public/partials/service/index.html +293 -0
- inpanel/public/partials/service/iptables.html +40 -0
- inpanel/public/partials/service/lighttpd.html +63 -0
- inpanel/public/partials/service/mariadb.html +113 -0
- inpanel/public/partials/service/memcache.html +36 -0
- inpanel/public/partials/service/minio.html +44 -0
- inpanel/public/partials/service/mongodb.html +44 -0
- inpanel/public/partials/service/mysql.html +113 -0
- inpanel/public/partials/service/named.html +61 -0
- inpanel/public/partials/service/nginx.html +296 -0
- inpanel/public/partials/service/ntp.html +40 -0
- inpanel/public/partials/service/php.html +250 -0
- inpanel/public/partials/service/proftpd.html +59 -0
- inpanel/public/partials/service/pureftpd.html +59 -0
- inpanel/public/partials/service/redis.html +40 -0
- inpanel/public/partials/service/samba.html +58 -0
- inpanel/public/partials/service/sendmail.html +40 -0
- inpanel/public/partials/service/ssh.html +186 -0
- inpanel/public/partials/service/tomcat.html +65 -0
- inpanel/public/partials/service/vsftpd.html +195 -0
- inpanel/public/partials/setting.html +299 -0
- inpanel/public/partials/site/apache/site.html +277 -0
- inpanel/public/partials/site/index.html +344 -0
- inpanel/public/partials/site/nginx/site.html +558 -0
- inpanel/public/partials/sorry.html +19 -0
- inpanel/public/partials/storage/autofm.html +103 -0
- inpanel/public/partials/storage/backup.html +42 -0
- inpanel/public/partials/storage/index.html +360 -0
- inpanel/public/partials/storage/migrate.html +93 -0
- inpanel/public/partials/storage/movedata.html +98 -0
- inpanel/public/partials/storage/remote.html +77 -0
- inpanel/public/partials/utils/cron.html +486 -0
- inpanel/public/partials/utils/firewall.html +266 -0
- inpanel/public/partials/utils/index.html +193 -0
- inpanel/public/partials/utils/network.html +118 -0
- inpanel/public/partials/utils/process.html +239 -0
- inpanel/public/partials/utils/repository.html +373 -0
- inpanel/public/partials/utils/shell.html +28 -0
- inpanel/public/partials/utils/ssl.html +319 -0
- inpanel/public/partials/utils/time.html +109 -0
- inpanel/public/partials/utils/user.html +359 -0
- inpanel/public/robots.txt +2 -0
- inpanel/remote.py +109 -0
- inpanel/templates/file/preview.html +34 -0
- inpanel/templates/index.html +93 -0
- inpanel/utils.py +282 -0
- inpanel/web.py +1811 -0
- inpanel/yum.py +756 -0
- inpanel-1.2.1.dist-info/METADATA +399 -0
- inpanel-1.2.1.dist-info/RECORD +198 -0
- inpanel-1.2.1.dist-info/WHEEL +5 -0
- inpanel-1.2.1.dist-info/entry_points.txt +2 -0
- inpanel-1.2.1.dist-info/licenses/LICENSE +30 -0
- inpanel-1.2.1.dist-info/top_level.txt +1 -0
inpanel/mod/firewall.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2020-2026 Jackson Dou
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# InPanel is distributed under the terms of The New BSD License.
|
|
7
|
+
# The full license can be found in 'LICENSE'.
|
|
8
|
+
'''Module for Firewall Manager Integration'''
|
|
9
|
+
|
|
10
|
+
from typing import Dict, Optional
|
|
11
|
+
|
|
12
|
+
from .firewall_base import FirewallManager
|
|
13
|
+
from .firewall_firewalld import FirewalldPM
|
|
14
|
+
from .firewall_iptables import IptablesPM
|
|
15
|
+
from .firewall_ufw import UfwPM
|
|
16
|
+
from .firewall_macos import MacosFirewallPM
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_firewall_manager() -> Optional[FirewallManager]:
|
|
20
|
+
"""工厂函数:根据系统返回对应的防火墙管理器实例"""
|
|
21
|
+
managers = [FirewalldPM(), UfwPM(), IptablesPM(), MacosFirewallPM()]
|
|
22
|
+
for mgr in managers:
|
|
23
|
+
if mgr.detect():
|
|
24
|
+
return mgr
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_firewall_type() -> str:
|
|
29
|
+
"""获取当前系统的防火墙类型"""
|
|
30
|
+
mgr = get_firewall_manager()
|
|
31
|
+
if isinstance(mgr, FirewalldPM):
|
|
32
|
+
return 'firewalld'
|
|
33
|
+
elif isinstance(mgr, UfwPM):
|
|
34
|
+
return 'ufw'
|
|
35
|
+
elif isinstance(mgr, IptablesPM):
|
|
36
|
+
return 'iptables'
|
|
37
|
+
elif isinstance(mgr, MacosFirewallPM):
|
|
38
|
+
return 'macos'
|
|
39
|
+
return 'unknown'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def web_handler(context: Dict) -> Dict:
|
|
43
|
+
"""Handle web requests for Firewall management"""
|
|
44
|
+
action = context.get('action', '')
|
|
45
|
+
mgr = get_firewall_manager()
|
|
46
|
+
|
|
47
|
+
if mgr is None:
|
|
48
|
+
return {'code': -1, 'msg': '未检测到支持的防火墙类型!', 'data': {'type': 'unknown'}}
|
|
49
|
+
|
|
50
|
+
firewall_type = get_firewall_type()
|
|
51
|
+
|
|
52
|
+
if action == 'detect':
|
|
53
|
+
return {'code': 0, 'msg': '', 'data': {'type': firewall_type}}
|
|
54
|
+
|
|
55
|
+
elif action == 'status':
|
|
56
|
+
success, output = mgr.status()
|
|
57
|
+
is_running = mgr.is_running()
|
|
58
|
+
return {'code': 0, 'msg': '', 'data': {
|
|
59
|
+
'type': firewall_type,
|
|
60
|
+
'status': output,
|
|
61
|
+
'running': is_running
|
|
62
|
+
}}
|
|
63
|
+
|
|
64
|
+
elif action == 'start':
|
|
65
|
+
success, output = mgr.start()
|
|
66
|
+
if success:
|
|
67
|
+
return {'code': 0, 'msg': '防火墙已启动!', 'data': {'output': output}}
|
|
68
|
+
else:
|
|
69
|
+
return {'code': -1, 'msg': f'启动失败:{output}', 'data': {'output': output}}
|
|
70
|
+
|
|
71
|
+
elif action == 'stop':
|
|
72
|
+
success, output = mgr.stop()
|
|
73
|
+
if success:
|
|
74
|
+
return {'code': 0, 'msg': '防火墙已停止!', 'data': {'output': output}}
|
|
75
|
+
else:
|
|
76
|
+
return {'code': -1, 'msg': f'停止失败:{output}', 'data': {'output': output}}
|
|
77
|
+
|
|
78
|
+
elif action == 'restart':
|
|
79
|
+
success, output = mgr.restart()
|
|
80
|
+
if success:
|
|
81
|
+
return {'code': 0, 'msg': '防火墙已重启!', 'data': {'output': output}}
|
|
82
|
+
else:
|
|
83
|
+
return {'code': -1, 'msg': f'重启失败:{output}', 'data': {'output': output}}
|
|
84
|
+
|
|
85
|
+
elif action == 'enable':
|
|
86
|
+
success, output = mgr.enable()
|
|
87
|
+
if success:
|
|
88
|
+
return {'code': 0, 'msg': '防火墙已设置开机自启!', 'data': {'output': output}}
|
|
89
|
+
else:
|
|
90
|
+
return {'code': -1, 'msg': f'设置失败:{output}', 'data': {'output': output}}
|
|
91
|
+
|
|
92
|
+
elif action == 'disable':
|
|
93
|
+
success, output = mgr.disable()
|
|
94
|
+
if success:
|
|
95
|
+
return {'code': 0, 'msg': '防火墙已关闭开机自启!', 'data': {'output': output}}
|
|
96
|
+
else:
|
|
97
|
+
return {'code': -1, 'msg': f'设置失败:{output}', 'data': {'output': output}}
|
|
98
|
+
|
|
99
|
+
elif action == 'list_rules':
|
|
100
|
+
rules = mgr.parse_rules()
|
|
101
|
+
return {'code': 0, 'msg': '', 'data': {
|
|
102
|
+
'type': firewall_type,
|
|
103
|
+
'rules': rules
|
|
104
|
+
}}
|
|
105
|
+
|
|
106
|
+
elif action == 'add_port_rule':
|
|
107
|
+
port = context.get('port', '')
|
|
108
|
+
protocol = context.get('protocol', 'tcp')
|
|
109
|
+
zone = context.get('zone', '')
|
|
110
|
+
|
|
111
|
+
if not port:
|
|
112
|
+
return {'code': -1, 'msg': '端口号不能为空!'}
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
port_num = int(port)
|
|
116
|
+
except ValueError:
|
|
117
|
+
return {'code': -1, 'msg': '端口号必须是数字!'}
|
|
118
|
+
|
|
119
|
+
success, output = mgr.add_rule(port_num, protocol, zone)
|
|
120
|
+
if success:
|
|
121
|
+
return {'code': 0, 'msg': f'已添加端口规则:{port}/{protocol}', 'data': {'output': output}}
|
|
122
|
+
else:
|
|
123
|
+
return {'code': -1, 'msg': f'添加失败:{output}', 'data': {'output': output}}
|
|
124
|
+
|
|
125
|
+
elif action == 'remove_port_rule':
|
|
126
|
+
port = context.get('port', '')
|
|
127
|
+
protocol = context.get('protocol', 'tcp')
|
|
128
|
+
zone = context.get('zone', '')
|
|
129
|
+
|
|
130
|
+
if not port:
|
|
131
|
+
return {'code': -1, 'msg': '端口号不能为空!'}
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
port_num = int(port)
|
|
135
|
+
except ValueError:
|
|
136
|
+
return {'code': -1, 'msg': '端口号必须是数字!'}
|
|
137
|
+
|
|
138
|
+
success, output = mgr.remove_rule(port_num, protocol, zone)
|
|
139
|
+
if success:
|
|
140
|
+
return {'code': 0, 'msg': f'已移除端口规则:{port}/{protocol}', 'data': {'output': output}}
|
|
141
|
+
else:
|
|
142
|
+
return {'code': -1, 'msg': f'移除失败:{output}', 'data': {'output': output}}
|
|
143
|
+
|
|
144
|
+
elif action == 'add_ip_rule':
|
|
145
|
+
ip = context.get('ip', '')
|
|
146
|
+
action_type = context.get('action_type', 'allow')
|
|
147
|
+
|
|
148
|
+
if not ip:
|
|
149
|
+
return {'code': -1, 'msg': 'IP地址不能为空!'}
|
|
150
|
+
|
|
151
|
+
success, output = mgr.add_ip_rule(ip, action_type)
|
|
152
|
+
if success:
|
|
153
|
+
return {'code': 0, 'msg': f'已添加IP规则:{action_type} {ip}', 'data': {'output': output}}
|
|
154
|
+
else:
|
|
155
|
+
return {'code': -1, 'msg': f'添加失败:{output}', 'data': {'output': output}}
|
|
156
|
+
|
|
157
|
+
elif action == 'remove_ip_rule':
|
|
158
|
+
ip = context.get('ip', '')
|
|
159
|
+
|
|
160
|
+
if not ip:
|
|
161
|
+
return {'code': -1, 'msg': 'IP地址不能为空!'}
|
|
162
|
+
|
|
163
|
+
success, output = mgr.remove_ip_rule(ip)
|
|
164
|
+
if success:
|
|
165
|
+
return {'code': 0, 'msg': f'已移除IP规则:{ip}', 'data': {'output': output}}
|
|
166
|
+
else:
|
|
167
|
+
return {'code': -1, 'msg': f'移除失败:{output}', 'data': {'output': output}}
|
|
168
|
+
|
|
169
|
+
elif action == 'remove_app_rule':
|
|
170
|
+
app_path = context.get('app_path', '')
|
|
171
|
+
|
|
172
|
+
if not app_path:
|
|
173
|
+
return {'code': -1, 'msg': '应用路径不能为空!'}
|
|
174
|
+
|
|
175
|
+
if hasattr(mgr, 'remove_app_rule'):
|
|
176
|
+
success, output = mgr.remove_app_rule(app_path)
|
|
177
|
+
if success:
|
|
178
|
+
return {'code': 0, 'msg': f'已移除应用规则:{app_path}', 'data': {'output': output}}
|
|
179
|
+
else:
|
|
180
|
+
return {'code': -1, 'msg': f'移除失败:{output}', 'data': {'output': output}}
|
|
181
|
+
else:
|
|
182
|
+
return {'code': -1, 'msg': '当前防火墙类型不支持应用规则管理'}
|
|
183
|
+
|
|
184
|
+
elif action == 'list_zones':
|
|
185
|
+
success, zones = mgr.list_zones()
|
|
186
|
+
if success:
|
|
187
|
+
return {'code': 0, 'msg': '', 'data': {'zones': zones}}
|
|
188
|
+
else:
|
|
189
|
+
return {'code': -1, 'msg': '不支持区域管理', 'data': {'zones': []}}
|
|
190
|
+
|
|
191
|
+
elif action == 'get_default_zone':
|
|
192
|
+
success, zone = mgr.get_default_zone()
|
|
193
|
+
if success:
|
|
194
|
+
return {'code': 0, 'msg': '', 'data': {'zone': zone}}
|
|
195
|
+
else:
|
|
196
|
+
return {'code': -1, 'msg': '不支持区域管理', 'data': {'zone': ''}}
|
|
197
|
+
|
|
198
|
+
else:
|
|
199
|
+
return {'code': -1, 'msg': '未定义的操作!'}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2020-2026 Jackson Dou
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# InPanel is distributed under the terms of The New BSD License.
|
|
7
|
+
# The full license can be found in 'LICENSE'.
|
|
8
|
+
'''Module for Firewall Manager Base Class'''
|
|
9
|
+
|
|
10
|
+
import subprocess
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
from typing import Tuple, List, Dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FirewallManager(ABC):
|
|
16
|
+
"""防火墙管理器抽象基类"""
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def detect(self) -> bool:
|
|
20
|
+
"""检测当前系统是否匹配该防火墙管理器"""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def status(self) -> Tuple[bool, str]:
|
|
25
|
+
"""获取防火墙状态(运行中/已停止)"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def enable(self) -> Tuple[bool, str]:
|
|
30
|
+
"""启用防火墙(开机自启)"""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def disable(self) -> Tuple[bool, str]:
|
|
35
|
+
"""禁用防火墙(关闭开机自启)"""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def start(self) -> Tuple[bool, str]:
|
|
40
|
+
"""启动防火墙服务"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def stop(self) -> Tuple[bool, str]:
|
|
45
|
+
"""停止防火墙服务"""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def restart(self) -> Tuple[bool, str]:
|
|
50
|
+
"""重启防火墙服务"""
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def add_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
55
|
+
"""
|
|
56
|
+
添加端口规则
|
|
57
|
+
:param port: 端口号
|
|
58
|
+
:param protocol: 协议(tcp/udp)
|
|
59
|
+
:param zone: 区域(firewalld专用)
|
|
60
|
+
:return: (成功?, 输出日志)
|
|
61
|
+
"""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def remove_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
66
|
+
"""移除端口规则"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def add_ip_rule(self, ip: str, action: str = 'allow') -> Tuple[bool, str]:
|
|
71
|
+
"""
|
|
72
|
+
添加IP规则
|
|
73
|
+
:param ip: IP地址
|
|
74
|
+
:param action: allow/deny
|
|
75
|
+
:return: (成功?, 输出日志)
|
|
76
|
+
"""
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def remove_ip_rule(self, ip: str) -> Tuple[bool, str]:
|
|
81
|
+
"""移除IP规则"""
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def list_rules(self) -> Tuple[bool, str]:
|
|
86
|
+
"""列出所有规则"""
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def list_zones(self) -> Tuple[bool, List[str]]:
|
|
91
|
+
"""列出所有区域(firewalld专用)"""
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def get_default_zone(self) -> Tuple[bool, str]:
|
|
96
|
+
"""获取默认区域(firewalld专用)"""
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
@abstractmethod
|
|
100
|
+
def parse_rules(self) -> List[Dict]:
|
|
101
|
+
"""解析规则列表为结构化数据
|
|
102
|
+
|
|
103
|
+
返回格式示例:
|
|
104
|
+
[
|
|
105
|
+
{
|
|
106
|
+
'type': 'port', # 'port' 或 'ip'
|
|
107
|
+
'port': 80, # 端口号
|
|
108
|
+
'protocol': 'tcp', # 'tcp' 或 'udp'
|
|
109
|
+
'action': 'allow', # 'allow' 或 'deny'
|
|
110
|
+
'ip': '', # IP地址(type='ip'时使用)
|
|
111
|
+
'zone': '', # 区域(firewalld专用)
|
|
112
|
+
'description': '' # 规则描述
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
"""
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
def is_running(self) -> bool:
|
|
119
|
+
"""检查防火墙是否正在运行"""
|
|
120
|
+
success, output = self.status()
|
|
121
|
+
return success and 'running' in output.lower()
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _run_cmd(cmd: List[str]) -> Tuple[bool, str]:
|
|
125
|
+
"""执行 shell 命令并返回结果"""
|
|
126
|
+
try:
|
|
127
|
+
result = subprocess.run(
|
|
128
|
+
cmd,
|
|
129
|
+
stdout=subprocess.PIPE,
|
|
130
|
+
stderr=subprocess.PIPE,
|
|
131
|
+
text=True,
|
|
132
|
+
check=False
|
|
133
|
+
)
|
|
134
|
+
output = result.stdout + result.stderr
|
|
135
|
+
return (result.returncode == 0, output.strip())
|
|
136
|
+
except Exception as e:
|
|
137
|
+
return (False, str(e))
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2020-2026 Jackson Dou
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# InPanel is distributed under the terms of The New BSD License.
|
|
7
|
+
# The full license can be found in 'LICENSE'.
|
|
8
|
+
'''Module for Firewalld Management'''
|
|
9
|
+
|
|
10
|
+
import shutil
|
|
11
|
+
from typing import Tuple, List, Dict
|
|
12
|
+
|
|
13
|
+
from .firewall_base import FirewallManager
|
|
14
|
+
from .system import is_rhel_family, get_os_version_major
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FirewalldPM(FirewallManager):
|
|
18
|
+
"""Alma 8-10 / Rocky 8-10 / CentOS 8+ / RHEL 8+:firewalld"""
|
|
19
|
+
|
|
20
|
+
def detect(self) -> bool:
|
|
21
|
+
if not shutil.which("firewall-cmd"):
|
|
22
|
+
return False
|
|
23
|
+
if not is_rhel_family():
|
|
24
|
+
return False
|
|
25
|
+
version_major = get_os_version_major()
|
|
26
|
+
return version_major >= 8
|
|
27
|
+
|
|
28
|
+
def status(self) -> Tuple[bool, str]:
|
|
29
|
+
return self._run_cmd(["firewall-cmd", "--state"])
|
|
30
|
+
|
|
31
|
+
def enable(self) -> Tuple[bool, str]:
|
|
32
|
+
return self._run_cmd(["systemctl", "enable", "firewalld"])
|
|
33
|
+
|
|
34
|
+
def disable(self) -> Tuple[bool, str]:
|
|
35
|
+
return self._run_cmd(["systemctl", "disable", "firewalld"])
|
|
36
|
+
|
|
37
|
+
def start(self) -> Tuple[bool, str]:
|
|
38
|
+
return self._run_cmd(["systemctl", "start", "firewalld"])
|
|
39
|
+
|
|
40
|
+
def stop(self) -> Tuple[bool, str]:
|
|
41
|
+
return self._run_cmd(["systemctl", "stop", "firewalld"])
|
|
42
|
+
|
|
43
|
+
def restart(self) -> Tuple[bool, str]:
|
|
44
|
+
return self._run_cmd(["systemctl", "restart", "firewalld"])
|
|
45
|
+
|
|
46
|
+
def add_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
47
|
+
cmd = ["firewall-cmd", "--permanent"]
|
|
48
|
+
if zone:
|
|
49
|
+
cmd.extend(["--zone", zone])
|
|
50
|
+
cmd.extend(["--add-port", f"{port}/{protocol}"])
|
|
51
|
+
success, output = self._run_cmd(cmd)
|
|
52
|
+
if success:
|
|
53
|
+
self._run_cmd(["firewall-cmd", "--reload"])
|
|
54
|
+
return (success, output)
|
|
55
|
+
|
|
56
|
+
def remove_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
57
|
+
cmd = ["firewall-cmd", "--permanent"]
|
|
58
|
+
if zone:
|
|
59
|
+
cmd.extend(["--zone", zone])
|
|
60
|
+
cmd.extend(["--remove-port", f"{port}/{protocol}"])
|
|
61
|
+
success, output = self._run_cmd(cmd)
|
|
62
|
+
if success:
|
|
63
|
+
self._run_cmd(["firewall-cmd", "--reload"])
|
|
64
|
+
return (success, output)
|
|
65
|
+
|
|
66
|
+
def add_ip_rule(self, ip: str, action: str = 'allow') -> Tuple[bool, str]:
|
|
67
|
+
cmd = ["firewall-cmd", "--permanent"]
|
|
68
|
+
if action == 'allow':
|
|
69
|
+
cmd.extend(["--add-source", ip])
|
|
70
|
+
else:
|
|
71
|
+
cmd.extend(["--add-rich-rule", f'rule family="ipv4" source address="{ip}" reject'])
|
|
72
|
+
success, output = self._run_cmd(cmd)
|
|
73
|
+
if success:
|
|
74
|
+
self._run_cmd(["firewall-cmd", "--reload"])
|
|
75
|
+
return (success, output)
|
|
76
|
+
|
|
77
|
+
def remove_ip_rule(self, ip: str) -> Tuple[bool, str]:
|
|
78
|
+
cmd = ["firewall-cmd", "--permanent", "--remove-source", ip]
|
|
79
|
+
success, output = self._run_cmd(cmd)
|
|
80
|
+
if success:
|
|
81
|
+
self._run_cmd(["firewall-cmd", "--reload"])
|
|
82
|
+
return (success, output)
|
|
83
|
+
|
|
84
|
+
def list_rules(self) -> Tuple[bool, str]:
|
|
85
|
+
return self._run_cmd(["firewall-cmd", "--list-all"])
|
|
86
|
+
|
|
87
|
+
def list_zones(self) -> Tuple[bool, List[str]]:
|
|
88
|
+
success, output = self._run_cmd(["firewall-cmd", "--get-zones"])
|
|
89
|
+
if success:
|
|
90
|
+
zones = output.split()
|
|
91
|
+
return (True, zones)
|
|
92
|
+
return (False, [])
|
|
93
|
+
|
|
94
|
+
def get_default_zone(self) -> Tuple[bool, str]:
|
|
95
|
+
return self._run_cmd(["firewall-cmd", "--get-default-zone"])
|
|
96
|
+
|
|
97
|
+
def parse_rules(self) -> List[Dict]:
|
|
98
|
+
rules = []
|
|
99
|
+
success, output = self._run_cmd(["firewall-cmd", "--list-all"])
|
|
100
|
+
if not success:
|
|
101
|
+
return rules
|
|
102
|
+
|
|
103
|
+
zone = ""
|
|
104
|
+
lines = output.split('\n')
|
|
105
|
+
for line in lines:
|
|
106
|
+
if line and not line.startswith(' '):
|
|
107
|
+
zone = line.split(' ')[0]
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
line = line.strip()
|
|
111
|
+
if line.startswith('services:'):
|
|
112
|
+
services = line.replace('services:', '').strip()
|
|
113
|
+
if services and services != '-':
|
|
114
|
+
for svc in services.split():
|
|
115
|
+
rules.append({
|
|
116
|
+
'type': 'service',
|
|
117
|
+
'service': svc,
|
|
118
|
+
'port': '',
|
|
119
|
+
'protocol': '',
|
|
120
|
+
'action': 'allow',
|
|
121
|
+
'ip': '',
|
|
122
|
+
'zone': zone,
|
|
123
|
+
'description': f"服务: {svc}"
|
|
124
|
+
})
|
|
125
|
+
elif line.startswith('ports:'):
|
|
126
|
+
ports = line.replace('ports:', '').strip()
|
|
127
|
+
if ports and ports != '-':
|
|
128
|
+
for port in ports.split():
|
|
129
|
+
parts = port.split('/')
|
|
130
|
+
p = parts[0]
|
|
131
|
+
proto = parts[1] if len(parts) > 1 else 'tcp'
|
|
132
|
+
rules.append({
|
|
133
|
+
'type': 'port',
|
|
134
|
+
'service': '',
|
|
135
|
+
'port': int(p) if p.isdigit() else p,
|
|
136
|
+
'protocol': proto,
|
|
137
|
+
'action': 'allow',
|
|
138
|
+
'ip': '',
|
|
139
|
+
'zone': zone,
|
|
140
|
+
'description': f"端口: {p}/{proto}"
|
|
141
|
+
})
|
|
142
|
+
elif line.startswith('sources:'):
|
|
143
|
+
sources = line.replace('sources:', '').strip()
|
|
144
|
+
if sources and sources != '-':
|
|
145
|
+
for source in sources.split():
|
|
146
|
+
rules.append({
|
|
147
|
+
'type': 'ip',
|
|
148
|
+
'service': '',
|
|
149
|
+
'port': '',
|
|
150
|
+
'protocol': '',
|
|
151
|
+
'action': 'allow',
|
|
152
|
+
'ip': source,
|
|
153
|
+
'zone': zone,
|
|
154
|
+
'description': f"允许IP: {source}"
|
|
155
|
+
})
|
|
156
|
+
elif line.startswith('rich rules:'):
|
|
157
|
+
rich_rules = line.replace('rich rules:', '').strip()
|
|
158
|
+
if rich_rules and rich_rules != '-':
|
|
159
|
+
rules.append({
|
|
160
|
+
'type': 'rich',
|
|
161
|
+
'service': '',
|
|
162
|
+
'port': '',
|
|
163
|
+
'protocol': '',
|
|
164
|
+
'action': 'allow',
|
|
165
|
+
'ip': '',
|
|
166
|
+
'zone': zone,
|
|
167
|
+
'description': f"富规则: {rich_rules}"
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
return rules
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2020-2026 Jackson Dou
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# InPanel is distributed under the terms of The New BSD License.
|
|
7
|
+
# The full license can be found in 'LICENSE'.
|
|
8
|
+
'''Module for iptables Management'''
|
|
9
|
+
|
|
10
|
+
import shutil
|
|
11
|
+
from typing import Tuple, List, Dict
|
|
12
|
+
|
|
13
|
+
from .firewall_base import FirewallManager
|
|
14
|
+
from .system import is_rhel_family, get_os_version_major
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class IptablesPM(FirewallManager):
|
|
18
|
+
"""CentOS 7 及以下 / RHEL 7 及以下:iptables"""
|
|
19
|
+
|
|
20
|
+
def detect(self) -> bool:
|
|
21
|
+
if not shutil.which("iptables"):
|
|
22
|
+
return False
|
|
23
|
+
if not is_rhel_family():
|
|
24
|
+
return False
|
|
25
|
+
version_major = get_os_version_major()
|
|
26
|
+
return version_major <= 7 or version_major == 0
|
|
27
|
+
|
|
28
|
+
def status(self) -> Tuple[bool, str]:
|
|
29
|
+
success, output = self._run_cmd(["systemctl", "status", "iptables"])
|
|
30
|
+
if not success:
|
|
31
|
+
success, output = self._run_cmd(["service", "iptables", "status"])
|
|
32
|
+
return (success, output)
|
|
33
|
+
|
|
34
|
+
def enable(self) -> Tuple[bool, str]:
|
|
35
|
+
success, output = self._run_cmd(["systemctl", "enable", "iptables"])
|
|
36
|
+
if not success:
|
|
37
|
+
success, output = self._run_cmd(["chkconfig", "iptables", "on"])
|
|
38
|
+
return (success, output)
|
|
39
|
+
|
|
40
|
+
def disable(self) -> Tuple[bool, str]:
|
|
41
|
+
success, output = self._run_cmd(["systemctl", "disable", "iptables"])
|
|
42
|
+
if not success:
|
|
43
|
+
success, output = self._run_cmd(["chkconfig", "iptables", "off"])
|
|
44
|
+
return (success, output)
|
|
45
|
+
|
|
46
|
+
def start(self) -> Tuple[bool, str]:
|
|
47
|
+
success, output = self._run_cmd(["systemctl", "start", "iptables"])
|
|
48
|
+
if not success:
|
|
49
|
+
success, output = self._run_cmd(["service", "iptables", "start"])
|
|
50
|
+
return (success, output)
|
|
51
|
+
|
|
52
|
+
def stop(self) -> Tuple[bool, str]:
|
|
53
|
+
success, output = self._run_cmd(["systemctl", "stop", "iptables"])
|
|
54
|
+
if not success:
|
|
55
|
+
success, output = self._run_cmd(["service", "iptables", "stop"])
|
|
56
|
+
return (success, output)
|
|
57
|
+
|
|
58
|
+
def restart(self) -> Tuple[bool, str]:
|
|
59
|
+
success, output = self._run_cmd(["systemctl", "restart", "iptables"])
|
|
60
|
+
if not success:
|
|
61
|
+
success, output = self._run_cmd(["service", "iptables", "restart"])
|
|
62
|
+
return (success, output)
|
|
63
|
+
|
|
64
|
+
def add_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
65
|
+
cmd = ["iptables", "-A", "INPUT", "-p", protocol, "--dport", str(port), "-j", "ACCEPT"]
|
|
66
|
+
success, output = self._run_cmd(cmd)
|
|
67
|
+
if success:
|
|
68
|
+
self._run_cmd(["service", "iptables", "save"])
|
|
69
|
+
return (success, output)
|
|
70
|
+
|
|
71
|
+
def remove_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
72
|
+
cmd = ["iptables", "-D", "INPUT", "-p", protocol, "--dport", str(port), "-j", "ACCEPT"]
|
|
73
|
+
success, output = self._run_cmd(cmd)
|
|
74
|
+
if success:
|
|
75
|
+
self._run_cmd(["service", "iptables", "save"])
|
|
76
|
+
return (success, output)
|
|
77
|
+
|
|
78
|
+
def add_ip_rule(self, ip: str, action: str = 'allow') -> Tuple[bool, str]:
|
|
79
|
+
chain = "ACCEPT" if action == 'allow' else "DROP"
|
|
80
|
+
cmd = ["iptables", "-A", "INPUT", "-s", ip, "-j", chain]
|
|
81
|
+
success, output = self._run_cmd(cmd)
|
|
82
|
+
if success:
|
|
83
|
+
self._run_cmd(["service", "iptables", "save"])
|
|
84
|
+
return (success, output)
|
|
85
|
+
|
|
86
|
+
def remove_ip_rule(self, ip: str) -> Tuple[bool, str]:
|
|
87
|
+
cmd = ["iptables", "-D", "INPUT", "-s", ip, "-j", "ACCEPT"]
|
|
88
|
+
success1, output1 = self._run_cmd(cmd)
|
|
89
|
+
cmd = ["iptables", "-D", "INPUT", "-s", ip, "-j", "DROP"]
|
|
90
|
+
success2, output2 = self._run_cmd(cmd)
|
|
91
|
+
if success1 or success2:
|
|
92
|
+
self._run_cmd(["service", "iptables", "save"])
|
|
93
|
+
return (True, output1 + output2)
|
|
94
|
+
return (False, output1 + output2)
|
|
95
|
+
|
|
96
|
+
def list_rules(self) -> Tuple[bool, str]:
|
|
97
|
+
return self._run_cmd(["iptables", "-L", "-n"])
|
|
98
|
+
|
|
99
|
+
def list_zones(self) -> Tuple[bool, List[str]]:
|
|
100
|
+
return (False, [])
|
|
101
|
+
|
|
102
|
+
def get_default_zone(self) -> Tuple[bool, str]:
|
|
103
|
+
return (False, "Not supported")
|
|
104
|
+
|
|
105
|
+
def parse_rules(self) -> List[Dict]:
|
|
106
|
+
rules = []
|
|
107
|
+
success, output = self._run_cmd(["iptables", "-L", "-n", "-v"])
|
|
108
|
+
if not success:
|
|
109
|
+
return rules
|
|
110
|
+
|
|
111
|
+
current_chain = ""
|
|
112
|
+
lines = output.split('\n')
|
|
113
|
+
for line in lines:
|
|
114
|
+
line = line.strip()
|
|
115
|
+
if not line:
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
if line.startswith('Chain '):
|
|
119
|
+
current_chain = line.split()[1]
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
parts = line.split()
|
|
123
|
+
if len(parts) < 10:
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
pkts = parts[0]
|
|
127
|
+
bytes = parts[1]
|
|
128
|
+
target = parts[2]
|
|
129
|
+
prot = parts[3]
|
|
130
|
+
opt = parts[4]
|
|
131
|
+
in_iface = parts[5]
|
|
132
|
+
out_iface = parts[6]
|
|
133
|
+
source = parts[7]
|
|
134
|
+
destination = parts[8]
|
|
135
|
+
|
|
136
|
+
action = 'allow' if target == 'ACCEPT' else ('deny' if target == 'DROP' or target == 'REJECT' else target.lower())
|
|
137
|
+
|
|
138
|
+
port = ''
|
|
139
|
+
ip = ''
|
|
140
|
+
|
|
141
|
+
for i in range(9, len(parts)):
|
|
142
|
+
if parts[i].startswith('dpt:'):
|
|
143
|
+
port = parts[i].replace('dpt:', '')
|
|
144
|
+
elif parts[i].startswith('spt:'):
|
|
145
|
+
port = parts[i].replace('spt:', '')
|
|
146
|
+
|
|
147
|
+
if source != '0.0.0.0/0' and '/' not in source:
|
|
148
|
+
ip = source
|
|
149
|
+
elif source != '0.0.0.0/0':
|
|
150
|
+
ip = source
|
|
151
|
+
|
|
152
|
+
if port or ip or source != '0.0.0.0/0':
|
|
153
|
+
if port:
|
|
154
|
+
rules.append({
|
|
155
|
+
'type': 'port',
|
|
156
|
+
'service': '',
|
|
157
|
+
'port': int(port) if port.isdigit() else port,
|
|
158
|
+
'protocol': prot,
|
|
159
|
+
'action': action,
|
|
160
|
+
'ip': '',
|
|
161
|
+
'zone': '',
|
|
162
|
+
'description': f"{action.upper()}: {prot} dpt:{port} from {source}"
|
|
163
|
+
})
|
|
164
|
+
elif ip:
|
|
165
|
+
rules.append({
|
|
166
|
+
'type': 'ip',
|
|
167
|
+
'service': '',
|
|
168
|
+
'port': '',
|
|
169
|
+
'protocol': '',
|
|
170
|
+
'action': action,
|
|
171
|
+
'ip': ip,
|
|
172
|
+
'zone': '',
|
|
173
|
+
'description': f"{action.upper()}: {source}"
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
return rules
|