fanqiang 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +4 -2
- package/lib/core/TerraformTunnelProxyOperations.d.ts.map +1 -1
- package/lib/core/TerraformTunnelProxyOperations.js +24 -0
- package/lib/core/TerraformTunnelProxyOperations.js.map +1 -1
- package/lib/core/TerraformTunnelProxyOperations.ts +23 -0
- package/lib/core/terraform.d.ts.map +1 -1
- package/lib/core/terraform.js +15 -4
- package/lib/core/terraform.js.map +1 -1
- package/lib/core/terraform.ts +15 -4
- package/lib/domain/Clash.d.ts.map +1 -1
- package/lib/domain/Clash.js +12 -2
- package/lib/domain/Clash.js.map +1 -1
- package/lib/domain/Clash.ts +12 -2
- package/package.json +5 -14
- package/terraform/cloud-init/proxy-init.sh +9 -0
- package/terraform/cloud-init/tunnel-init.sh +38 -0
- package/terraform/main.tf +25 -0
- package/terraform/outputs.tf +6 -0
- package/terraform/proxy.tf +34 -0
- package/terraform/tunnel.tf +106 -0
- package/terraform/variables.tf +18 -0
package/README.md
CHANGED
@@ -33,6 +33,8 @@ Options:
|
|
33
33
|
[string] [default: "us-east-1"]
|
34
34
|
--tunnel-region Aliyun region for tunnel deployment
|
35
35
|
[string] [default: "cn-shanghai"]
|
36
|
+
--bucket AWS S3 bucket name, used to store clash client configuration file
|
37
|
+
[string] [default: "fanqiang-$USER"]
|
36
38
|
--help Show help [boolean]
|
37
39
|
--version Show version number [boolean]
|
38
40
|
```
|
@@ -43,7 +45,7 @@ You need to configure an AWS IAM user on the local machine before running any co
|
|
43
45
|
supports reading AWS credentials from <code>Shared Credentials File</code>:
|
44
46
|
|
45
47
|
- The shared credentials file on Linux, Unix, and macOS: ~/.aws/credentials
|
46
|
-
- The shared credentials file on Windows: C:\Users\USER_NAME
|
48
|
+
- The shared credentials file on Windows: C:\Users\USER_NAME\\.aws\credentials
|
47
49
|
|
48
50
|
An example of credentials file:
|
49
51
|
|
@@ -63,7 +65,7 @@ You need to configure an Aliyun RAM user on a local machine if you want to use -
|
|
63
65
|
supports reading credentials from <code>$HOME/.alibabacloud/credentials</code>:
|
64
66
|
|
65
67
|
- The credentials file on Linux, Unix, and macOS: ~/.alibabacloud/credentials
|
66
|
-
- The credentials file on Windows: C:\Users\USER_NAME
|
68
|
+
- The credentials file on Windows: C:\Users\USER_NAME\\.alibabacloud\credentials
|
67
69
|
|
68
70
|
An example of credentials file:
|
69
71
|
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"TerraformTunnelProxyOperations.d.ts","sourceRoot":"","sources":["TerraformTunnelProxyOperations.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
1
|
+
{"version":3,"file":"TerraformTunnelProxyOperations.d.ts","sourceRoot":"","sources":["TerraformTunnelProxyOperations.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAMhD,qBAAa,8BAA+B,YAAW,qBAAqB;IAC9D,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa;IAEnD,MAAM,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAoB/E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAI/B"}
|
@@ -5,12 +5,23 @@ const tslib_1 = require("tslib");
|
|
5
5
|
const terraform = tslib_1.__importStar(require("./terraform"));
|
6
6
|
const AwsS3CloudStorage_1 = require("./AwsS3CloudStorage");
|
7
7
|
const fs = tslib_1.__importStar(require("fs-extra"));
|
8
|
+
const net = tslib_1.__importStar(require("net"));
|
9
|
+
const promise_retry_1 = tslib_1.__importDefault(require("promise-retry"));
|
8
10
|
class TerraformTunnelProxyOperations {
|
9
11
|
constructor(configuration) {
|
10
12
|
this.configuration = configuration;
|
11
13
|
}
|
12
14
|
async create(request) {
|
13
15
|
const applyResult = await terraform.apply(request, this.configuration.terraformWorkspace, this.configuration.aliyun.credentials);
|
16
|
+
await promise_retry_1.default(async (retry) => {
|
17
|
+
try {
|
18
|
+
await checkServiceAvailable(request.port, applyResult.address, 2000);
|
19
|
+
}
|
20
|
+
catch (error) {
|
21
|
+
console.log("Service is not ready, waiting...");
|
22
|
+
retry(error);
|
23
|
+
}
|
24
|
+
});
|
14
25
|
return {
|
15
26
|
address: applyResult.address,
|
16
27
|
cloudStorage: new AwsS3CloudStorage_1.AwsS3CloudStorage(request.proxyRegion, request.bucket, applyResult.bucketDomain),
|
@@ -22,4 +33,17 @@ class TerraformTunnelProxyOperations {
|
|
22
33
|
}
|
23
34
|
}
|
24
35
|
exports.TerraformTunnelProxyOperations = TerraformTunnelProxyOperations;
|
36
|
+
async function checkServiceAvailable(port, host, timeout) {
|
37
|
+
const socket = net.connect({ port, host, family: 4, timeout });
|
38
|
+
try {
|
39
|
+
await new Promise((resolve, reject) => {
|
40
|
+
socket.once("connect", resolve);
|
41
|
+
socket.once("timeout", () => reject("timeout"));
|
42
|
+
socket.once("error", (err) => reject(err));
|
43
|
+
});
|
44
|
+
}
|
45
|
+
finally {
|
46
|
+
socket.destroy();
|
47
|
+
}
|
48
|
+
}
|
25
49
|
//# sourceMappingURL=TerraformTunnelProxyOperations.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"TerraformTunnelProxyOperations.js","sourceRoot":"","sources":["TerraformTunnelProxyOperations.ts"],"names":[],"mappings":";;;;AAKA,+DAAyC;AAEzC,2DAAwD;AACxD,qDAA+B;
|
1
|
+
{"version":3,"file":"TerraformTunnelProxyOperations.js","sourceRoot":"","sources":["TerraformTunnelProxyOperations.ts"],"names":[],"mappings":";;;;AAKA,+DAAyC;AAEzC,2DAAwD;AACxD,qDAA+B;AAC/B,iDAA2B;AAC3B,0EAAyC;AAEzC,MAAa,8BAA8B;IACzC,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAE7D,KAAK,CAAC,MAAM,CAAC,OAAmC;QAC9C,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,KAAK,CACvC,OAAO,EACP,IAAI,CAAC,aAAa,CAAC,kBAAkB,EACrC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,CACtC,CAAC;QACF,MAAM,uBAAY,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACjC,IAAI;gBACF,MAAM,qBAAqB,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;aACtE;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;gBAChD,KAAK,CAAC,KAAK,CAAC,CAAC;aACd;QACH,CAAC,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,YAAY,EAAE,IAAI,qCAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,YAAY,CAAC;SACnG,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACtG,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvF,CAAC;CACF;AA3BD,wEA2BC;AAED,KAAK,UAAU,qBAAqB,CAAC,IAAY,EAAE,IAAY,EAAE,OAAe;IAC9E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/D,IAAI;QACF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACpC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAChC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;KACJ;YAAS;QACR,MAAM,CAAC,OAAO,EAAE,CAAC;KAClB;AACH,CAAC"}
|
@@ -7,6 +7,8 @@ import * as terraform from "./terraform";
|
|
7
7
|
import { Configuration } from "./Configuration";
|
8
8
|
import { AwsS3CloudStorage } from "./AwsS3CloudStorage";
|
9
9
|
import * as fs from "fs-extra";
|
10
|
+
import * as net from "net";
|
11
|
+
import promiseRetry from "promise-retry";
|
10
12
|
|
11
13
|
export class TerraformTunnelProxyOperations implements TunnelProxyOperations {
|
12
14
|
constructor(private readonly configuration: Configuration) {}
|
@@ -17,6 +19,14 @@ export class TerraformTunnelProxyOperations implements TunnelProxyOperations {
|
|
17
19
|
this.configuration.terraformWorkspace,
|
18
20
|
this.configuration.aliyun.credentials
|
19
21
|
);
|
22
|
+
await promiseRetry(async (retry) => {
|
23
|
+
try {
|
24
|
+
await checkServiceAvailable(request.port, applyResult.address, 2000);
|
25
|
+
} catch (error) {
|
26
|
+
console.log("Service is not ready, waiting...");
|
27
|
+
retry(error);
|
28
|
+
}
|
29
|
+
});
|
20
30
|
return {
|
21
31
|
address: applyResult.address,
|
22
32
|
cloudStorage: new AwsS3CloudStorage(request.proxyRegion, request.bucket, applyResult.bucketDomain),
|
@@ -28,3 +38,16 @@ export class TerraformTunnelProxyOperations implements TunnelProxyOperations {
|
|
28
38
|
await fs.rm(this.configuration.terraformWorkspace, { force: true, recursive: true });
|
29
39
|
}
|
30
40
|
}
|
41
|
+
|
42
|
+
async function checkServiceAvailable(port: number, host: string, timeout: number): Promise<void> {
|
43
|
+
const socket = net.connect({ port, host, family: 4, timeout });
|
44
|
+
try {
|
45
|
+
await new Promise((resolve, reject) => {
|
46
|
+
socket.once("connect", resolve);
|
47
|
+
socket.once("timeout", () => reject("timeout"));
|
48
|
+
socket.once("error", (err) => reject(err));
|
49
|
+
});
|
50
|
+
} finally {
|
51
|
+
socket.destroy();
|
52
|
+
}
|
53
|
+
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"terraform.d.ts","sourceRoot":"","sources":["terraform.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAmC,MAAM,qBAAqB,CAAC;AAEzF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;
|
1
|
+
{"version":3,"file":"terraform.d.ts","sourceRoot":"","sources":["terraform.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAmC,MAAM,qBAAqB,CAAC;AAEzF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAM7E,wBAAsB,KAAK,CACzB,OAAO,EAAE,0BAA0B,EACnC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,iBAAiB,GAC7B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAOpD;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAM5F"}
|
package/lib/core/terraform.js
CHANGED
@@ -8,9 +8,10 @@ const util = tslib_1.__importStar(require("util"));
|
|
8
8
|
const process = tslib_1.__importStar(require("process"));
|
9
9
|
const fs = tslib_1.__importStar(require("fs-extra"));
|
10
10
|
const path = tslib_1.__importStar(require("path"));
|
11
|
+
const promise_retry_1 = tslib_1.__importDefault(require("promise-retry"));
|
11
12
|
async function apply(request, workdir, credentials) {
|
12
13
|
await init(request, workdir);
|
13
|
-
await
|
14
|
+
await provisioningRetry(["apply", "-auto-approve"], workdir, aliyunCredentials_1.asTerraformEnvironmentVariables(credentials));
|
14
15
|
return {
|
15
16
|
address: await inspecting(["output", "-raw", "address"], workdir),
|
16
17
|
bucketDomain: await inspecting(["output", "-raw", "bucket_domain_name"], workdir),
|
@@ -22,7 +23,7 @@ async function destroy(workdir, credentials) {
|
|
22
23
|
console.log("Tunnel proxy never created, nothing to destroy");
|
23
24
|
return;
|
24
25
|
}
|
25
|
-
await
|
26
|
+
await provisioningRetry(["destroy", "-auto-approve"], workdir, aliyunCredentials_1.asTerraformEnvironmentVariables(credentials));
|
26
27
|
}
|
27
28
|
exports.destroy = destroy;
|
28
29
|
async function init(request, workdir) {
|
@@ -37,10 +38,20 @@ async function init(request, workdir) {
|
|
37
38
|
});
|
38
39
|
if (!(await fs.pathExists(path.join(workdir, ".terraform")))) {
|
39
40
|
await fs.copy(path.resolve(__dirname, "..", "..", "terraform"), workdir, { overwrite: true, recursive: true });
|
40
|
-
await
|
41
|
+
await provisioningRetry(["init"], workdir);
|
41
42
|
}
|
42
43
|
}
|
43
|
-
async function
|
44
|
+
async function provisioningRetry(args, cwd, customEnv = {}) {
|
45
|
+
await promise_retry_1.default(async (retry) => {
|
46
|
+
try {
|
47
|
+
await provisioning(args, cwd, customEnv);
|
48
|
+
}
|
49
|
+
catch (error) {
|
50
|
+
retry(error);
|
51
|
+
}
|
52
|
+
});
|
53
|
+
}
|
54
|
+
async function provisioning(args, cwd, customEnv) {
|
44
55
|
return new Promise((resolve, reject) => {
|
45
56
|
const p = child_process.spawn("terraform", args, { cwd, stdio: "inherit", env: Object.assign(Object.assign({}, process.env), customEnv) });
|
46
57
|
p.on("close", (code) => {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"terraform.js","sourceRoot":"","sources":["terraform.ts"],"names":[],"mappings":";;;;AAAA,qEAA+C;AAC/C,2DAAyF;AACzF,mDAA6B;AAE7B,yDAAmC;AACnC,qDAA+B;AAC/B,mDAA6B;
|
1
|
+
{"version":3,"file":"terraform.js","sourceRoot":"","sources":["terraform.ts"],"names":[],"mappings":"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"}
|
package/lib/core/terraform.ts
CHANGED
@@ -5,6 +5,7 @@ import { TunnelProxyCreatingRequest } from "../domain/TunnelProxyOperations";
|
|
5
5
|
import * as process from "process";
|
6
6
|
import * as fs from "fs-extra";
|
7
7
|
import * as path from "path";
|
8
|
+
import promiseRetry from "promise-retry";
|
8
9
|
|
9
10
|
export async function apply(
|
10
11
|
request: TunnelProxyCreatingRequest,
|
@@ -12,7 +13,7 @@ export async function apply(
|
|
12
13
|
credentials: AliyunCredentials
|
13
14
|
): Promise<{ address: string; bucketDomain: string }> {
|
14
15
|
await init(request, workdir);
|
15
|
-
await
|
16
|
+
await provisioningRetry(["apply", "-auto-approve"], workdir, asTerraformEnvironmentVariables(credentials));
|
16
17
|
return {
|
17
18
|
address: await inspecting(["output", "-raw", "address"], workdir),
|
18
19
|
bucketDomain: await inspecting(["output", "-raw", "bucket_domain_name"], workdir),
|
@@ -24,7 +25,7 @@ export async function destroy(workdir: string, credentials: AliyunCredentials):
|
|
24
25
|
console.log("Tunnel proxy never created, nothing to destroy");
|
25
26
|
return;
|
26
27
|
}
|
27
|
-
await
|
28
|
+
await provisioningRetry(["destroy", "-auto-approve"], workdir, asTerraformEnvironmentVariables(credentials));
|
28
29
|
}
|
29
30
|
|
30
31
|
async function init(request: TunnelProxyCreatingRequest, workdir: string): Promise<void> {
|
@@ -39,11 +40,21 @@ async function init(request: TunnelProxyCreatingRequest, workdir: string): Promi
|
|
39
40
|
});
|
40
41
|
if (!(await fs.pathExists(path.join(workdir, ".terraform")))) {
|
41
42
|
await fs.copy(path.resolve(__dirname, "..", "..", "terraform"), workdir, { overwrite: true, recursive: true });
|
42
|
-
await
|
43
|
+
await provisioningRetry(["init"], workdir);
|
43
44
|
}
|
44
45
|
}
|
45
46
|
|
46
|
-
async function
|
47
|
+
async function provisioningRetry(args: string[], cwd: string, customEnv: Record<string, string> = {}): Promise<void> {
|
48
|
+
await promiseRetry(async (retry) => {
|
49
|
+
try {
|
50
|
+
await provisioning(args, cwd, customEnv);
|
51
|
+
} catch (error) {
|
52
|
+
retry(error);
|
53
|
+
}
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
async function provisioning(args: string[], cwd: string, customEnv: Record<string, string>): Promise<void> {
|
47
58
|
return new Promise<void>((resolve, reject) => {
|
48
59
|
const p = child_process.spawn("terraform", args, { cwd, stdio: "inherit", env: { ...process.env, ...customEnv } });
|
49
60
|
p.on("close", (code) => {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"Clash.d.ts","sourceRoot":"","sources":["Clash.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,oBAAY,yBAAyB,GAAG,YAAY,GAAG;IACrD,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,yBAAyB,GAAG,MAAM,
|
1
|
+
{"version":3,"file":"Clash.d.ts","sourceRoot":"","sources":["Clash.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,oBAAY,yBAAyB,GAAG,YAAY,GAAG;IACrD,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,yBAAyB,GAAG,MAAM,CA0B/E"}
|
package/lib/domain/Clash.js
CHANGED
@@ -7,10 +7,19 @@ function generateConfigFrom(endpoints) {
|
|
7
7
|
return yaml_1.default.stringify({
|
8
8
|
port: 7890,
|
9
9
|
"socks-port": 7891,
|
10
|
-
|
10
|
+
"redir-port": 7892,
|
11
|
+
"tproxy-port": 7893,
|
12
|
+
"mixed-port": 7890,
|
13
|
+
mode: "rule",
|
14
|
+
dns: {
|
15
|
+
enable: true,
|
16
|
+
listen: "0.0.0.0:53",
|
17
|
+
"enhanced-mode": "redir-host",
|
18
|
+
nameserver: ["223.5.5.5", "119.29.29.29", "114.114.114.114", "tls://dns.rubyfish.cn:853"],
|
19
|
+
},
|
11
20
|
proxies: [
|
12
21
|
{
|
13
|
-
name: "
|
22
|
+
name: "auto",
|
14
23
|
type: "ss",
|
15
24
|
server: endpoints.address,
|
16
25
|
port: endpoints.port,
|
@@ -18,6 +27,7 @@ function generateConfigFrom(endpoints) {
|
|
18
27
|
password: endpoints.password,
|
19
28
|
},
|
20
29
|
],
|
30
|
+
rules: ["DOMAIN-SUFFIX,google.com,auto", "DOMAIN,ad.com,REJECT", "GEOIP,CN,DIRECT", "MATCH,auto"],
|
21
31
|
});
|
22
32
|
}
|
23
33
|
exports.generateConfigFrom = generateConfigFrom;
|
package/lib/domain/Clash.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"Clash.js","sourceRoot":"","sources":["Clash.ts"],"names":[],"mappings":";;;;AAAA,wDAAwB;AAOxB,SAAgB,kBAAkB,CAAC,SAAoC;IACrE,OAAO,cAAI,CAAC,SAAS,CAAC;QACpB,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,IAAI;QAClB,IAAI,EAAE,
|
1
|
+
{"version":3,"file":"Clash.js","sourceRoot":"","sources":["Clash.ts"],"names":[],"mappings":";;;;AAAA,wDAAwB;AAOxB,SAAgB,kBAAkB,CAAC,SAAoC;IACrE,OAAO,cAAI,CAAC,SAAS,CAAC;QACpB,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,IAAI;QAClB,aAAa,EAAE,IAAI;QACnB,YAAY,EAAE,IAAI;QAClB,IAAI,EAAE,MAAM;QACZ,GAAG,EAAE;YACH,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,YAAY;YACpB,eAAe,EAAE,YAAY;YAC7B,UAAU,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,2BAA2B,CAAC;SAC1F;QACD,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI;gBACV,MAAM,EAAE,SAAS,CAAC,OAAO;gBACzB,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,MAAM,EAAE,SAAS,CAAC,mBAAmB;gBACrC,QAAQ,EAAE,SAAS,CAAC,QAAQ;aAC7B;SACF;QACD,KAAK,EAAE,CAAC,+BAA+B,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,YAAY,CAAC;KAClG,CAAC,CAAC;AACL,CAAC;AA1BD,gDA0BC"}
|
package/lib/domain/Clash.ts
CHANGED
@@ -9,10 +9,19 @@ export function generateConfigFrom(endpoints: TunnelProxyConnectionInfo): string
|
|
9
9
|
return yaml.stringify({
|
10
10
|
port: 7890,
|
11
11
|
"socks-port": 7891,
|
12
|
-
|
12
|
+
"redir-port": 7892,
|
13
|
+
"tproxy-port": 7893,
|
14
|
+
"mixed-port": 7890,
|
15
|
+
mode: "rule",
|
16
|
+
dns: {
|
17
|
+
enable: true,
|
18
|
+
listen: "0.0.0.0:53",
|
19
|
+
"enhanced-mode": "redir-host",
|
20
|
+
nameserver: ["223.5.5.5", "119.29.29.29", "114.114.114.114", "tls://dns.rubyfish.cn:853"],
|
21
|
+
},
|
13
22
|
proxies: [
|
14
23
|
{
|
15
|
-
name: "
|
24
|
+
name: "auto",
|
16
25
|
type: "ss",
|
17
26
|
server: endpoints.address,
|
18
27
|
port: endpoints.port,
|
@@ -20,5 +29,6 @@ export function generateConfigFrom(endpoints: TunnelProxyConnectionInfo): string
|
|
20
29
|
password: endpoints.password,
|
21
30
|
},
|
22
31
|
],
|
32
|
+
rules: ["DOMAIN-SUFFIX,google.com,auto", "DOMAIN,ad.com,REJECT", "GEOIP,CN,DIRECT", "MATCH,auto"],
|
23
33
|
});
|
24
34
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "fanqiang",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.3.0",
|
4
4
|
"description": "Tunnel Proxy Auto Deployment",
|
5
5
|
"bin": "./bin/fanqiang.js",
|
6
6
|
"scripts": {
|
@@ -23,7 +23,7 @@
|
|
23
23
|
"files": [
|
24
24
|
"/bin",
|
25
25
|
"/lib",
|
26
|
-
"/
|
26
|
+
"/terraform",
|
27
27
|
"index.*"
|
28
28
|
],
|
29
29
|
"homepage": "https://github.com/zhifanz/fanqiang#readme",
|
@@ -32,14 +32,11 @@
|
|
32
32
|
"@commitlint/config-conventional": "^12.1.4",
|
33
33
|
"@semantic-release/changelog": "^5.0.1",
|
34
34
|
"@semantic-release/git": "^9.0.0",
|
35
|
-
"@types/ali-oss": "^6.0.10",
|
36
35
|
"@types/fs-extra": "^9.0.12",
|
37
36
|
"@types/lodash": "^4.14.171",
|
38
37
|
"@types/mocha": "^8.2.3",
|
39
|
-
"@types/netmask": "^1.0.30",
|
40
38
|
"@types/node": "^14.14.31",
|
41
|
-
"@types/
|
42
|
-
"@types/tmp": "^0.2.1",
|
39
|
+
"@types/promise-retry": "^1.1.3",
|
43
40
|
"@types/yargs": "^17.0.2",
|
44
41
|
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
45
42
|
"@typescript-eslint/parser": "^4.28.2",
|
@@ -62,18 +59,12 @@
|
|
62
59
|
},
|
63
60
|
"dependencies": {
|
64
61
|
"@alicloud/credentials": "^2.1.1",
|
65
|
-
"@alicloud/openapi-client": "^0.3.3",
|
66
|
-
"@alicloud/tea-typescript": "^1.7.1",
|
67
|
-
"@aws-sdk/client-iam": "^3.27.0",
|
68
|
-
"@aws-sdk/client-lightsail": "^3.21.0",
|
69
62
|
"@aws-sdk/client-s3": "^3.27.0",
|
70
|
-
"
|
63
|
+
"@aws-sdk/node-config-provider": "^3.29.0",
|
71
64
|
"dotenv": "^10.0.0",
|
72
65
|
"fs-extra": "^10.0.0",
|
73
66
|
"lodash": "^4.17.21",
|
74
|
-
"
|
75
|
-
"qs": "^6.10.1",
|
76
|
-
"tmp": "^0.2.1",
|
67
|
+
"promise-retry": "^2.0.1",
|
77
68
|
"tslib": "^2.3.0",
|
78
69
|
"yaml": "^1.10.2",
|
79
70
|
"yargs": "^17.0.1"
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
PORT=${port}
|
3
|
+
ENCRYPTION_ALGORITHM=${encryption_algorithm}
|
4
|
+
PASSWORD=${password}
|
5
|
+
|
6
|
+
curl --location -o /tmp/shadowsocks.tar.xz https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.11.1/shadowsocks-v1.11.1.x86_64-unknown-linux-gnu.tar.xz
|
7
|
+
mkdir /var/lib/shadowsocks
|
8
|
+
tar -x -f /tmp/shadowsocks.tar.xz -C /var/lib/shadowsocks
|
9
|
+
/var/lib/shadowsocks/ssserver -s "[::]:$PORT" -m "$ENCRYPTION_ALGORITHM" -k "$PASSWORD" -d
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
PROXY_ADDRESS=${proxy_address}
|
4
|
+
PROXY_PORT=${proxy_port}
|
5
|
+
ELASTIC_IP_ALLOCATION_ID=${elastic_ip_allocation_id}
|
6
|
+
REGION=${region}
|
7
|
+
RAM_ROLE_NAME=${ram_role_name}
|
8
|
+
|
9
|
+
aliyun configure set --region $REGION --mode EcsRamRole --ram-role-name $RAM_ROLE_NAME
|
10
|
+
aliyun --endpoint "vpc-vpc.$REGION.aliyuncs.com" vpc UnassociateEipAddress --AllocationId $ELASTIC_IP_ALLOCATION_ID || true
|
11
|
+
aliyun --endpoint "vpc-vpc.$REGION.aliyuncs.com" vpc AssociateEipAddress --AllocationId $ELASTIC_IP_ALLOCATION_ID --InstanceId "i-$${HOSTNAME: 2: 20}"
|
12
|
+
sleep 5
|
13
|
+
|
14
|
+
yum install nginx -y &>> ~/cloud-init.log
|
15
|
+
sleep 5
|
16
|
+
yum install nginx-all-modules -y &>> ~/cloud-init.log
|
17
|
+
|
18
|
+
cat > /etc/nginx/nginx.conf <<EOF
|
19
|
+
user nginx;
|
20
|
+
worker_processes auto;
|
21
|
+
error_log /var/log/nginx/error.log;
|
22
|
+
pid /run/nginx.pid;
|
23
|
+
|
24
|
+
include /usr/share/nginx/modules/*.conf;
|
25
|
+
|
26
|
+
events {
|
27
|
+
worker_connections 1024;
|
28
|
+
}
|
29
|
+
|
30
|
+
stream {
|
31
|
+
server {
|
32
|
+
listen $PROXY_PORT;
|
33
|
+
proxy_pass $PROXY_ADDRESS:$PROXY_PORT;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
EOF
|
37
|
+
|
38
|
+
systemctl start nginx
|
@@ -0,0 +1,25 @@
|
|
1
|
+
terraform {
|
2
|
+
required_providers {
|
3
|
+
aws = {
|
4
|
+
source = "hashicorp/aws"
|
5
|
+
version = "~> 3.0"
|
6
|
+
}
|
7
|
+
alicloud = {
|
8
|
+
source = "aliyun/alicloud"
|
9
|
+
version = "1.134.0"
|
10
|
+
}
|
11
|
+
}
|
12
|
+
}
|
13
|
+
provider "aws" {
|
14
|
+
region = var.proxy_region
|
15
|
+
}
|
16
|
+
provider "alicloud" {
|
17
|
+
region = var.tunnel_region
|
18
|
+
}
|
19
|
+
|
20
|
+
locals {
|
21
|
+
instance_type = "ecs.t5-lc2m1.nano"
|
22
|
+
image_id = "aliyun_2_1903_x64_20G_alibase_20210726.vhd"
|
23
|
+
internet_max_bandwidth_out = 100
|
24
|
+
max_price_per_hour = "0.05"
|
25
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
resource "aws_lightsail_instance" "default" {
|
2
|
+
availability_zone = data.aws_availability_zones.default.names[0]
|
3
|
+
blueprint_id = "centos_8"
|
4
|
+
bundle_id = "nano_2_0"
|
5
|
+
name = "fanqiang"
|
6
|
+
user_data = templatefile("${path.root}/cloud-init/proxy-init.sh", {
|
7
|
+
port = var.port,
|
8
|
+
encryption_algorithm = var.encryption_algorithm,
|
9
|
+
password = var.password
|
10
|
+
})
|
11
|
+
}
|
12
|
+
|
13
|
+
resource "aws_lightsail_instance_public_ports" "default" {
|
14
|
+
instance_name = aws_lightsail_instance.default.name
|
15
|
+
|
16
|
+
dynamic "port_info" {
|
17
|
+
for_each = [var.port, 22]
|
18
|
+
content {
|
19
|
+
protocol = "tcp"
|
20
|
+
from_port = port_info.value
|
21
|
+
to_port = port_info.value
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
resource "aws_s3_bucket" "default" {
|
27
|
+
bucket = var.bucket
|
28
|
+
acl = "public-read"
|
29
|
+
force_destroy = true
|
30
|
+
}
|
31
|
+
|
32
|
+
data "aws_availability_zones" "default" {
|
33
|
+
state = "available"
|
34
|
+
}
|
@@ -0,0 +1,106 @@
|
|
1
|
+
resource "alicloud_vpc" "default" {
|
2
|
+
cidr_block = "192.168.0.0/16"
|
3
|
+
}
|
4
|
+
|
5
|
+
resource "alicloud_vswitch" "default" {
|
6
|
+
count = length(data.alicloud_zones.default.ids)
|
7
|
+
zone_id = data.alicloud_zones.default.ids[count.index]
|
8
|
+
cidr_block = "192.168.${count.index}.0/24"
|
9
|
+
vpc_id = alicloud_vpc.default.id
|
10
|
+
}
|
11
|
+
|
12
|
+
resource "alicloud_security_group" "default" {
|
13
|
+
vpc_id = alicloud_vpc.default.id
|
14
|
+
}
|
15
|
+
|
16
|
+
resource "alicloud_security_group_rule" "default" {
|
17
|
+
for_each = toset([tostring(var.port), "22"])
|
18
|
+
security_group_id = alicloud_security_group.default.id
|
19
|
+
ip_protocol = "tcp"
|
20
|
+
type = "ingress"
|
21
|
+
cidr_ip = "0.0.0.0/0"
|
22
|
+
port_range = "${each.key}/${each.key}"
|
23
|
+
}
|
24
|
+
|
25
|
+
resource "alicloud_auto_provisioning_group" "default" {
|
26
|
+
launch_template_id = alicloud_ecs_launch_template.default.id
|
27
|
+
total_target_capacity = "1"
|
28
|
+
pay_as_you_go_target_capacity = "0"
|
29
|
+
spot_target_capacity = "1"
|
30
|
+
auto_provisioning_group_type = "maintain"
|
31
|
+
spot_allocation_strategy = "lowest-price"
|
32
|
+
spot_instance_interruption_behavior = "terminate"
|
33
|
+
excess_capacity_termination_policy = "termination"
|
34
|
+
terminate_instances = true
|
35
|
+
dynamic "launch_template_config" {
|
36
|
+
for_each = alicloud_vswitch.default.*.id
|
37
|
+
content {
|
38
|
+
instance_type = local.instance_type
|
39
|
+
max_price = local.max_price_per_hour
|
40
|
+
vswitch_id = launch_template_config.value
|
41
|
+
weighted_capacity = "1"
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
resource "alicloud_ecs_launch_template" "default" {
|
47
|
+
launch_template_name = "fanqiang"
|
48
|
+
image_id = local.image_id
|
49
|
+
instance_charge_type = "PostPaid"
|
50
|
+
instance_type = local.instance_type
|
51
|
+
security_group_id = alicloud_security_group.default.id
|
52
|
+
key_pair_name = length(data.alicloud_ecs_key_pairs.default) > 0 ? data.alicloud_ecs_key_pairs.default.names[0] : null
|
53
|
+
spot_duration = 0
|
54
|
+
spot_strategy = "SpotAsPriceGo"
|
55
|
+
ram_role_name = alicloud_ram_role.default.id
|
56
|
+
user_data = base64encode(templatefile("${path.root}/cloud-init/tunnel-init.sh", {
|
57
|
+
proxy_port = var.port,
|
58
|
+
proxy_address = aws_lightsail_instance.default.public_ip_address,
|
59
|
+
elastic_ip_allocation_id = alicloud_eip_address.default.id,
|
60
|
+
region = var.tunnel_region
|
61
|
+
ram_role_name = alicloud_ram_role.default.id
|
62
|
+
}))
|
63
|
+
system_disk {
|
64
|
+
category = "cloud_efficiency"
|
65
|
+
delete_with_instance = true
|
66
|
+
size = 40
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
data "alicloud_ecs_key_pairs" "default" {}
|
71
|
+
|
72
|
+
resource "alicloud_ram_role" "default" {
|
73
|
+
name = "FangqiangEcsEipAccessRole"
|
74
|
+
document = <<EOF
|
75
|
+
{
|
76
|
+
"Statement": [
|
77
|
+
{
|
78
|
+
"Action": "sts:AssumeRole",
|
79
|
+
"Effect": "Allow",
|
80
|
+
"Principal": {
|
81
|
+
"Service": [
|
82
|
+
"ecs.aliyuncs.com"
|
83
|
+
]
|
84
|
+
}
|
85
|
+
}
|
86
|
+
],
|
87
|
+
"Version": "1"
|
88
|
+
}
|
89
|
+
EOF
|
90
|
+
force = true
|
91
|
+
}
|
92
|
+
|
93
|
+
resource "alicloud_ram_role_policy_attachment" "default" {
|
94
|
+
policy_name = "AliyunEIPFullAccess"
|
95
|
+
policy_type = "System"
|
96
|
+
role_name = alicloud_ram_role.default.id
|
97
|
+
}
|
98
|
+
|
99
|
+
resource "alicloud_eip_address" "default" {
|
100
|
+
bandwidth = local.internet_max_bandwidth_out
|
101
|
+
internet_charge_type = "PayByTraffic"
|
102
|
+
}
|
103
|
+
|
104
|
+
data "alicloud_zones" "default" {}
|
105
|
+
|
106
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
variable "proxy_region" {
|
2
|
+
type = string
|
3
|
+
}
|
4
|
+
variable "tunnel_region" {
|
5
|
+
type = string
|
6
|
+
}
|
7
|
+
variable "port" {
|
8
|
+
type = number
|
9
|
+
}
|
10
|
+
variable "password" {
|
11
|
+
type = string
|
12
|
+
}
|
13
|
+
variable "encryption_algorithm" {
|
14
|
+
type = string
|
15
|
+
}
|
16
|
+
variable "bucket" {
|
17
|
+
type = string
|
18
|
+
}
|