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.
Files changed (198) hide show
  1. inpanel/__init__.py +29 -0
  2. inpanel/acme.py +289 -0
  3. inpanel/aliyuncs.py +269 -0
  4. inpanel/app.py +514 -0
  5. inpanel/apt.py +223 -0
  6. inpanel/base.py +200 -0
  7. inpanel/config.py +139 -0
  8. inpanel/data/config.ini +29 -0
  9. inpanel/data/history +5 -0
  10. inpanel/data/runlogs.ini +12 -0
  11. inpanel/data/update_info.ini +4 -0
  12. inpanel/dnf.py +225 -0
  13. inpanel/lib/__init__.py +9 -0
  14. inpanel/lib/async_process.py +98 -0
  15. inpanel/lib/filelock.py +82 -0
  16. inpanel/lib/magic.py +233 -0
  17. inpanel/lib/pxssh.py +310 -0
  18. inpanel/lib/pyDes.py +867 -0
  19. inpanel/mod/__init__.py +0 -0
  20. inpanel/mod/apt.py +316 -0
  21. inpanel/mod/certificate.py +268 -0
  22. inpanel/mod/config.py +372 -0
  23. inpanel/mod/cron.py +315 -0
  24. inpanel/mod/disk.py +322 -0
  25. inpanel/mod/dnf.py +239 -0
  26. inpanel/mod/file.py +722 -0
  27. inpanel/mod/firewall.py +199 -0
  28. inpanel/mod/firewall_base.py +137 -0
  29. inpanel/mod/firewall_firewalld.py +170 -0
  30. inpanel/mod/firewall_iptables.py +176 -0
  31. inpanel/mod/firewall_macos.py +213 -0
  32. inpanel/mod/firewall_ufw.py +128 -0
  33. inpanel/mod/ftp.py +231 -0
  34. inpanel/mod/httpd.py +1205 -0
  35. inpanel/mod/lighttpd.py +26 -0
  36. inpanel/mod/login.py +98 -0
  37. inpanel/mod/mysql.py +577 -0
  38. inpanel/mod/named.py +26 -0
  39. inpanel/mod/nginx.py +2327 -0
  40. inpanel/mod/package.py +33 -0
  41. inpanel/mod/package_apt.py +62 -0
  42. inpanel/mod/package_base.py +77 -0
  43. inpanel/mod/package_dnf.py +62 -0
  44. inpanel/mod/package_map.py +366 -0
  45. inpanel/mod/package_yum.py +64 -0
  46. inpanel/mod/php.py +289 -0
  47. inpanel/mod/process.py +291 -0
  48. inpanel/mod/proftpd.py +26 -0
  49. inpanel/mod/pureftpd.py +26 -0
  50. inpanel/mod/query.py +108 -0
  51. inpanel/mod/server.py +1319 -0
  52. inpanel/mod/service.py +262 -0
  53. inpanel/mod/setting.py +228 -0
  54. inpanel/mod/shell.py +78 -0
  55. inpanel/mod/ssh.py +280 -0
  56. inpanel/mod/system.py +220 -0
  57. inpanel/mod/task.py +1337 -0
  58. inpanel/mod/ufw.py +14 -0
  59. inpanel/mod/user.py +306 -0
  60. inpanel/mod/vsftpd.py +67 -0
  61. inpanel/mod/yum.py +243 -0
  62. inpanel/plugins/__init__.py +10 -0
  63. inpanel/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
  64. inpanel/plugins/acme/static/index.html +10 -0
  65. inpanel/plugins/acme/static/index.js +13 -0
  66. inpanel/plugins/shadowsocks-libev/__pycache__/info.cpython-313.pyc +0 -0
  67. inpanel/plugins/shadowsocks-libev/info.py +73 -0
  68. inpanel/plugins/shadowsocks-libev/static/index.html +70 -0
  69. inpanel/plugins/shadowsocks-libev/static/index.js +18 -0
  70. inpanel/public/css/global.css +138 -0
  71. inpanel/public/favicon.ico +0 -0
  72. inpanel/public/images/alipay.jpg +0 -0
  73. inpanel/public/images/loading.gif +0 -0
  74. inpanel/public/images/weixin.jpg +0 -0
  75. inpanel/public/index.html +94 -0
  76. inpanel/public/js/controllers/controllers.js +566 -0
  77. inpanel/public/js/controllers/database.js +590 -0
  78. inpanel/public/js/controllers/ecs.js +624 -0
  79. inpanel/public/js/controllers/file.js +940 -0
  80. inpanel/public/js/controllers/plugins.js +105 -0
  81. inpanel/public/js/controllers/service.js +1460 -0
  82. inpanel/public/js/controllers/site.js +1241 -0
  83. inpanel/public/js/controllers/utils.js +2319 -0
  84. inpanel/public/js/core.js +221 -0
  85. inpanel/public/js/directives.js +784 -0
  86. inpanel/public/js/filters.js +395 -0
  87. inpanel/public/js/services.js +257 -0
  88. inpanel/public/lib/angular/1.0.2/angular-resource.min.js +10 -0
  89. inpanel/public/lib/angular/1.0.2/angular.min.js +158 -0
  90. inpanel/public/lib/bootstrap/css/bootstrap-theme.css +587 -0
  91. inpanel/public/lib/bootstrap/css/bootstrap-theme.css.map +1 -0
  92. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css +6 -0
  93. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css.map +1 -0
  94. inpanel/public/lib/bootstrap/css/bootstrap.css +6757 -0
  95. inpanel/public/lib/bootstrap/css/bootstrap.css.map +1 -0
  96. inpanel/public/lib/bootstrap/css/bootstrap.min.css +6 -0
  97. inpanel/public/lib/bootstrap/css/bootstrap.min.css.map +1 -0
  98. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  99. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  100. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  101. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  102. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  103. inpanel/public/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
  104. inpanel/public/lib/bootstrap/img/glyphicons-halflings.png +0 -0
  105. inpanel/public/lib/bootstrap/js/bootstrap.js +2377 -0
  106. inpanel/public/lib/bootstrap/js/bootstrap.min.js +7 -0
  107. inpanel/public/lib/bootstrap/js/npm.js +13 -0
  108. inpanel/public/lib/codemirror/codemirror-compressed.js +1 -0
  109. inpanel/public/lib/codemirror/codemirror.css +173 -0
  110. inpanel/public/lib/jquery/jquery-1.12.4.min.js +5 -0
  111. inpanel/public/lib/md5/md5.js +379 -0
  112. inpanel/public/lib/md5/md5.min.js +1 -0
  113. inpanel/public/partials/database/index.html +172 -0
  114. inpanel/public/partials/database/mysql/dbedit.html +462 -0
  115. inpanel/public/partials/database/mysql/dbnew.html +348 -0
  116. inpanel/public/partials/database/mysql/useredit.html +310 -0
  117. inpanel/public/partials/database/mysql/usernew.html +97 -0
  118. inpanel/public/partials/directives/message.html +20 -0
  119. inpanel/public/partials/directives/navbar.html +25 -0
  120. inpanel/public/partials/directives/selector.html +57 -0
  121. inpanel/public/partials/directives/srvbase.html +72 -0
  122. inpanel/public/partials/directives/srvext.html +34 -0
  123. inpanel/public/partials/directives/srvfile.html +23 -0
  124. inpanel/public/partials/directives/srvinstall.html +34 -0
  125. inpanel/public/partials/directives/srvlog.html +23 -0
  126. inpanel/public/partials/directives/srvminiop.html +28 -0
  127. inpanel/public/partials/directives/srvuninstall.html +38 -0
  128. inpanel/public/partials/directives/srvupdate.html +30 -0
  129. inpanel/public/partials/ecs/account.html +156 -0
  130. inpanel/public/partials/ecs/ecs.html +198 -0
  131. inpanel/public/partials/ecs/index.html +40 -0
  132. inpanel/public/partials/ecs/setings.html +459 -0
  133. inpanel/public/partials/file/file.html +501 -0
  134. inpanel/public/partials/file/trash.html +95 -0
  135. inpanel/public/partials/ftp.html +36 -0
  136. inpanel/public/partials/log.html +83 -0
  137. inpanel/public/partials/login.html +79 -0
  138. inpanel/public/partials/logout.html +22 -0
  139. inpanel/public/partials/main.html +534 -0
  140. inpanel/public/partials/plugins/index.html +55 -0
  141. inpanel/public/partials/plugins/plugins.html +13 -0
  142. inpanel/public/partials/secure.html +13 -0
  143. inpanel/public/partials/service/apache.html +147 -0
  144. inpanel/public/partials/service/cron.html +71 -0
  145. inpanel/public/partials/service/index.html +293 -0
  146. inpanel/public/partials/service/iptables.html +40 -0
  147. inpanel/public/partials/service/lighttpd.html +63 -0
  148. inpanel/public/partials/service/mariadb.html +113 -0
  149. inpanel/public/partials/service/memcache.html +36 -0
  150. inpanel/public/partials/service/minio.html +44 -0
  151. inpanel/public/partials/service/mongodb.html +44 -0
  152. inpanel/public/partials/service/mysql.html +113 -0
  153. inpanel/public/partials/service/named.html +61 -0
  154. inpanel/public/partials/service/nginx.html +296 -0
  155. inpanel/public/partials/service/ntp.html +40 -0
  156. inpanel/public/partials/service/php.html +250 -0
  157. inpanel/public/partials/service/proftpd.html +59 -0
  158. inpanel/public/partials/service/pureftpd.html +59 -0
  159. inpanel/public/partials/service/redis.html +40 -0
  160. inpanel/public/partials/service/samba.html +58 -0
  161. inpanel/public/partials/service/sendmail.html +40 -0
  162. inpanel/public/partials/service/ssh.html +186 -0
  163. inpanel/public/partials/service/tomcat.html +65 -0
  164. inpanel/public/partials/service/vsftpd.html +195 -0
  165. inpanel/public/partials/setting.html +299 -0
  166. inpanel/public/partials/site/apache/site.html +277 -0
  167. inpanel/public/partials/site/index.html +344 -0
  168. inpanel/public/partials/site/nginx/site.html +558 -0
  169. inpanel/public/partials/sorry.html +19 -0
  170. inpanel/public/partials/storage/autofm.html +103 -0
  171. inpanel/public/partials/storage/backup.html +42 -0
  172. inpanel/public/partials/storage/index.html +360 -0
  173. inpanel/public/partials/storage/migrate.html +93 -0
  174. inpanel/public/partials/storage/movedata.html +98 -0
  175. inpanel/public/partials/storage/remote.html +77 -0
  176. inpanel/public/partials/utils/cron.html +486 -0
  177. inpanel/public/partials/utils/firewall.html +266 -0
  178. inpanel/public/partials/utils/index.html +193 -0
  179. inpanel/public/partials/utils/network.html +118 -0
  180. inpanel/public/partials/utils/process.html +239 -0
  181. inpanel/public/partials/utils/repository.html +373 -0
  182. inpanel/public/partials/utils/shell.html +28 -0
  183. inpanel/public/partials/utils/ssl.html +319 -0
  184. inpanel/public/partials/utils/time.html +109 -0
  185. inpanel/public/partials/utils/user.html +359 -0
  186. inpanel/public/robots.txt +2 -0
  187. inpanel/remote.py +109 -0
  188. inpanel/templates/file/preview.html +34 -0
  189. inpanel/templates/index.html +93 -0
  190. inpanel/utils.py +282 -0
  191. inpanel/web.py +1811 -0
  192. inpanel/yum.py +756 -0
  193. inpanel-1.2.1.dist-info/METADATA +399 -0
  194. inpanel-1.2.1.dist-info/RECORD +198 -0
  195. inpanel-1.2.1.dist-info/WHEEL +5 -0
  196. inpanel-1.2.1.dist-info/entry_points.txt +2 -0
  197. inpanel-1.2.1.dist-info/licenses/LICENSE +30 -0
  198. inpanel-1.2.1.dist-info/top_level.txt +1 -0
@@ -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