mm_os 3.2.9 → 3.3.1
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.
- package/README.md +47 -1
- package/core/base/mqtt/index.js +1109 -1106
- package/core/base/web/index.js +245 -156
- package/core/com/event/README.md +4 -4
- package/core/com/event/com.json +3 -3
- package/core/com/event/config.tpl.json +18 -18
- package/core/com/event/drive.js +132 -132
- package/core/com/event/index.js +344 -344
- package/core/com/event/script.js +25 -25
- package/core/com/middleware/com.js +152 -151
- package/core/com/socket/config.tpl.json +2 -2
- package/core/com/socket/drive.js +2 -2
- package/core/com/socket/index.js +1 -1
- package/core/com/sql/drive.js +7 -7
- package/core/com/static/index.js +1 -1
- package/index.js +34 -5
- package/middleware/cors/index.js +112 -96
- package/middleware/cors/middleware.json +18 -7
- package/middleware/csrf/index.js +202 -0
- package/middleware/csrf/middleware.json +24 -0
- package/middleware/ip_firewall/index.js +476 -0
- package/middleware/ip_firewall/middleware.json +109 -0
- package/middleware/mqtt_base/middleware.json +2 -1
- package/middleware/security_audit/index.js +543 -0
- package/middleware/security_audit/middleware.json +48 -0
- package/middleware/waf/index.js +273 -7
- package/middleware/waf/middleware.json +2 -1
- package/middleware/waf_ddos/index.js +520 -0
- package/middleware/waf_ddos/middleware.json +38 -0
- package/middleware/waf_xss/index.js +269 -0
- package/middleware/waf_xss/middleware.json +18 -0
- package/middleware/web_after/middleware.json +2 -1
- package/middleware/web_base/middleware.json +2 -1
- package/middleware/web_before/middleware.json +3 -2
- package/middleware/web_check/middleware.json +2 -1
- package/middleware/web_main/middleware.json +2 -1
- package/middleware/web_proxy/middleware.json +2 -1
- package/middleware/web_render/middleware.json +2 -1
- package/middleware/web_socket/middleware.json +4 -3
- package/middleware/web_static/middleware.json +2 -1
- package/package.json +28 -15
- package/middleware/log/index.js +0 -32
- package/middleware/log/middleware.json +0 -9
- package/middleware/performance/index.js +0 -143
- package/middleware/performance/middleware.json +0 -16
- package/middleware/rate_limit/index.js +0 -112
- package/middleware/rate_limit/middleware.json +0 -10
- package/middleware/waf_ip/index.js +0 -168
- package/middleware/waf_ip/middleware.json +0 -10
- package/nodemon.json +0 -31
- package/package.txt +0 -1
- package/rps.bat +0 -3
- package/test.js +0 -10
- package/tps.bat +0 -3
- package/update.bat +0 -1
- package//347/263/273/347/273/237/346/236/266/346/236/204/350/257/204/344/274/260/344/270/216/344/274/230/345/214/226/345/273/272/350/256/256.md +0 -599
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IP防火墙中间件
|
|
3
|
+
* 合并了IP过滤和WAF IP防火墙的功能
|
|
4
|
+
* 支持黑白名单、频率限制、自动黑名单等功能
|
|
5
|
+
*/
|
|
6
|
+
class IpFirewallMiddleware {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.default = {
|
|
9
|
+
// 基础功能配置
|
|
10
|
+
enable: true,
|
|
11
|
+
mode: 'blacklist',
|
|
12
|
+
blacklist: [],
|
|
13
|
+
whitelist: [],
|
|
14
|
+
ignore_paths: [],
|
|
15
|
+
log: true,
|
|
16
|
+
block_status_code: 403,
|
|
17
|
+
block_message: 'Forbidden: Access denied',
|
|
18
|
+
enable_cidr: true,
|
|
19
|
+
trust_proxy: false,
|
|
20
|
+
|
|
21
|
+
// 频率限制配置
|
|
22
|
+
request_limit: 1000,
|
|
23
|
+
request_duration: 60000,
|
|
24
|
+
request_block: true,
|
|
25
|
+
blacklist_expire_time: 86400000,
|
|
26
|
+
persist_blacklist: true
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
IpFirewallMiddleware.prototype.init = function(config) {
|
|
32
|
+
this.config = Object.assign({}, this.default, config || {});
|
|
33
|
+
|
|
34
|
+
// 初始化持久化黑名单
|
|
35
|
+
if (this.config.persist_blacklist) {
|
|
36
|
+
this._initBlacklist();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return this;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
IpFirewallMiddleware.prototype.run = async function(ctx, next) {
|
|
43
|
+
const config = this.config;
|
|
44
|
+
|
|
45
|
+
if (!config.enable) {
|
|
46
|
+
return await next();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 检查是否应该忽略该路径
|
|
50
|
+
const path = ctx.path;
|
|
51
|
+
if (config.ignore_paths.some(p => path.startsWith(p))) {
|
|
52
|
+
return await next();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 获取客户端IP
|
|
56
|
+
const client_ip = this._getClientIp(ctx);
|
|
57
|
+
|
|
58
|
+
// 检查基础IP过滤
|
|
59
|
+
if (!(await this._isIpAllowed(client_ip, config))) {
|
|
60
|
+
if (config.log) {
|
|
61
|
+
this._logBlockedAccess(ctx, client_ip, 'basic_filter');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ctx.status = config.block_status_code;
|
|
65
|
+
ctx.body = {
|
|
66
|
+
code: config.block_status_code,
|
|
67
|
+
msg: config.block_message
|
|
68
|
+
};
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 检查频率限制
|
|
73
|
+
if (!(await this._checkRateLimit(client_ip, config))) {
|
|
74
|
+
if (config.log) {
|
|
75
|
+
this._logBlockedAccess(ctx, client_ip, 'rate_limit');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ctx.status = 429;
|
|
79
|
+
ctx.body = '请求频率过高,请稍后再试。';
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await next();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
IpFirewallMiddleware.prototype._getClientIp = function(ctx) {
|
|
87
|
+
let ip = ctx.ip;
|
|
88
|
+
|
|
89
|
+
// 如果信任代理,则从X-Forwarded-For头获取真实IP
|
|
90
|
+
if (this.config.trust_proxy) {
|
|
91
|
+
const forwarded_for = ctx.get('X-Forwarded-For');
|
|
92
|
+
if (forwarded_for) {
|
|
93
|
+
// 取第一个IP(最原始的客户端IP)
|
|
94
|
+
ip = forwarded_for.split(',')[0].trim();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 处理IPv6格式
|
|
99
|
+
if (ip.startsWith('::ffff:')) {
|
|
100
|
+
ip = ip.substring(7);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 移除可能的端口号
|
|
104
|
+
if (ip && ip.includes(':')) {
|
|
105
|
+
ip = ip.split(':')[0];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 处理IPv6本地地址
|
|
109
|
+
if (ip === '::1' || ip === '::ffff:127.0.0.1') {
|
|
110
|
+
ip = '127.0.0.1';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return ip;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
IpFirewallMiddleware.prototype._isIpAllowed = async function(ip, config) {
|
|
117
|
+
// 优先使用全局IP管理器
|
|
118
|
+
try {
|
|
119
|
+
var { is_ip_in_whitelist, is_ip_in_blacklist } = require('../../tools/ip_manager/ip.manager.global.js');
|
|
120
|
+
|
|
121
|
+
if (config.mode === 'whitelist') {
|
|
122
|
+
// 白名单模式:只有在白名单中的IP才允许访问
|
|
123
|
+
if (await is_ip_in_whitelist(ip)) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
// 黑名单模式:只有在黑名单中的IP才被阻止
|
|
128
|
+
if (await is_ip_in_blacklist(ip)) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
// 如果全局IP管理器不可用,使用本地配置
|
|
134
|
+
if ($.log && $.log.warn) {
|
|
135
|
+
$.log.warn('全局IP管理器不可用,使用本地IP过滤配置');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 使用本地配置作为备用
|
|
140
|
+
if (config.mode === 'whitelist') {
|
|
141
|
+
return this._isIpInList(ip, config.whitelist);
|
|
142
|
+
} else {
|
|
143
|
+
return !this._isIpInList(ip, config.blacklist);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
IpFirewallMiddleware.prototype._isIpInList = function(ip, ip_list) {
|
|
148
|
+
if (!ip_list || !Array.isArray(ip_list)) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 检查精确匹配
|
|
153
|
+
if (ip_list.includes(ip)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 检查CIDR格式匹配
|
|
158
|
+
if (this.config.enable_cidr) {
|
|
159
|
+
for (const cidr of ip_list) {
|
|
160
|
+
if (this._isIpInCidr(ip, cidr)) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return false;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
IpFirewallMiddleware.prototype._isIpInCidr = function(ip, cidr) {
|
|
170
|
+
try {
|
|
171
|
+
// 简单的CIDR匹配实现
|
|
172
|
+
if (!cidr.includes('/')) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const [network, prefix_length] = cidr.split('/');
|
|
177
|
+
const prefix = parseInt(prefix_length, 10);
|
|
178
|
+
|
|
179
|
+
// 将IP地址转换为数字
|
|
180
|
+
const ip_num = this._ipToNumber(ip);
|
|
181
|
+
const network_num = this._ipToNumber(network);
|
|
182
|
+
|
|
183
|
+
if (isNaN(ip_num) || isNaN(network_num)) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 计算掩码
|
|
188
|
+
const mask = this._getMask(prefix);
|
|
189
|
+
|
|
190
|
+
// 检查IP是否在CIDR范围内
|
|
191
|
+
return (ip_num & mask) === (network_num & mask);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
IpFirewallMiddleware.prototype._ipToNumber = function(ip) {
|
|
198
|
+
const parts = ip.split('.');
|
|
199
|
+
if (parts.length !== 4) {
|
|
200
|
+
return NaN;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return parts.reduce((num, part) => {
|
|
204
|
+
return (num << 8) + parseInt(part, 10);
|
|
205
|
+
}, 0) >>> 0;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
IpFirewallMiddleware.prototype._getMask = function(prefix) {
|
|
209
|
+
return (0xffffffff << (32 - prefix)) >>> 0;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
IpFirewallMiddleware.prototype._checkRateLimit = async function(ip, config) {
|
|
213
|
+
if (!config.request_limit || !config.request_duration) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
// 检查是否已被黑名单
|
|
219
|
+
const is_blacklisted = await $.cache.get(`blacklist_${ip}`);
|
|
220
|
+
if (is_blacklisted) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
var num = 1;
|
|
225
|
+
var now = new Date();
|
|
226
|
+
var date = now.toStr('yyyy-MM-dd');
|
|
227
|
+
var time;
|
|
228
|
+
var json;
|
|
229
|
+
|
|
230
|
+
var str = await $.cache.get("ip_" + ip);
|
|
231
|
+
if (str) {
|
|
232
|
+
if (typeof (str) === "string") {
|
|
233
|
+
try {
|
|
234
|
+
json = JSON.parse(str);
|
|
235
|
+
} catch (jsonError) {
|
|
236
|
+
if ($.log && $.log.error) {
|
|
237
|
+
$.log.error('IP防火墙JSON解析错误:', jsonError);
|
|
238
|
+
}
|
|
239
|
+
json = null;
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
json = str;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (json) {
|
|
246
|
+
if (json.date !== date) {
|
|
247
|
+
num = 1;
|
|
248
|
+
} else {
|
|
249
|
+
// 判断时间间隔是否在范围外
|
|
250
|
+
if (typeof json.time === 'string') {
|
|
251
|
+
const savedTime = new Date(json.time);
|
|
252
|
+
if (!isNaN(savedTime.getTime()) && (now - savedTime) > config.request_duration) {
|
|
253
|
+
num = 1;
|
|
254
|
+
} else {
|
|
255
|
+
num = json.num + 1;
|
|
256
|
+
if (num > config.request_limit) {
|
|
257
|
+
// 超出上限禁止访问,并加入黑名单
|
|
258
|
+
if (config.request_block) {
|
|
259
|
+
await this._addToBlacklist(ip, config);
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
num = 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!time) {
|
|
272
|
+
time = now.toStr('yyyy-MM-dd hh:mm:ss');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
await $.cache.set("ip_" + ip, JSON.stringify({
|
|
276
|
+
date,
|
|
277
|
+
time,
|
|
278
|
+
num
|
|
279
|
+
}), config.request_duration);
|
|
280
|
+
|
|
281
|
+
return true;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if ($.log && $.log.error) {
|
|
284
|
+
$.log.error('IP防火墙频率检查错误:', error);
|
|
285
|
+
}
|
|
286
|
+
return true; // 出错时默认允许通过
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
IpFirewallMiddleware.prototype._addToBlacklist = async function(ip, config) {
|
|
291
|
+
const { exec } = require('child_process');
|
|
292
|
+
const platform = require('os').platform();
|
|
293
|
+
|
|
294
|
+
// 检查是否已在黑名单中
|
|
295
|
+
const blacklistKey = `blacklist_${ip}`;
|
|
296
|
+
try {
|
|
297
|
+
const is_blacklisted = await $.cache.get(blacklistKey);
|
|
298
|
+
if (is_blacklisted) {
|
|
299
|
+
if ($.log && $.log.info) {
|
|
300
|
+
$.log.info(`IP ${ip} 已在黑名单中,无需重复添加`);
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if ($.log && $.log.error) {
|
|
306
|
+
$.log.error('检查黑名单缓存错误:', error);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
var cmd;
|
|
311
|
+
if (platform == "win32") {
|
|
312
|
+
cmd = `netsh advfirewall firewall add rule name="Blacklist ${ip}" dir=in action=block remoteip="${ip}" protocol=any`;
|
|
313
|
+
} else {
|
|
314
|
+
cmd = `iptables -A INPUT -s ${ip} -j DROP`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
await new Promise((resolve, reject) => {
|
|
319
|
+
exec(cmd, (error, stdout, stderr) => {
|
|
320
|
+
if (error) {
|
|
321
|
+
reject(error);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if ($.log && $.log.info) {
|
|
325
|
+
$.log.info(`IP ${ip} 已加入防火墙黑名单`);
|
|
326
|
+
}
|
|
327
|
+
resolve();
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// 记录到缓存,支持黑名单过期时间
|
|
332
|
+
const expire_time = config.blacklist_expire_time || 86400000;
|
|
333
|
+
await $.cache.set(blacklistKey, true, expire_time);
|
|
334
|
+
|
|
335
|
+
// 记录黑名单IP,便于管理
|
|
336
|
+
const blacklistIPsKey = 'blacklisted_ips';
|
|
337
|
+
let blacklist_ips = [];
|
|
338
|
+
try {
|
|
339
|
+
const existing = await $.cache.get(blacklistIPsKey);
|
|
340
|
+
if (existing) {
|
|
341
|
+
blacklist_ips = typeof existing === 'string' ? JSON.parse(existing) : existing;
|
|
342
|
+
}
|
|
343
|
+
if (!Array.isArray(blacklist_ips)) {
|
|
344
|
+
blacklist_ips = [];
|
|
345
|
+
}
|
|
346
|
+
if (!blacklist_ips.includes(ip)) {
|
|
347
|
+
blacklist_ips.push(ip);
|
|
348
|
+
await $.cache.set(blacklistIPsKey, blacklist_ips, 0); // 永不过期
|
|
349
|
+
}
|
|
350
|
+
} catch (cacheError) {
|
|
351
|
+
if ($.log && $.log.error) {
|
|
352
|
+
$.log.error('保存黑名单IP列表错误:', cacheError);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
} catch (error) {
|
|
356
|
+
if ($.log && $.log.error) {
|
|
357
|
+
$.log.error(`添加IP ${ip} 到黑名单失败:`, error);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
IpFirewallMiddleware.prototype._removeFromBlacklist = async function(ip) {
|
|
363
|
+
const { exec } = require('child_process');
|
|
364
|
+
const platform = require('os').platform();
|
|
365
|
+
|
|
366
|
+
var cmd;
|
|
367
|
+
if (platform == "win32") {
|
|
368
|
+
cmd = `netsh advfirewall firewall delete rule name="Blacklist ${ip}"`;
|
|
369
|
+
} else {
|
|
370
|
+
cmd = `iptables -D INPUT -s ${ip} -j DROP`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
await new Promise((resolve, reject) => {
|
|
375
|
+
exec(cmd, (error, stdout, stderr) => {
|
|
376
|
+
if (error) {
|
|
377
|
+
if (platform !== "win32") {
|
|
378
|
+
reject(error);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if ($.log && $.log.info) {
|
|
383
|
+
$.log.info(`IP ${ip} 已从防火墙黑名单中移除`);
|
|
384
|
+
}
|
|
385
|
+
resolve();
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// 从缓存中移除
|
|
390
|
+
await $.cache.del(`blacklist_${ip}`);
|
|
391
|
+
|
|
392
|
+
// 从黑名单列表中移除
|
|
393
|
+
const blacklistIPsKey = 'blacklisted_ips';
|
|
394
|
+
try {
|
|
395
|
+
const existing = await $.cache.get(blacklistIPsKey);
|
|
396
|
+
if (existing) {
|
|
397
|
+
let blacklist_ips = typeof existing === 'string' ? JSON.parse(existing) : existing;
|
|
398
|
+
if (Array.isArray(blacklist_ips)) {
|
|
399
|
+
blacklist_ips = blacklist_ips.filter(ip_item => ip_item !== ip);
|
|
400
|
+
await $.cache.set(blacklistIPsKey, blacklist_ips, 0);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} catch (cacheError) {
|
|
404
|
+
if ($.log && $.log.error) {
|
|
405
|
+
$.log.error('更新黑名单IP列表错误:', cacheError);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
} catch (error) {
|
|
409
|
+
if ($.log && $.log.error) {
|
|
410
|
+
$.log.error(`移除IP ${ip} 从黑名单失败:`, error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
IpFirewallMiddleware.prototype._initBlacklist = async function() {
|
|
416
|
+
if (!this.config.persist_blacklist) return;
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
const blacklistIPsKey = 'blacklisted_ips';
|
|
420
|
+
const existing = await $.cache.get(blacklistIPsKey);
|
|
421
|
+
if (existing) {
|
|
422
|
+
let blacklist_ips = typeof existing === 'string' ? JSON.parse(existing) : existing;
|
|
423
|
+
if (Array.isArray(blacklist_ips) && blacklist_ips.length > 0) {
|
|
424
|
+
if ($.log && $.log.info) {
|
|
425
|
+
$.log.info(`正在应用 ${blacklist_ips.length} 个持久化的黑名单IP规则...`);
|
|
426
|
+
}
|
|
427
|
+
for (const ip of blacklist_ips) {
|
|
428
|
+
// 重新应用黑名单规则
|
|
429
|
+
await this._addToBlacklist(ip, this.config);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
} catch (error) {
|
|
434
|
+
if ($.log && $.log.error) {
|
|
435
|
+
$.log.error('初始化持久化黑名单失败:', error);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
IpFirewallMiddleware.prototype._logBlockedAccess = function(ctx, ip, reason) {
|
|
441
|
+
if ($.log && $.log.warn) {
|
|
442
|
+
$.log.warn(`IP访问被阻止: IP=${ip}, 路径=${ctx.path}, 方法=${ctx.method}, 原因=${reason}`);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// 创建中间件实例
|
|
447
|
+
const middleware = new IpFirewallMiddleware();
|
|
448
|
+
|
|
449
|
+
// 导出符合系统期望的函数
|
|
450
|
+
exports = module.exports = async function(server, config) {
|
|
451
|
+
// 初始化中间件
|
|
452
|
+
middleware.init(config);
|
|
453
|
+
|
|
454
|
+
// 注册中间件到服务器
|
|
455
|
+
server.use(middleware.run.bind(middleware));
|
|
456
|
+
|
|
457
|
+
// 记录中间件初始化信息
|
|
458
|
+
if ($.log && $.log.info) {
|
|
459
|
+
$.log.info(`IP防火墙中间件已加载: 启用=${middleware.config.enable}, 模式=${middleware.config.mode}, 频率限制=${middleware.config.request_limit}/${middleware.config.request_duration}ms`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// 提供手动管理黑名单的API方法
|
|
463
|
+
if (!$.ip_firewall) {
|
|
464
|
+
$.ip_firewall = {
|
|
465
|
+
addBlacklist: middleware._addToBlacklist.bind(middleware),
|
|
466
|
+
removeBlacklist: middleware._removeFromBlacklist.bind(middleware),
|
|
467
|
+
getClientIP: middleware._getClientIp.bind(middleware),
|
|
468
|
+
isInWhitelist: middleware._isIpInList.bind(middleware)
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return server;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// 保留原始实例,以便其他方式调用
|
|
476
|
+
exports.middleware = middleware;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ip_firewall",
|
|
3
|
+
"title": "IP防火墙中间件",
|
|
4
|
+
"description": "合并了IP过滤和WAF IP防火墙的功能,支持黑白名单、频率限制、自动黑名单等功能",
|
|
5
|
+
"version": "2.0",
|
|
6
|
+
"mode": "web",
|
|
7
|
+
"process_type": "common_before",
|
|
8
|
+
"sort": 10,
|
|
9
|
+
"state": 1,
|
|
10
|
+
"config": {
|
|
11
|
+
"enable": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"title": "启用IP防火墙",
|
|
14
|
+
"description": "是否启用IP防火墙功能",
|
|
15
|
+
"value": true
|
|
16
|
+
},
|
|
17
|
+
"mode": {
|
|
18
|
+
"type": "select",
|
|
19
|
+
"title": "过滤模式",
|
|
20
|
+
"description": "选择IP过滤模式:黑名单模式或白名单模式",
|
|
21
|
+
"options": [
|
|
22
|
+
{"label": "黑名单模式", "value": "blacklist"},
|
|
23
|
+
{"label": "白名单模式", "value": "whitelist"}
|
|
24
|
+
],
|
|
25
|
+
"value": "blacklist"
|
|
26
|
+
},
|
|
27
|
+
"blacklist": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"title": "黑名单IP列表",
|
|
30
|
+
"description": "在黑名单模式下的禁止访问IP列表,支持CIDR格式",
|
|
31
|
+
"value": []
|
|
32
|
+
},
|
|
33
|
+
"whitelist": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"title": "白名单IP列表",
|
|
36
|
+
"description": "在白名单模式下的允许访问IP列表,支持CIDR格式",
|
|
37
|
+
"value": ["127.0.0.1"]
|
|
38
|
+
},
|
|
39
|
+
"ignore_paths": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"title": "忽略路径",
|
|
42
|
+
"description": "在这些路径下不进行IP过滤",
|
|
43
|
+
"value": []
|
|
44
|
+
},
|
|
45
|
+
"log": {
|
|
46
|
+
"type": "boolean",
|
|
47
|
+
"title": "记录日志",
|
|
48
|
+
"description": "是否记录被阻止的访问日志",
|
|
49
|
+
"value": true
|
|
50
|
+
},
|
|
51
|
+
"block_status_code": {
|
|
52
|
+
"type": "number",
|
|
53
|
+
"title": "阻止状态码",
|
|
54
|
+
"description": "IP被阻止时返回的HTTP状态码",
|
|
55
|
+
"value": 403
|
|
56
|
+
},
|
|
57
|
+
"block_message": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"title": "阻止消息",
|
|
60
|
+
"description": "IP被阻止时返回的消息",
|
|
61
|
+
"value": "Forbidden: Access denied"
|
|
62
|
+
},
|
|
63
|
+
"enable_cidr": {
|
|
64
|
+
"type": "boolean",
|
|
65
|
+
"title": "启用CIDR支持",
|
|
66
|
+
"description": "是否支持CIDR格式的IP段匹配",
|
|
67
|
+
"value": true
|
|
68
|
+
},
|
|
69
|
+
"trust_proxy": {
|
|
70
|
+
"type": "boolean",
|
|
71
|
+
"title": "信任代理",
|
|
72
|
+
"description": "是否信任X-Forwarded-For头获取真实IP",
|
|
73
|
+
"value": false
|
|
74
|
+
},
|
|
75
|
+
"request_limit": {
|
|
76
|
+
"type": "number",
|
|
77
|
+
"title": "请求限制数量",
|
|
78
|
+
"description": "在指定时间内允许的最大请求数",
|
|
79
|
+
"value": 1000,
|
|
80
|
+
"placeholder": "请输入数字"
|
|
81
|
+
},
|
|
82
|
+
"request_duration": {
|
|
83
|
+
"type": "number",
|
|
84
|
+
"title": "统计时间窗口(毫秒)",
|
|
85
|
+
"description": "请求计数的时间窗口,单位为毫秒",
|
|
86
|
+
"value": 60000,
|
|
87
|
+
"placeholder": "请输入毫秒数"
|
|
88
|
+
},
|
|
89
|
+
"request_block": {
|
|
90
|
+
"type": "boolean",
|
|
91
|
+
"title": "启用黑名单封禁",
|
|
92
|
+
"description": "是否将超限IP添加到系统防火墙黑名单",
|
|
93
|
+
"value": true
|
|
94
|
+
},
|
|
95
|
+
"blacklist_expire_time": {
|
|
96
|
+
"type": "number",
|
|
97
|
+
"title": "黑名单过期时间(毫秒)",
|
|
98
|
+
"description": "IP被添加到黑名单后自动失效的时间,单位为毫秒",
|
|
99
|
+
"value": 86400000,
|
|
100
|
+
"placeholder": "请输入毫秒数"
|
|
101
|
+
},
|
|
102
|
+
"persist_blacklist": {
|
|
103
|
+
"type": "boolean",
|
|
104
|
+
"title": "持久化黑名单",
|
|
105
|
+
"description": "系统重启后是否自动重新应用已保存的黑名单规则",
|
|
106
|
+
"value": true
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|