Skip to content

Commit 0241b90

Browse files
committed
add ca helper when run anyproxy -i
check and help to generate the root CA when run in https interception mode, and install the CA after user's confirmation (Mac only for now)
1 parent 71477d5 commit 0241b90

File tree

7 files changed

+201
-98
lines changed

7 files changed

+201
-98
lines changed

bin/anyproxy

Lines changed: 15 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
const program = require('commander'),
66
color = require('colorful'),
7+
co = require('co'),
78
packageInfo = require('../package.json'),
8-
ruleLoader = require('../lib/ruleLoader'),
99
util = require('../lib/util'),
10+
rootCACheck = require('./rootCaCheck'),
11+
startServer = require('./startServer'),
1012
logUtil = require('../lib/log');
1113

1214
program
@@ -33,85 +35,20 @@ if (program.clear) {
3335
process.exit(0);
3436
});
3537
} else {
36-
const AnyProxy = require('../proxy.js');
37-
let proxyServer;
38-
39-
if (program.silent) {
40-
logUtil.setPrintStatus(false);
41-
}
42-
43-
// load rule module
44-
new Promise((resolve, reject) => {
45-
if (program.rule) {
46-
resolve(ruleLoader.requireModule(program.rule));
47-
} else {
48-
resolve(null);
49-
}
50-
})
51-
.catch(e => {
52-
logUtil.printLog('Failed to load rule file', logUtil.T_ERR);
53-
logUtil.printLog(e, logUtil.T_ERR);
54-
process.exit();
55-
})
56-
57-
//start proxy
58-
.then(ruleModule => {
59-
proxyServer = new AnyProxy.ProxyServer({
60-
type: 'http',
61-
port: program.port || 8001,
62-
throttle: program.throttle,
63-
rule: ruleModule,
64-
webInterface: {
65-
enable: true,
66-
webPort: program.web,
67-
},
68-
wsIntercept: program.wsIntercept,
69-
forceProxyHttps: program.intercept,
70-
dangerouslyIgnoreUnauthorized: !!program.ignoreUnauthorizedSsl,
71-
silent: program.silent
72-
});
73-
// proxyServer.on('ready', () => {});
74-
proxyServer.start();
75-
})
76-
.catch(e => {
77-
logUtil.printLog(e, logUtil.T_ERR);
78-
if (e && e.code) {
79-
logUtil.printLog('code ' + e.code, logUtil.T_ERR);
80-
}
81-
logUtil.printLog(e.stack, logUtil.T_ERR);
82-
});
83-
84-
process.on('exit', (code) => {
85-
if (code > 0) {
86-
logUtil.printLog('AnyProxy is about to exit with code: ' + code, logUtil.T_ERR);
38+
co(function *() {
39+
if (program.silent) {
40+
logUtil.setPrintStatus(false);
8741
}
8842

89-
process.exit();
90-
});
91-
92-
//exit cause ctrl+c
93-
process.on('SIGINT', () => {
94-
try {
95-
proxyServer && proxyServer.close();
96-
} catch (e) {
97-
console.error(e);
43+
if (program.intercept) {
44+
try {
45+
yield rootCACheck();
46+
} catch (e) {
47+
console.error(e);
48+
}
9849
}
99-
process.exit();
100-
});
10150

102-
process.on('uncaughtException', (err) => {
103-
let errorTipText = 'got an uncaught exception, is there anything goes wrong in your rule file ?\n';
104-
try {
105-
if (err && err.stack) {
106-
errorTipText += err.stack;
107-
} else {
108-
errorTipText += err;
109-
}
110-
} catch (e) {}
111-
logUtil.printLog(errorTipText, logUtil.T_ERR);
112-
try {
113-
proxyServer && proxyServer.close();
114-
} catch (e) {}
115-
process.exit();
116-
});
51+
return startServer(program);
52+
})
11753
}
54+

bin/rootCACheck.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* check if root CA exists and installed
3+
* will prompt to generate when needed
4+
*/
5+
6+
const thunkify = require('thunkify');
7+
const AnyProxy = require('../proxy');
8+
const logUtil = require('../lib/log');
9+
10+
const certMgr = AnyProxy.utils.certMgr;
11+
12+
function checkRootCAExists() {
13+
return certMgr.isRootCAFileExists();
14+
}
15+
16+
module.exports = function *() {
17+
try {
18+
if (!checkRootCAExists()) {
19+
logUtil.warn('Missing root CA, generating now');
20+
yield thunkify(certMgr.generateRootCA)();
21+
yield certMgr.trustRootCA();
22+
} else {
23+
const isCATrusted = yield thunkify(certMgr.ifRootCATrusted)();
24+
if (!isCATrusted) {
25+
logUtil.warn('ROOT CA NOT INSTALLED YET');
26+
yield certMgr.trustRootCA();
27+
}
28+
}
29+
} catch (e) {
30+
console.error(e);
31+
}
32+
};
33+

bin/startServer.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* start the AnyProxy server
3+
*/
4+
5+
const ruleLoader = require('../lib/ruleLoader');
6+
const logUtil = require('../lib/log');
7+
const AnyProxy = require('../proxy');
8+
9+
module.exports = function startServer(program) {
10+
let proxyServer;
11+
// load rule module
12+
new Promise((resolve, reject) => {
13+
if (program.rule) {
14+
resolve(ruleLoader.requireModule(program.rule));
15+
} else {
16+
resolve(null);
17+
}
18+
})
19+
.catch(e => {
20+
logUtil.printLog('Failed to load rule file', logUtil.T_ERR);
21+
logUtil.printLog(e, logUtil.T_ERR);
22+
process.exit();
23+
})
24+
25+
//start proxy
26+
.then(ruleModule => {
27+
proxyServer = new AnyProxy.ProxyServer({
28+
type: 'http',
29+
port: program.port || 8001,
30+
throttle: program.throttle,
31+
rule: ruleModule,
32+
webInterface: {
33+
enable: true,
34+
webPort: program.web,
35+
},
36+
wsIntercept: program.wsIntercept,
37+
forceProxyHttps: program.intercept,
38+
dangerouslyIgnoreUnauthorized: !!program.ignoreUnauthorizedSsl,
39+
silent: program.silent
40+
});
41+
// proxyServer.on('ready', () => {});
42+
proxyServer.start();
43+
})
44+
.catch(e => {
45+
logUtil.printLog(e, logUtil.T_ERR);
46+
if (e && e.code) {
47+
logUtil.printLog('code ' + e.code, logUtil.T_ERR);
48+
}
49+
logUtil.printLog(e.stack, logUtil.T_ERR);
50+
});
51+
52+
53+
process.on('exit', (code) => {
54+
if (code > 0) {
55+
logUtil.printLog('AnyProxy is about to exit with code: ' + code, logUtil.T_ERR);
56+
}
57+
58+
process.exit();
59+
});
60+
61+
//exit cause ctrl+c
62+
process.on('SIGINT', () => {
63+
try {
64+
proxyServer && proxyServer.close();
65+
} catch (e) {
66+
console.error(e);
67+
}
68+
process.exit();
69+
});
70+
71+
process.on('uncaughtException', (err) => {
72+
let errorTipText = 'got an uncaught exception, is there anything goes wrong in your rule file ?\n';
73+
try {
74+
if (err && err.stack) {
75+
errorTipText += err.stack;
76+
} else {
77+
errorTipText += err;
78+
}
79+
} catch (e) { }
80+
logUtil.printLog(errorTipText, logUtil.T_ERR);
81+
try {
82+
proxyServer && proxyServer.close();
83+
} catch (e) { }
84+
process.exit();
85+
});
86+
}

lib/certMgr.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
'use strict'
22

3-
const util = require('./util');
43
const EasyCert = require('node-easy-cert');
54
const co = require('co');
5+
const os = require('os');
6+
const inquirer = require('inquirer');
7+
8+
const util = require('./util');
9+
const logUtil = require('./log');
610

711
const options = {
812
rootDirPath: util.getAnyProxyPath('certificates'),
13+
inMemory: false,
914
defaultCertAttrs: [
1015
{ name: 'countryName', value: 'CN' },
1116
{ name: 'organizationName', value: 'AnyProxy' },
@@ -54,4 +59,43 @@ crtMgr.getCAStatus = function *() {
5459
});
5560
}
5661

62+
/**
63+
* trust the root ca by command
64+
*/
65+
crtMgr.trustRootCA = function *() {
66+
const platform = os.platform();
67+
const rootCAPath = crtMgr.getRootCAFilePath();
68+
const trustInquiry = [
69+
{
70+
type: 'list',
71+
name: 'trustCA',
72+
message: 'The rootCA is not trusted yet, install it to the trust store now?',
73+
choices: ['Yes', "No, I'll do it myself"]
74+
}
75+
];
76+
77+
if (platform === 'darwin') {
78+
const answer = yield inquirer.prompt(trustInquiry);
79+
if (answer.trustCA === 'Yes') {
80+
logUtil.info('About to trust the root CA, this may requires your password');
81+
// https://ss64.com/osx/security-cert.html
82+
const result = util.execScriptSync(`sudo security add-trusted-cert -d -k /Library/Keychains/System.keychain ${rootCAPath}`);
83+
if (result.status === 0) {
84+
logUtil.info('Root CA install, you are ready to intercept the https now');
85+
} else {
86+
console.error(result);
87+
logUtil.info('Failed to trust the root CA, please trust it manually');
88+
}
89+
} else {
90+
logUtil.info('Please trust the root CA manually so https interception works');
91+
}
92+
}
93+
94+
95+
if (/^win/.test(process.platform)) {
96+
logUtil.info('You can install the root CA manually.');
97+
}
98+
logUtil.info('The root CA file path is: ' + crtMgr.getRootCAFilePath());
99+
}
100+
57101
module.exports = crtMgr;

lib/log.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function printLog(content, type) {
5858
return;
5959
}
6060

61-
console.error(color.magenta(`[AnyProxy WARN][${timeString}]: ` + content));
61+
console.error(color.yellow(`[AnyProxy WARN][${timeString}]: ` + content));
6262
break;
6363
}
6464

@@ -89,7 +89,7 @@ module.exports.warn = (content) => {
8989
};
9090

9191
module.exports.error = (content) => {
92-
printLog(content, LogLevelMap.error);
92+
printLog(content, LogLevelMap.system_error);
9393
};
9494

9595
module.exports.ruleError = (content) => {

lib/util.js

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const fs = require('fs'),
44
path = require('path'),
55
mime = require('mime-types'),
66
color = require('colorful'),
7-
crypto = require('crypto'),
7+
child_process = require('child_process'),
88
Buffer = require('buffer').Buffer,
99
logUtil = require('./log');
1010
const networkInterfaces = require('os').networkInterfaces();
@@ -216,6 +216,8 @@ function deleteFolderContentsRecursive(dirPath, ifClearFolderItself) {
216216
throw new Error('can_not_delete_this_dir');
217217
}
218218

219+
console.info('==>>> delete cache ', dirPath);
220+
219221
if (fs.existsSync(dirPath)) {
220222
fs.readdirSync(dirPath).forEach((file) => {
221223
const curPath = path.join(dirPath, file);
@@ -307,17 +309,18 @@ module.exports.isIpDomain = function (domain) {
307309
return ipReg.test(domain);
308310
};
309311

310-
/**
311-
* To generic a Sec-WebSocket-Accept value
312-
* 1. append the `Sec-WebSocket-Key` request header with `matic string`
313-
* 2. get sha1 hash of the string
314-
* 3. get base64 of the sha1 hash
315-
*/
316-
module.exports.genericWsSecAccept = function (wsSecKey) {
317-
// the string to generate the Sec-WebSocket-Accept
318-
const magicString = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
319-
const targetString = `${wsSecKey}${magicString}`;
320-
const shasum = crypto.createHash('sha1');
321-
shasum.update(targetString);
322-
return shasum.digest('base64');
323-
}
312+
module.exports.execScriptSync = function (cmd) {
313+
let stdout,
314+
status = 0;
315+
try {
316+
stdout = child_process.execSync(cmd);
317+
} catch (err) {
318+
stdout = err.stdout;
319+
status = err.status;
320+
}
321+
322+
return {
323+
stdout: stdout.toString(),
324+
status
325+
};
326+
};

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "anyproxy",
3-
"version": "4.0.10",
3+
"version": "4.0.11",
44
"description": "A fully configurable HTTP/HTTPS proxy in Node.js",
55
"main": "proxy.js",
66
"bin": {
@@ -35,6 +35,7 @@
3535
"request": "^2.74.0",
3636
"stream-throttle": "^0.1.3",
3737
"svg-inline-react": "^1.0.2",
38+
"thunkify": "^2.1.2",
3839
"whatwg-fetch": "^1.0.0",
3940
"ws": "^5.1.0"
4041
},
@@ -68,7 +69,6 @@
6869
"koa-send": "^3.2.0",
6970
"less": "^2.7.1",
7071
"less-loader": "^2.2.3",
71-
"memwatch-next": "^0.3.0",
7272
"node-simhash": "^0.1.0",
7373
"nodeunit": "^0.9.1",
7474
"phantom": "^4.0.0",

0 commit comments

Comments
 (0)