block-proxy 0.1.2 → 0.1.5

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 CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- <img width="300" alt="image" src="https://github.com/user-attachments/assets/2bb069d8-508a-41b9-9fee-94a1e31cc0cb" />
2
+ <img width="287" alt="image" src="https://github.com/user-attachments/assets/2bb069d8-508a-41b9-9fee-94a1e31cc0cb" /> ![](https://nodei.co/npm/block-proxy.png?downloads=true&downloadRank=true&stars=true)
3
3
 
4
4
  ---------------------------
5
5
 
@@ -18,27 +18,42 @@
18
18
 
19
19
  ### 1)使用方法
20
20
 
21
- #### ① 快速部署
21
+ #### ① 方式一:快速部署
22
22
 
23
23
  安装:
24
24
 
25
- `npm install -g block-proxy`
25
+ ```
26
+ npm install -g block-proxy
27
+ ```
26
28
 
27
29
  启动:
28
30
 
29
- `block-proxy`
31
+ ```
32
+ block-proxy
33
+ ```
34
+
35
+ 或者带配置文件启动
36
+
37
+ ```
38
+ block-proxy -c rule.js
39
+ ```
40
+
41
+ 配置文件参照 [rule.js](example/rule.js),可留空
30
42
 
31
- #### ② Docker 部署(推荐)
43
+ #### ② 方式二,Docker 部署(推荐)
32
44
 
33
45
  1. 下载 Docker 文件
34
46
  - Arm 架构 → <a href="http://yui.cool:7001/public/downloads/block-proxy/arm/block-proxy.tar" target=_blank>block-proxy-arm.tar</a>
35
47
  - X86 架构 → <a href="http://yui.cool:7001/public/downloads/block-proxy/x86/block-proxy-x86.tar" target=_blank>block-proxy-x86.tar</a>
36
48
  2. 导入:`docker load < block-proxy.tar`
37
49
  3. 启动:参照下文 Docker 部署
38
- 4. 服务端配置:配置面板 <http://server-ip:8004>,关闭、启用配置面板:<http://server-ip:8001>
39
- 5. 客户端配置:http 代理直接在 iphone wifi 详情里手动配置,socks5 代理只支持 socks5 over TLS,用小火箭配置。配置信息参照[配置面板](http://localhost:8004)
40
50
 
41
- ### 2)开发和调试
51
+ ### 2)端口配置
52
+
53
+ 1. 服务端配置:配置面板 <http://server-ip:8004>,关闭、启用配置面板:<http://server-ip:8001>
54
+ 2. 客户端配置:http 代理直接在 iphone wifi 详情里手动配置,socks5 代理只支持 socks5 over TLS,用小火箭配置。配置信息参照[配置面板](http://localhost:8004)
55
+
56
+ ### 3)开发和调试
42
57
 
43
58
  代码 clone 下来后执行`pnpm i`,执行 `npm run dev` 运行本地服务。默认开启 5 个端口:
44
59
 
@@ -51,7 +66,7 @@
51
66
  |8004 |后台配置页端口 | 可禁用 |
52
67
 
53
68
 
54
- ### 3)Docker 构建和部署
69
+ ### 4)Docker 构建说明
55
70
 
56
71
  准备工作,构建 docker 包,先启动本地 Docker:
57
72
 
@@ -77,9 +92,12 @@ docker run --init -d --restart=unless-stopped \
77
92
  --log-opt max-file=3 \
78
93
  --cpus="5" \
79
94
  --memory 400m \
95
+ -v "$(pwd)/":/app/config \
80
96
  --name block-proxy block-proxy
81
97
  ```
82
98
 
99
+ 其中挂载目录 `$(pws)/` 下的 `rule.js` 是需要额外挂载的配置文件,可留空。
100
+
83
101
  > block-proxy 可以配置只启动 proxy 不启动后台面板,首次启动后访问 http://代理IP:8001 根据提示操作。
84
102
 
85
103
  网关里为了方便获取子网机器 ip 和 mac 地址,docker 容器需要和宿主机共享同一个网络,同时指定时区。
@@ -88,14 +106,15 @@ docker run --init -d --restart=unless-stopped \
88
106
 
89
107
  ```
90
108
  docker run --init -d --restart=unless-stopped --user=root \
109
+ -v "$(pwd)/":/app/config \
91
110
  -e TZ=Asia/Shanghai -p 8001:8001 -p 8002:8002 -p 8003:8003 \
92
111
  --name block-proxy block-proxy
93
112
  ```
94
113
 
95
114
 
96
- ### 4)配置说明
115
+ ### 5)配置说明
97
116
 
98
- #### 代理端口
117
+ #### 代理端口
99
118
 
100
119
  默认开启两个代理端口:HTTP 8001 和 Socks5(over TLS) 8002。
101
120
 
@@ -103,7 +122,7 @@ docker run --init -d --restart=unless-stopped --user=root \
103
122
 
104
123
  ⚠️ 使用小火箭的 Socks5 over TLS 代理,TLS 选项里勾选“允许不安全”
105
124
 
106
- #### 后台配置
125
+ #### 后台配置
107
126
 
108
127
  访问路径:`http://proxy-ip:8004`
109
128
 
@@ -112,16 +131,16 @@ docker run --init -d --restart=unless-stopped --user=root \
112
131
  <img src="https://github.com/user-attachments/assets/16f47d3f-1ef9-47a2-8640-c7e04ec64e1a" width=300 />
113
132
 
114
133
 
115
- #### 设备配置
134
+ #### 设备配置
116
135
 
117
136
  1. 代理设置:iPhone/iPad 为例:设置 → 无线局域网 → 点击当前网络 → HTTP代理/配置代理,设置服务器和端口。
118
- 2. 证书设置:打开anproxy监控地址(8003端口),扫码安装证书,在手机设置中安装该证书,同时配置完全信任:设置→通用→关于本机→证书信任设置→打开对AnyProxy的完全信任
137
+ 2. 证书设置:打开 anproxy 监控地址(8003端口),扫码安装证书,在手机设置中安装该证书,同时配置完全信任:设置→通用→关于本机→证书信任设置→打开对AnyProxy的完全信任
119
138
 
120
139
  小朋友的设备里把 Mac 固定下来:
121
140
 
122
141
  <img width="350" alt="image" src="https://github.com/user-attachments/assets/f9bfab89-7194-4a72-b1ae-5cca27911bc9" />
123
142
 
124
- #### 禁掉设备直连
143
+ #### 禁掉设备直连
125
144
 
126
145
  防止小朋友修改网Wifi连接,只允许设备通过代理访问,把直连上网权限关掉。网关里配置防火墙规则:
127
146
 
@@ -133,7 +152,7 @@ ip6tables -I forwarding_rule -m mac --mac-source D2:9E:8D:1B:F1:4E -j REJECT
133
152
  然后重启防火墙
134
153
 
135
154
 
136
- ### 5)使用说明
155
+ ### 6)更多信息
137
156
 
138
157
  #### 应用条件:
139
158
 
package/bin/start.js CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  const { spawn } = require('child_process');
4
4
  const path = require('path');
5
+ const { Command } = require('commander');
6
+ const program = new Command();
7
+ const _fs = require('../proxy/fs.js');
5
8
 
6
9
  const pkgDir = path.join(__dirname, '..');
7
10
  const startScript = path.resolve(pkgDir, 'server/start.js');
@@ -28,13 +31,15 @@ function startApp() {
28
31
  process.stderr.write(data);
29
32
  });
30
33
 
31
- currentChild.on('close', (code, signal) => {
34
+ currentChild.on('close', async (code, signal) => {
32
35
  currentChild = null; // 清空引用
33
36
  if (restartTimer) {
34
37
  clearTimeout(restartTimer);
35
38
  restartTimer = null;
36
39
  }
37
40
 
41
+ await _fs.clearGlobalConfigFile();
42
+
38
43
  if (code === 0) {
39
44
  console.error('[block proxy] 正常退出,不重启。');
40
45
  process.exit(0);
@@ -62,8 +67,10 @@ function startApp() {
62
67
  }
63
68
 
64
69
  // ✅ 只注册一次 SIGINT 监听器(在 startApp 外部!)
65
- process.on('SIGINT', () => {
70
+ process.on('SIGINT', async () => {
66
71
  console.error('\n[block proxy] 收到 SIGINT,正在关闭子进程...');
72
+
73
+ await _fs.clearGlobalConfigFile();
67
74
 
68
75
  if (restartTimer) {
69
76
  clearTimeout(restartTimer);
@@ -92,5 +99,27 @@ process.on('SIGTERM', () => {
92
99
  }
93
100
  });
94
101
 
95
- // 启动
96
- startApp();
102
+ (async function() {
103
+ program
104
+ .name('block-proxy')
105
+ .description('极简的 MITM 代理工具:https://github.com/jayli/block-proxy')
106
+ .version('0.1.3')
107
+ .option('-c, --config <config>', 'MITM 配置文件');
108
+
109
+ program.parse(process.argv);
110
+ const options = program.opts();
111
+
112
+ if (options.config && options.config != "") {
113
+ if (path.isAbsolute(options.config)) {
114
+ await _fs.setGlobalConfigFile(options.config);
115
+ } else {
116
+ var pwd = process.cwd();
117
+ var configFile = path.resolve(pwd, options.config);
118
+ await _fs.setGlobalConfigFile(configFile);
119
+ }
120
+ }
121
+
122
+ // 启动
123
+ startApp();
124
+ })();
125
+
package/config.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "network_scanning_status": "1",
2
+ "network_scanning_status": "0",
3
3
  "progress_time_stamp": "1766571511705",
4
4
  "block_hosts": [
5
5
  {
@@ -245,6 +245,14 @@
245
245
  {
246
246
  "ip": "192.168.124.128",
247
247
  "mac": "48:F3:F3:CA:1D:E"
248
+ },
249
+ {
250
+ "ip": "192.168.124.66",
251
+ "mac": "E2:4D:6E:1:21:6E"
252
+ },
253
+ {
254
+ "ip": "192.168.124.125",
255
+ "mac": "14:C0:50:14:6E:A5"
248
256
  }
249
257
  ]
250
258
  }
@@ -0,0 +1,39 @@
1
+ module.exports = {
2
+ AAA: [
3
+ {
4
+ 'type': 'beforeSendResponse',
5
+ 'host': '163.com',
6
+ 'regexp': "/123/v1/(browse|next)",
7
+ 'callback': async function(url, request, response) {
8
+ return {
9
+ response
10
+ }
11
+ } // -- callback
12
+ },
13
+ {
14
+ 'type': 'beforeSendResponse',
15
+ 'host': '163.com',
16
+ 'regexp': "/456",
17
+ 'callback': async function(url, request, response) {
18
+ return {
19
+ response
20
+ }
21
+ } // -- callback
22
+ }
23
+ ],
24
+ BBB: [
25
+ {
26
+ type: "beforeSendRequest",
27
+ host: "163.com",
28
+ regexp: "/hello",
29
+ callback: async function(url, request, response) {
30
+ return {
31
+ response : {
32
+ statusCode: 200,
33
+ body: 'hello world'
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ]
39
+ };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "block-proxy",
3
- "version": "0.1.2",
4
- "description": "Small-scale network proxy filter",
5
- "bin":{
6
- "block-proxy": "./bin/start.js"
3
+ "version": "0.1.5",
4
+ "description": "Small-scale network mitm proxy filter",
5
+ "bin": {
6
+ "block-proxy": "bin/start.js"
7
7
  },
8
8
  "dependencies": {
9
9
  "react": "^19.2.0",
@@ -16,7 +16,7 @@
16
16
  "url": "git+https://github.com/jayli/block-proxy.git"
17
17
  },
18
18
  "scripts": {
19
- "cp": "cp ./hack-of-anyproxy/lib/requestHandler.js ./node_modules/anyproxy/lib/",
19
+ "cp": "echo '---- program start ----'",
20
20
  "craco": "craco start",
21
21
  "dev": "BLOCK_PROXY_DEV=1 npm run express",
22
22
  "start": "npm run express",
@@ -31,13 +31,13 @@
31
31
  },
32
32
  "devDependencies": {
33
33
  "@craco/craco": "^7.1.0",
34
- "anyproxy": "^4.1.3",
34
+ "@bachi/anyproxy": "^0.1.1",
35
35
  "axios": "^1.13.2",
36
+ "commander": "^14.0.2",
36
37
  "express": "^5.1.0",
37
38
  "http-proxy-agent": "^7.0.2",
38
39
  "https-proxy-agent": "^7.0.6",
39
- "ping": "^1.0.0",
40
- "write-file-atomic": "^7.0.0"
40
+ "ping": "^1.0.0"
41
41
  },
42
42
  "browserslist": {
43
43
  "production": [
package/proxy/fs.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // proxy/fs.js
2
2
  const fs = require('fs').promises;
3
3
  const path = require('path');
4
- const writeFileAtomic = require('write-file-atomic'); // 引入 write-file-atomic
5
4
 
6
5
  const configPath = path.join(__dirname, '../config.json');
7
6
  const CONFIG_FILE_PATH = configPath;
@@ -9,9 +8,6 @@ const CONFIG_FILE_PATH = configPath;
9
8
  // 传入的是对象
10
9
  async function writeConfig(newData) {
11
10
  try {
12
- // 使用 write-file-atomic 进行原子写入
13
- // 它会在内部创建一个临时文件,写入成功后再重命名为目标文件
14
- // await writeFileAtomic(CONFIG_FILE_PATH, JSON.stringify(newData, null, 2), 'utf8');
15
11
  await fs.writeFile(CONFIG_FILE_PATH, JSON.stringify(newData, null, 2), 'utf8');
16
12
  // console.log('Config file written successfully');
17
13
  } catch (error) {
@@ -41,7 +37,33 @@ async function readConfig() {
41
37
  }
42
38
  }
43
39
 
40
+ async function setGlobalConfigFile(configFile) {
41
+ var data = await readConfig();
42
+ data.config_file = configFile;
43
+ await writeConfig(data);
44
+ }
45
+
46
+ async function getGlobalConfigFile() {
47
+ var data = await readConfig();
48
+ if (data.hasOwnProperty("config_file")) {
49
+ return data.config_file;
50
+ } else {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ async function clearGlobalConfigFile() {
56
+ var data = await readConfig();
57
+ if (data.hasOwnProperty("config_file")) {
58
+ delete data.config_file
59
+ }
60
+ await writeConfig(data);
61
+ }
62
+
44
63
  module.exports = {
45
64
  writeConfig,
46
- readConfig
65
+ readConfig,
66
+ setGlobalConfigFile,
67
+ getGlobalConfigFile,
68
+ clearGlobalConfigFile
47
69
  };
package/proxy/proxy.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // 文件名: proxy/proxy.js
2
- const AnyProxy = require('anyproxy');
2
+ const AnyProxy = require('@bachi/anyproxy');
3
3
  const { exec } = require('child_process');
4
4
  const fs = require('fs');
5
5
  const _fs = require('./fs.js');
@@ -7,6 +7,7 @@ const path = require('path');
7
7
  const { start } = require('repl');
8
8
  const net = require('net');
9
9
  const scanNetwork = require("./scan").scanNetwork;
10
+ const setScanStatus = require("./scan").setScanStatus;
10
11
  const util = require('util');
11
12
  const zlib = require('zlib');
12
13
  const _util = require('../server/util.js');
@@ -18,12 +19,12 @@ const axios = require('axios');
18
19
  const { HttpProxyAgent } = require('http-proxy-agent');
19
20
  const { HttpsProxyAgent } = require('https-proxy-agent');
20
21
  const _request = require("./http.js").request;
21
- const Rule = require("./mitm/rule.js");
22
22
  const uaFilter = require("./mitm/uaFilter.js");
23
23
  const attacker = require('./attacker.js');
24
24
  const monitor = require('./monitor.js');
25
25
  const domain = require('./domain.js');
26
26
  const wanip = require('./wanip.js');
27
+ var Rule = require("./mitm/rule.js");
27
28
 
28
29
  // 启用全局 keep-alive,使 AnyProxy 内部转发也复用连接
29
30
  http.globalAgent.keepAlive = true;
@@ -80,6 +81,45 @@ function preCompileRuleRegexp() {
80
81
  });
81
82
  }
82
83
 
84
+ async function fileExists(filePath) {
85
+ try {
86
+ await fs.promises.access(filePath);
87
+ return true;
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+
93
+ // 引入从命令行传进来的 rule.js,并加载
94
+ async function loadGlobalConfigFile() {
95
+ var configFile = await _fs.getGlobalConfigFile();
96
+ await _fs.clearGlobalConfigFile();
97
+ if (configFile == null) {
98
+ return;
99
+ } else {
100
+ var extraRule = require(configFile);
101
+ Rule = {
102
+ ...Rule,
103
+ ...extraRule
104
+ }
105
+ }
106
+ }
107
+
108
+ // 引入 Docker 挂载目录下的 rule.js,并加载
109
+ async function loadDockerMountedConfigFile() {
110
+ var rulePath = path.join(__dirname, '../config/rule.js');
111
+ var fileOK = await fileExists(rulePath);
112
+ if (fileOK) {
113
+ var extraRule = require(rulePath);
114
+ Rule = {
115
+ ...Rule,
116
+ ...extraRule
117
+ }
118
+ } else {
119
+ return;
120
+ }
121
+ }
122
+
83
123
  function isEmpty(obj) {
84
124
  if (obj === null || obj === undefined) {
85
125
  return true;
@@ -407,8 +447,6 @@ function startProxyServer() {
407
447
  if (enable_webinterface == "1") {
408
448
  console.log(`✅ \x1b[32mAnyProxy 监控面板启动,端口 ${webInterfacePort}\x1b[0m`);
409
449
  }
410
- console.log('Intercepting requests to hosts:', blockHosts.join(', '));
411
- console.log('All other requests will be passed through without HTTPS interception');
412
450
  });
413
451
 
414
452
  proxyServerInstance.on('error', (e) => {
@@ -1364,6 +1402,7 @@ var LocalProxy = {
1364
1402
  newRouterMap = await scanNetwork();
1365
1403
  } catch (e) {
1366
1404
  newRouterMap = [];
1405
+ setScanStatus("0");
1367
1406
  }
1368
1407
 
1369
1408
  var mergedRouterMap = [];
@@ -1450,7 +1489,17 @@ var LocalProxy = {
1450
1489
  return;
1451
1490
  }
1452
1491
 
1453
- console.log('启动代理服务 LocalProxy.init() ');
1492
+ // 加载命令行里携带的配置文件
1493
+ await loadGlobalConfigFile();
1494
+ // 加载 Docker 挂载目录中的配置文件
1495
+ await loadDockerMountedConfigFile();
1496
+ // 预编译 MITM Rule 的正则
1497
+ preCompileRuleRegexp();
1498
+
1499
+ // 启动时重置 Scan 本地扫描
1500
+ setScanStatus("0");
1501
+
1502
+ console.log('启动代理服务');
1454
1503
  console.log('Dev server started, starting LocalProxy...');
1455
1504
  is_running_in_docker = _util.isRunningInDocker();
1456
1505
  if (is_running_in_docker) {
@@ -1486,8 +1535,9 @@ var LocalProxy = {
1486
1535
  };
1487
1536
 
1488
1537
  // 预编译 MITM Rule 的正则
1489
- (function() {
1490
- preCompileRuleRegexp();
1538
+ (async function() {
1539
+ // await loadGlobalConfigFile();
1540
+ // preCompileRuleRegexp();
1491
1541
  })();
1492
1542
 
1493
1543
  module.exports = LocalProxy;
package/proxy/scan.js CHANGED
@@ -106,7 +106,7 @@ var tempDevices = [];
106
106
  async function scanNetwork() {
107
107
  var status = await getScanStatus();
108
108
  if (status == "1") {
109
- await setScanStatus("0");
109
+ // await setScanStatus("0");
110
110
  return tempDevices;
111
111
  } else {
112
112
  await setScanStatus("1");
@@ -117,4 +117,5 @@ async function scanNetwork() {
117
117
  }
118
118
  }
119
119
 
120
+ module.exports.setScanStatus = setScanStatus;
120
121
  module.exports.scanNetwork = scanNetwork;
package/AD_BLOCK.md DELETED
@@ -1,38 +0,0 @@
1
- [Rule]
2
- AND,((DOMAIN-SUFFIX,googlevideo.com), (PROTOCOL,UDP)),REJECT
3
- AND,((DOMAIN,youtubei.googleapis.com), (PROTOCOL,UDP)),REJECT
4
-
5
- [URL Rewrite]
6
- (^https?:\/\/[\w-]+\.googlevideo\.com\/(?!dclk_video_ads).+?)&ctier=L(&.+?),ctier,(.+) $1$2$3 302
7
- ^https?:\/\/[\w-]+\.googlevideo\.com\/(?!(dclk_video_ads|videoplayback\?)).+&oad _ reject-200
8
- ^https?:\/\/(www|s)\.youtube\.com\/api\/stats\/ads _ reject-200
9
- ^https?:\/\/(www|s)\.youtube\.com\/(pagead|ptracking) _ reject-200
10
- ^https?:\/\/s\.youtube\.com\/api\/stats\/qoe\?adcontext _ reject-200
11
-
12
- [Script]
13
- youtube.response = type=http-response,pattern=^https:\/\/youtubei\.googleapis\.com\/youtubei\/v1\/(browse|next|player|search|reel\/reel_watch_sequence|guide|account\/get_setting|get_watch),requires-body=1,max-size=-1,binary-body-mode=1,engine={{{脚本执行引擎}}},script-path=https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/youtube.response.js,argument="{"lyricLang":"{{{歌词翻译语言}}}","captionLang":"{{{字幕翻译语言}}}","blockUpload":{{{屏蔽上传按钮}}},"blockImmersive":{{{屏蔽选段按钮}}},"debug":{{{启用调试模式}}}}"
14
-
15
- [MITM]
16
- hostname = %APPEND% -redirector*.googlevideo.com,*.googlevideo.com,www.youtube.com,s.youtube.com,youtubei.googleapis.com
17
-
18
-
19
-
20
- --------------------
21
-
22
- youtube.com
23
- `^https?:\/\/(www|s)\.youtube\.com\/api\/stats\/ads`
24
-
25
- youtube.com
26
- `^https?:\/\/(www|s)\.youtube\.com\/(pagead|ptracking)`
27
-
28
- youtube.com
29
- `^https?:\/\/s\.youtube\.com\/api\/stats\/qoe\?adcontext`
30
-
31
- googlevideo.com
32
- `^https?:\/\/[\w-]+\.googlevideo\.com\/(?!(dclk_video_ads|videoplayback\?)).+(&oad|ctier)`
33
-
34
- s.youtube.com
35
- `^https?:\/\/s\.youtube\.com\/api\/stats\/atr.+&is_ad=1`
36
-
37
- youtube.com
38
- `^https?:\/\/(www|s)\.youtube\.com\/pcs\/activeview.+&ad_cpn=`