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
|
@@ -0,0 +1,213 @@
|
|
|
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 macOS Firewall Management'''
|
|
9
|
+
|
|
10
|
+
import shutil
|
|
11
|
+
from typing import Tuple, List, Dict
|
|
12
|
+
|
|
13
|
+
from .firewall_base import FirewallManager
|
|
14
|
+
from .system import get_os_family
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MacosFirewallPM(FirewallManager):
|
|
18
|
+
"""macOS 防火墙管理器"""
|
|
19
|
+
|
|
20
|
+
SOCKETFILTERFW = '/usr/libexec/ApplicationFirewall/socketfilterfw'
|
|
21
|
+
PFCTL = '/sbin/pfctl'
|
|
22
|
+
|
|
23
|
+
def detect(self) -> bool:
|
|
24
|
+
"""检测当前系统是否为 macOS"""
|
|
25
|
+
return get_os_family() == 'darwin'
|
|
26
|
+
|
|
27
|
+
def status(self) -> Tuple[bool, str]:
|
|
28
|
+
"""获取防火墙状态"""
|
|
29
|
+
if shutil.which(self.SOCKETFILTERFW):
|
|
30
|
+
success, output = self._run_cmd([self.SOCKETFILTERFW, '--getglobalstate'])
|
|
31
|
+
if success:
|
|
32
|
+
if 'enabled' in output.lower():
|
|
33
|
+
return (True, '运行中')
|
|
34
|
+
elif 'disabled' in output.lower():
|
|
35
|
+
return (True, '已停止')
|
|
36
|
+
return (False, '未知')
|
|
37
|
+
|
|
38
|
+
def enable(self) -> Tuple[bool, str]:
|
|
39
|
+
"""启用防火墙(开机自启)"""
|
|
40
|
+
if shutil.which(self.SOCKETFILTERFW):
|
|
41
|
+
success, output = self._run_cmd(['sudo', self.SOCKETFILTERFW, '--setglobalstate', 'on'])
|
|
42
|
+
if success:
|
|
43
|
+
return (True, '防火墙已启用')
|
|
44
|
+
return (False, '无法启用防火墙')
|
|
45
|
+
|
|
46
|
+
def disable(self) -> Tuple[bool, str]:
|
|
47
|
+
"""禁用防火墙(关闭开机自启)"""
|
|
48
|
+
if shutil.which(self.SOCKETFILTERFW):
|
|
49
|
+
success, output = self._run_cmd(['sudo', self.SOCKETFILTERFW, '--setglobalstate', 'off'])
|
|
50
|
+
if success:
|
|
51
|
+
return (True, '防火墙已禁用')
|
|
52
|
+
return (False, '无法禁用防火墙')
|
|
53
|
+
|
|
54
|
+
def start(self) -> Tuple[bool, str]:
|
|
55
|
+
"""启动防火墙服务"""
|
|
56
|
+
return self.enable()
|
|
57
|
+
|
|
58
|
+
def stop(self) -> Tuple[bool, str]:
|
|
59
|
+
"""停止防火墙服务"""
|
|
60
|
+
return self.disable()
|
|
61
|
+
|
|
62
|
+
def restart(self) -> Tuple[bool, str]:
|
|
63
|
+
"""重启防火墙服务"""
|
|
64
|
+
self.disable()
|
|
65
|
+
return self.enable()
|
|
66
|
+
|
|
67
|
+
def add_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
68
|
+
"""添加端口规则 - macOS 使用 pf"""
|
|
69
|
+
if shutil.which(self.PFCTL):
|
|
70
|
+
rule = f'pass in proto {protocol} from any to any port {port}'
|
|
71
|
+
success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'add', rule])
|
|
72
|
+
if not success:
|
|
73
|
+
success, output = self._run_cmd(['sudo', self.PFCTL, '-f', '/dev/stdin'], input=rule)
|
|
74
|
+
if success:
|
|
75
|
+
return (True, f'已添加端口规则: {port}/{protocol}')
|
|
76
|
+
return (False, '无法添加端口规则')
|
|
77
|
+
|
|
78
|
+
def remove_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
79
|
+
"""移除端口规则"""
|
|
80
|
+
if shutil.which(self.PFCTL):
|
|
81
|
+
success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'delete', f'pass in proto {protocol} from any to any port {port}'])
|
|
82
|
+
if success:
|
|
83
|
+
return (True, f'已移除端口规则: {port}/{protocol}')
|
|
84
|
+
return (False, '无法移除端口规则')
|
|
85
|
+
|
|
86
|
+
def add_ip_rule(self, ip: str, action: str = 'allow') -> Tuple[bool, str]:
|
|
87
|
+
"""添加IP规则"""
|
|
88
|
+
if shutil.which(self.PFCTL):
|
|
89
|
+
if action == 'allow':
|
|
90
|
+
rule = f'pass in from {ip} to any'
|
|
91
|
+
else:
|
|
92
|
+
rule = f'block in from {ip} to any'
|
|
93
|
+
success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'add', rule])
|
|
94
|
+
if success:
|
|
95
|
+
return (True, f'已添加IP规则: {action} {ip}')
|
|
96
|
+
return (False, '无法添加IP规则')
|
|
97
|
+
|
|
98
|
+
def remove_ip_rule(self, ip: str) -> Tuple[bool, str]:
|
|
99
|
+
"""移除IP规则"""
|
|
100
|
+
if shutil.which(self.PFCTL):
|
|
101
|
+
success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'delete', f'pass in from {ip} to any'])
|
|
102
|
+
if not success:
|
|
103
|
+
success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'delete', f'block in from {ip} to any'])
|
|
104
|
+
if success:
|
|
105
|
+
return (True, f'已移除IP规则: {ip}')
|
|
106
|
+
return (False, '无法移除IP规则')
|
|
107
|
+
|
|
108
|
+
def remove_app_rule(self, app_path: str) -> Tuple[bool, str]:
|
|
109
|
+
"""移除应用规则"""
|
|
110
|
+
if shutil.which(self.SOCKETFILTERFW):
|
|
111
|
+
success, output = self._run_cmd(['sudo', self.SOCKETFILTERFW, '--remove', app_path])
|
|
112
|
+
if success:
|
|
113
|
+
return (True, f'已移除应用规则: {app_path}')
|
|
114
|
+
return (False, '无法移除应用规则')
|
|
115
|
+
|
|
116
|
+
def list_rules(self) -> Tuple[bool, str]:
|
|
117
|
+
"""列出所有规则"""
|
|
118
|
+
rules = []
|
|
119
|
+
|
|
120
|
+
if shutil.which(self.SOCKETFILTERFW):
|
|
121
|
+
success, output = self._run_cmd([self.SOCKETFILTERFW, '--listapps'])
|
|
122
|
+
if success:
|
|
123
|
+
rules.append("=== 应用规则 ===")
|
|
124
|
+
rules.append(output)
|
|
125
|
+
|
|
126
|
+
if shutil.which(self.PFCTL):
|
|
127
|
+
success, output = self._run_cmd(['sudo', self.PFCTL, '-s', 'rules'])
|
|
128
|
+
if success:
|
|
129
|
+
rules.append("\n=== PF 端口规则 ===")
|
|
130
|
+
rules.append(output)
|
|
131
|
+
|
|
132
|
+
return (True, '\n'.join(rules) if rules else '无规则')
|
|
133
|
+
|
|
134
|
+
def list_zones(self) -> Tuple[bool, List[str]]:
|
|
135
|
+
"""macOS 不使用区域概念"""
|
|
136
|
+
return (True, [])
|
|
137
|
+
|
|
138
|
+
def get_default_zone(self) -> Tuple[bool, str]:
|
|
139
|
+
"""macOS 不使用区域概念"""
|
|
140
|
+
return (True, '')
|
|
141
|
+
|
|
142
|
+
def parse_rules(self) -> List[Dict]:
|
|
143
|
+
"""解析规则列表为结构化数据"""
|
|
144
|
+
rules = []
|
|
145
|
+
success, output = self.list_rules()
|
|
146
|
+
|
|
147
|
+
if not success:
|
|
148
|
+
return rules
|
|
149
|
+
|
|
150
|
+
lines = output.split('\n')
|
|
151
|
+
app_section = False
|
|
152
|
+
pf_section = False
|
|
153
|
+
current_app_path = ''
|
|
154
|
+
|
|
155
|
+
for line in lines:
|
|
156
|
+
line = line.strip()
|
|
157
|
+
if not line:
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
if '=== 应用规则 ===' in line:
|
|
161
|
+
app_section = True
|
|
162
|
+
pf_section = False
|
|
163
|
+
continue
|
|
164
|
+
if '=== PF 端口规则 ===' in line:
|
|
165
|
+
app_section = False
|
|
166
|
+
pf_section = True
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
if app_section and line:
|
|
170
|
+
if 'total number of apps' in line:
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
import re
|
|
174
|
+
app_match = re.match(r'\d+\s*:\s*(/.+)', line)
|
|
175
|
+
if app_match:
|
|
176
|
+
current_app_path = app_match.group(1).strip()
|
|
177
|
+
app_name = current_app_path.split('/')[-1].replace('.app', '')
|
|
178
|
+
rules.append({
|
|
179
|
+
'type': 'app',
|
|
180
|
+
'port': '',
|
|
181
|
+
'protocol': '',
|
|
182
|
+
'action': 'allow',
|
|
183
|
+
'ip': '',
|
|
184
|
+
'zone': '',
|
|
185
|
+
'description': app_name,
|
|
186
|
+
'app_path': current_app_path
|
|
187
|
+
})
|
|
188
|
+
elif 'Allow incoming connections' in line and current_app_path:
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
if pf_section and line:
|
|
192
|
+
import re
|
|
193
|
+
port_match = re.search(r'port\s+(\d+)', line)
|
|
194
|
+
proto_match = re.search(r'proto\s+(\w+)', line)
|
|
195
|
+
ip_match = re.search(r'from\s+(\d+\.\d+\.\d+\.\d+)', line)
|
|
196
|
+
|
|
197
|
+
rule_type = 'port' if port_match else 'ip'
|
|
198
|
+
port = port_match.group(1) if port_match else ''
|
|
199
|
+
protocol = proto_match.group(1) if proto_match else 'tcp'
|
|
200
|
+
ip = ip_match.group(1) if ip_match else ''
|
|
201
|
+
action = 'allow' if 'pass' in line else 'deny'
|
|
202
|
+
|
|
203
|
+
rules.append({
|
|
204
|
+
'type': rule_type,
|
|
205
|
+
'port': port,
|
|
206
|
+
'protocol': protocol,
|
|
207
|
+
'action': action,
|
|
208
|
+
'ip': ip,
|
|
209
|
+
'zone': '',
|
|
210
|
+
'description': line
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
return rules
|
|
@@ -0,0 +1,128 @@
|
|
|
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 UFW (Uncomplicated Firewall) Management'''
|
|
9
|
+
|
|
10
|
+
import shutil
|
|
11
|
+
from typing import Tuple, List, Dict
|
|
12
|
+
|
|
13
|
+
from .firewall_base import FirewallManager
|
|
14
|
+
from .system import is_debian_family
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class UfwPM(FirewallManager):
|
|
18
|
+
"""Debian 9-13 / Ubuntu 18.04+ / Linux Mint:ufw"""
|
|
19
|
+
|
|
20
|
+
def detect(self) -> bool:
|
|
21
|
+
if not shutil.which("ufw"):
|
|
22
|
+
return False
|
|
23
|
+
return is_debian_family()
|
|
24
|
+
|
|
25
|
+
def status(self) -> Tuple[bool, str]:
|
|
26
|
+
return self._run_cmd(["ufw", "status", "verbose"])
|
|
27
|
+
|
|
28
|
+
def enable(self) -> Tuple[bool, str]:
|
|
29
|
+
return self._run_cmd(["ufw", "enable"])
|
|
30
|
+
|
|
31
|
+
def disable(self) -> Tuple[bool, str]:
|
|
32
|
+
return self._run_cmd(["ufw", "disable"])
|
|
33
|
+
|
|
34
|
+
def start(self) -> Tuple[bool, str]:
|
|
35
|
+
return self._run_cmd(["ufw", "enable"])
|
|
36
|
+
|
|
37
|
+
def stop(self) -> Tuple[bool, str]:
|
|
38
|
+
return self._run_cmd(["ufw", "disable"])
|
|
39
|
+
|
|
40
|
+
def restart(self) -> Tuple[bool, str]:
|
|
41
|
+
return self._run_cmd(["ufw", "reload"])
|
|
42
|
+
|
|
43
|
+
def add_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
44
|
+
return self._run_cmd(["ufw", "allow", f"{port}/{protocol}"])
|
|
45
|
+
|
|
46
|
+
def remove_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
|
|
47
|
+
return self._run_cmd(["ufw", "delete", "allow", f"{port}/{protocol}"])
|
|
48
|
+
|
|
49
|
+
def add_ip_rule(self, ip: str, action: str = 'allow') -> Tuple[bool, str]:
|
|
50
|
+
if action == 'allow':
|
|
51
|
+
return self._run_cmd(["ufw", "allow", "from", ip])
|
|
52
|
+
else:
|
|
53
|
+
return self._run_cmd(["ufw", "deny", "from", ip])
|
|
54
|
+
|
|
55
|
+
def remove_ip_rule(self, ip: str) -> Tuple[bool, str]:
|
|
56
|
+
success1, output1 = self._run_cmd(["ufw", "delete", "allow", "from", ip])
|
|
57
|
+
if not success1:
|
|
58
|
+
success2, output2 = self._run_cmd(["ufw", "delete", "deny", "from", ip])
|
|
59
|
+
return (success2, output2)
|
|
60
|
+
return (success1, output1)
|
|
61
|
+
|
|
62
|
+
def list_rules(self) -> Tuple[bool, str]:
|
|
63
|
+
return self._run_cmd(["ufw", "status"])
|
|
64
|
+
|
|
65
|
+
def list_zones(self) -> Tuple[bool, List[str]]:
|
|
66
|
+
return (False, [])
|
|
67
|
+
|
|
68
|
+
def get_default_zone(self) -> Tuple[bool, str]:
|
|
69
|
+
return (False, "Not supported")
|
|
70
|
+
|
|
71
|
+
def parse_rules(self) -> List[Dict]:
|
|
72
|
+
rules = []
|
|
73
|
+
success, output = self._run_cmd(["ufw", "status", "numbered"])
|
|
74
|
+
if not success:
|
|
75
|
+
return rules
|
|
76
|
+
|
|
77
|
+
lines = output.split('\n')
|
|
78
|
+
for line in lines:
|
|
79
|
+
line = line.strip()
|
|
80
|
+
if not line or line.startswith('Status:') or line.startswith('To') or line.startswith('--'):
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
parts = line.split()
|
|
84
|
+
if len(parts) < 4:
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
num = parts[0].rstrip('[')
|
|
88
|
+
target = parts[1]
|
|
89
|
+
action = parts[2]
|
|
90
|
+
source = parts[3]
|
|
91
|
+
|
|
92
|
+
protocol = 'tcp'
|
|
93
|
+
port = ''
|
|
94
|
+
ip = ''
|
|
95
|
+
|
|
96
|
+
if '/' in target:
|
|
97
|
+
port, proto = target.split('/', 1)
|
|
98
|
+
protocol = proto
|
|
99
|
+
elif target.isdigit():
|
|
100
|
+
port = int(target)
|
|
101
|
+
else:
|
|
102
|
+
rules.append({
|
|
103
|
+
'type': 'service',
|
|
104
|
+
'service': target,
|
|
105
|
+
'port': '',
|
|
106
|
+
'protocol': '',
|
|
107
|
+
'action': action.lower(),
|
|
108
|
+
'ip': '',
|
|
109
|
+
'zone': '',
|
|
110
|
+
'description': f"{action}: {target} from {source}"
|
|
111
|
+
})
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
if source != 'Anywhere':
|
|
115
|
+
ip = source
|
|
116
|
+
|
|
117
|
+
rules.append({
|
|
118
|
+
'type': 'port',
|
|
119
|
+
'service': '',
|
|
120
|
+
'port': int(port) if port.isdigit() else port,
|
|
121
|
+
'protocol': protocol,
|
|
122
|
+
'action': action.lower(),
|
|
123
|
+
'ip': ip,
|
|
124
|
+
'zone': '',
|
|
125
|
+
'description': f"{action}: {port}/{protocol} from {source}"
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return rules
|
inpanel/mod/ftp.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2019-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
|
+
|
|
9
|
+
'''Module for FTP'''
|
|
10
|
+
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from ftplib import FTP
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FTPClient():
|
|
17
|
+
'''FTP Module'''
|
|
18
|
+
|
|
19
|
+
def __init__(self, host, user, pswd):
|
|
20
|
+
self.ftp = None
|
|
21
|
+
self.host = host
|
|
22
|
+
self.user = user
|
|
23
|
+
self.pswd = pswd
|
|
24
|
+
self.init_client()
|
|
25
|
+
|
|
26
|
+
def __delete__(self, instance):
|
|
27
|
+
self.quit()
|
|
28
|
+
|
|
29
|
+
def init_client(self):
|
|
30
|
+
'''init instance'''
|
|
31
|
+
try:
|
|
32
|
+
self.ftp = FTP(self.host)
|
|
33
|
+
self.ftp.login(self.user, self.pswd)
|
|
34
|
+
print('connected: "%s"' % self.host)
|
|
35
|
+
return self.ftp
|
|
36
|
+
except:
|
|
37
|
+
print('connect failed.')
|
|
38
|
+
print('Please check if the host, user and password is correct.')
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
def reconnect(self):
|
|
42
|
+
'''reconnect to ftp'''
|
|
43
|
+
try:
|
|
44
|
+
self.ftp.login(self.user, self.pswd)
|
|
45
|
+
print('reconnected: "%s"' % self.host)
|
|
46
|
+
except:
|
|
47
|
+
print('reconnect failed')
|
|
48
|
+
return False
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
def disconnect(self):
|
|
52
|
+
'''close connection'''
|
|
53
|
+
try:
|
|
54
|
+
self.ftp.close()
|
|
55
|
+
print('disconnect successful')
|
|
56
|
+
except:
|
|
57
|
+
print('disconnect failed')
|
|
58
|
+
return False
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
def quit(self):
|
|
62
|
+
'''quit and close connection'''
|
|
63
|
+
try:
|
|
64
|
+
self.ftp.quit()
|
|
65
|
+
print('quit successful')
|
|
66
|
+
except:
|
|
67
|
+
print('quit failed')
|
|
68
|
+
return False
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
def cd(self, path):
|
|
72
|
+
'''change directory'''
|
|
73
|
+
if not path:
|
|
74
|
+
return False
|
|
75
|
+
try:
|
|
76
|
+
return self.ftp.cwd(path)
|
|
77
|
+
except:
|
|
78
|
+
print('change directory failed: "%s"' % path)
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
def pwd(self):
|
|
82
|
+
'''get current path'''
|
|
83
|
+
try:
|
|
84
|
+
return self.ftp.pwd()
|
|
85
|
+
except:
|
|
86
|
+
print('get path failed')
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def ls(self, path='', onlyname=False):
|
|
90
|
+
'''get list of files and directories'''
|
|
91
|
+
try:
|
|
92
|
+
if onlyname is True:
|
|
93
|
+
return self.ftp.nlst()
|
|
94
|
+
return self.ftp.dir(path)
|
|
95
|
+
except:
|
|
96
|
+
print('get list failed: "%s"' % path)
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
def mkd(self, path):
|
|
100
|
+
'''create a directory, return its full pathname.
|
|
101
|
+
'''
|
|
102
|
+
if not path:
|
|
103
|
+
return False
|
|
104
|
+
try:
|
|
105
|
+
return self.ftp.mkd(path)
|
|
106
|
+
except:
|
|
107
|
+
print('create failed: "%s"' % path)
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
def rename(self, oldname, newname):
|
|
111
|
+
'''rename a file or directory.'''
|
|
112
|
+
try:
|
|
113
|
+
self.ftp.rename(oldname, newname)
|
|
114
|
+
print('rename successful: from "%s" to "%s"' % (oldname, newname))
|
|
115
|
+
except:
|
|
116
|
+
print('rename failed: from "%s" to "%s"' % (oldname, newname))
|
|
117
|
+
return False
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
def rm(self, filename):
|
|
121
|
+
'''delete a file or directory.'''
|
|
122
|
+
try:
|
|
123
|
+
if self.ftp.delete(filename):
|
|
124
|
+
print('delete successful: "%s"' % filename)
|
|
125
|
+
return True
|
|
126
|
+
else:
|
|
127
|
+
print('delete failed: "%s"' % filename)
|
|
128
|
+
return False
|
|
129
|
+
except:
|
|
130
|
+
print('delete error: "%s"' % filename)
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
def rmd(self, filename):
|
|
134
|
+
'''delete a file or directory.'''
|
|
135
|
+
try:
|
|
136
|
+
if self.ftp.delete(filename):
|
|
137
|
+
print('delete successful: "%s"' % filename)
|
|
138
|
+
return True
|
|
139
|
+
else:
|
|
140
|
+
print('delete failed: "%s"' % filename)
|
|
141
|
+
return False
|
|
142
|
+
except:
|
|
143
|
+
print('delete failed: "%s"' % filename)
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
def upload(self, filepath):
|
|
147
|
+
'''upload local file to FTP'''
|
|
148
|
+
try:
|
|
149
|
+
with open(filepath, 'rb') as source:
|
|
150
|
+
target = Path(filepath).name
|
|
151
|
+
self.ftp.storbinary('STOR %s' % target, source)
|
|
152
|
+
except:
|
|
153
|
+
print('upload failed: "%s"' % filepath)
|
|
154
|
+
return False
|
|
155
|
+
print('upload successful: "%s"' % filepath)
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
def download(self, filename, localfile=''):
|
|
159
|
+
'''download FTP file to local'''
|
|
160
|
+
localfile = str(Path(filename if not localfile else localfile))
|
|
161
|
+
try:
|
|
162
|
+
with open(localfile, 'wb') as target:
|
|
163
|
+
self.ftp.retrbinary("RETR %s" % filename, target.write)
|
|
164
|
+
except:
|
|
165
|
+
print('download failed: from "%s" to "%s"' % (filename, localfile))
|
|
166
|
+
return False
|
|
167
|
+
print('download successful: from "%s" to "%s"' % (filename, localfile))
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
def find(self, filename):
|
|
171
|
+
'''find file on FTP'''
|
|
172
|
+
files = self.ftp.nlst()
|
|
173
|
+
if filename in files:
|
|
174
|
+
return True
|
|
175
|
+
else:
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
def uploadtoftp(address, account, password, source, target):
|
|
179
|
+
try:
|
|
180
|
+
ftp = FTPClient(address, account, password)
|
|
181
|
+
# print('1')
|
|
182
|
+
ftp.cd(str(Path(target)))
|
|
183
|
+
# print('2')
|
|
184
|
+
ftp.upload(source)
|
|
185
|
+
# print('3')
|
|
186
|
+
ftp.quit()
|
|
187
|
+
# print('4')
|
|
188
|
+
return True
|
|
189
|
+
except:
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
def uploadtoftpa(*args, **kwds):
|
|
193
|
+
time.sleep(5)
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
def test():
|
|
197
|
+
host = ''
|
|
198
|
+
user = ''
|
|
199
|
+
pswd = ''
|
|
200
|
+
ftp = FTPClient(host, user, pswd)
|
|
201
|
+
# ftp.ls()
|
|
202
|
+
ftp.cd('/Backup')
|
|
203
|
+
# print(ftp.pwd())
|
|
204
|
+
# ftp.ls()
|
|
205
|
+
# ftp.rename('1234567/cc.txt', 'ccaa.txt')
|
|
206
|
+
# ftp.rename('ccaa.txt', '1234567/afasff.txt')
|
|
207
|
+
# ftp.mkd('abc/aaa/ccc') # 创建文件夹
|
|
208
|
+
# ftp.mkd('ccc') # 创建文件夹
|
|
209
|
+
# ftp.upload('./app.icns') # 上传文件
|
|
210
|
+
# ftp.upload('./bbb.txt') # 上传文件
|
|
211
|
+
|
|
212
|
+
# ftp.upload('111.txt') # 上传文件
|
|
213
|
+
# ftp.upload('222.txt') # 上传文件
|
|
214
|
+
# ftp.upload('bbb.txt') # 上传文件
|
|
215
|
+
# ftp.download('111.txt', '111aaa.txt') # 下载文件重命名
|
|
216
|
+
# ftp.download('222.txt', '222aaa.txt') # 下载文件重命名
|
|
217
|
+
# ftp.download('111.txt') # 下载文件覆盖
|
|
218
|
+
# ftp.download('222.txt') # 下载文件覆盖
|
|
219
|
+
# ftp.download('bbb.txt') # 下载文件覆盖
|
|
220
|
+
|
|
221
|
+
# ftp.rm('bbb.txt') # 删除文件
|
|
222
|
+
# ftp.rmd("ccc") # 删除目录
|
|
223
|
+
|
|
224
|
+
print(ftp.find('aaa.txt'))
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
ftp.quit() # 退出
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == '__main__':
|
|
231
|
+
test()
|