bdy 1.22.84 → 1.22.85-beta
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/distTs/package.json +1 -1
- package/distTs/src/agent/socket/client.js +2 -2
- package/distTs/src/api/client.js +4 -3
- package/distTs/src/command/login.js +11 -2
- package/distTs/src/tunnel/api/buddy.js +1 -1
- package/distTs/src/tunnel/config.js +241 -0
- package/distTs/src/tunnel/http/auth.js +404 -0
- package/distTs/src/tunnel/http/jwks.js +84 -0
- package/distTs/src/tunnel/tunnel.js +134 -820
- package/distTs/src/utils.js +49 -0
- package/package.json +1 -1
package/distTs/package.json
CHANGED
|
@@ -139,10 +139,10 @@ class AgentSocketClient extends events_1.default {
|
|
|
139
139
|
forceBase64: true,
|
|
140
140
|
transports: ['websocket'],
|
|
141
141
|
reconnection: false,
|
|
142
|
-
rejectUnauthorized:
|
|
142
|
+
rejectUnauthorized: !(0, utils_1.isLocalHost)(host),
|
|
143
143
|
// @ts-ignore
|
|
144
144
|
agent: new https_1.Agent({
|
|
145
|
-
rejectUnauthorized:
|
|
145
|
+
rejectUnauthorized: !(0, utils_1.isLocalHost)(host),
|
|
146
146
|
}),
|
|
147
147
|
maxHttpBufferSize: 3e6,
|
|
148
148
|
extraHeaders: {
|
package/distTs/src/api/client.js
CHANGED
|
@@ -26,7 +26,7 @@ class ApiClient {
|
|
|
26
26
|
this.clientToken = clientToken;
|
|
27
27
|
this.client = new undici_1.Pool(baseUrl, {
|
|
28
28
|
connect: {
|
|
29
|
-
rejectUnauthorized:
|
|
29
|
+
rejectUnauthorized: !(0, utils_1.isLocalHost)(baseUrl.hostname),
|
|
30
30
|
},
|
|
31
31
|
});
|
|
32
32
|
}
|
|
@@ -706,7 +706,7 @@ class ApiClient {
|
|
|
706
706
|
client_name: name,
|
|
707
707
|
grant_types: ['authorization_code', 'refresh_token'],
|
|
708
708
|
response_types: ['code'],
|
|
709
|
-
token_endpoint_auth_method: '
|
|
709
|
+
token_endpoint_auth_method: 'client_secret_post',
|
|
710
710
|
token_expires_in: 3600,
|
|
711
711
|
},
|
|
712
712
|
parseResponseBody: true,
|
|
@@ -732,7 +732,7 @@ class ApiClient {
|
|
|
732
732
|
parseResponseBody: true,
|
|
733
733
|
});
|
|
734
734
|
}
|
|
735
|
-
async exchangeAppToken(code, clientId, clientSecret, redirectUrl) {
|
|
735
|
+
async exchangeAppToken(code, clientId, clientSecret, redirectUrl, codeVerifier) {
|
|
736
736
|
return await this.request({
|
|
737
737
|
method: 'POST',
|
|
738
738
|
path: '/oauth2/token',
|
|
@@ -742,6 +742,7 @@ class ApiClient {
|
|
|
742
742
|
client_id: clientId,
|
|
743
743
|
client_secret: clientSecret,
|
|
744
744
|
redirect_uri: redirectUrl,
|
|
745
|
+
code_verifier: codeVerifier,
|
|
745
746
|
},
|
|
746
747
|
parseResponseBody: true,
|
|
747
748
|
httpUrlEncoded: true,
|
|
@@ -7,6 +7,7 @@ exports.authorize = authorize;
|
|
|
7
7
|
const cfg_1 = __importDefault(require("../tunnel/cfg"));
|
|
8
8
|
const output_1 = __importDefault(require("../output"));
|
|
9
9
|
const node_http_1 = __importDefault(require("node:http"));
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
10
11
|
const texts_1 = require("../texts");
|
|
11
12
|
const utils_1 = require("../utils");
|
|
12
13
|
const input_1 = __importDefault(require("../input"));
|
|
@@ -37,6 +38,13 @@ const OAUTH_CLIENT_APP_SCOPES = [
|
|
|
37
38
|
function getRedirectUrl(api) {
|
|
38
39
|
return `https://${api}/oauth2/cli`;
|
|
39
40
|
}
|
|
41
|
+
function generatePkcePair() {
|
|
42
|
+
const codeVerifier = (0, node_crypto_1.randomBytes)(32).toString('base64url');
|
|
43
|
+
const codeChallenge = (0, node_crypto_1.createHash)('sha256')
|
|
44
|
+
.update(codeVerifier)
|
|
45
|
+
.digest('base64url');
|
|
46
|
+
return { codeVerifier, codeChallenge };
|
|
47
|
+
}
|
|
40
48
|
async function waitForOpen(isRegister, abortCode, seconds = 3) {
|
|
41
49
|
output_1.default.normal(isRegister ? texts_1.TXT_REGISTER_OPENING : texts_1.TXT_LOGIN_OPENING);
|
|
42
50
|
let star = true;
|
|
@@ -66,11 +74,12 @@ async function oauthServer(api, clientId, clientSecret, forceRegisterEmail, forc
|
|
|
66
74
|
}, OAUTH_TIMEOUT);
|
|
67
75
|
return new Promise((resolve) => {
|
|
68
76
|
const state = (0, uuid_1.v4)();
|
|
77
|
+
const { codeVerifier, codeChallenge } = generatePkcePair();
|
|
69
78
|
const abortCode = new AbortController();
|
|
70
79
|
const exchange = async (urlCode, urlState, res) => {
|
|
71
80
|
const client = new ApiClient(new URL(`https://${api}`));
|
|
72
81
|
try {
|
|
73
|
-
const response = await client.exchangeAppToken(urlCode, clientId, clientSecret, redirectUrl);
|
|
82
|
+
const response = await client.exchangeAppToken(urlCode, clientId, clientSecret, redirectUrl, codeVerifier);
|
|
74
83
|
if (res)
|
|
75
84
|
res.end(urlState);
|
|
76
85
|
s.close();
|
|
@@ -111,7 +120,7 @@ async function oauthServer(api, clientId, clientSecret, forceRegisterEmail, forc
|
|
|
111
120
|
output_1.default.exitError(texts_1.ERR_LOGIN_HTTP_SERVER_PORT_TAKEN);
|
|
112
121
|
});
|
|
113
122
|
s.listen(OAUTH_CLIENT_APP_PORT, OAUTH_CLIENT_APP_HOST, async () => {
|
|
114
|
-
let url = `https://${api}/oauth2/authorize?type=web_server&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUrl)}&response_type=code&scope=${encodeURIComponent(OAUTH_CLIENT_APP_SCOPES.join(' '))}&state=${encodeURIComponent(state)}`;
|
|
123
|
+
let url = `https://${api}/oauth2/authorize?type=web_server&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUrl)}&response_type=code&scope=${encodeURIComponent(OAUTH_CLIENT_APP_SCOPES.join(' '))}&state=${encodeURIComponent(state)}&code_challenge=${encodeURIComponent(codeChallenge)}&code_challenge_method=S256`;
|
|
115
124
|
if (isRegister) {
|
|
116
125
|
url += `®ister_email=${encodeURIComponent(forceRegisterEmail)}®ister_name=${encodeURIComponent(forceRegisterName)}`;
|
|
117
126
|
}
|
|
@@ -26,7 +26,7 @@ const makeRequest = async (host, path, body, method = 'POST', respAsJson = true,
|
|
|
26
26
|
headers: body ? { 'Content-Type': 'application/json' } : undefined,
|
|
27
27
|
method,
|
|
28
28
|
signal: c.signal,
|
|
29
|
-
dispatcher: output_1.default.getUndiciUnauthorizedAgent(),
|
|
29
|
+
dispatcher: (0, utils_1.isLocalHost)(host) ? output_1.default.getUndiciUnauthorizedAgent() : undefined,
|
|
30
30
|
});
|
|
31
31
|
if (respAsJson)
|
|
32
32
|
data = await response.json();
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
8
|
+
const tunnel_1 = require("../types/tunnel");
|
|
9
|
+
class TunnelConfig {
|
|
10
|
+
type;
|
|
11
|
+
region;
|
|
12
|
+
target;
|
|
13
|
+
whitelist;
|
|
14
|
+
domain;
|
|
15
|
+
subdomain;
|
|
16
|
+
name;
|
|
17
|
+
timeout;
|
|
18
|
+
useragents;
|
|
19
|
+
headers;
|
|
20
|
+
responseHeaders;
|
|
21
|
+
terminate;
|
|
22
|
+
key;
|
|
23
|
+
cert;
|
|
24
|
+
ca;
|
|
25
|
+
host;
|
|
26
|
+
login;
|
|
27
|
+
password;
|
|
28
|
+
authType;
|
|
29
|
+
serve;
|
|
30
|
+
circuitBreaker;
|
|
31
|
+
log;
|
|
32
|
+
verify;
|
|
33
|
+
compression;
|
|
34
|
+
http2;
|
|
35
|
+
sshIp;
|
|
36
|
+
sshId;
|
|
37
|
+
sshPort;
|
|
38
|
+
sshUser;
|
|
39
|
+
sshPassword;
|
|
40
|
+
sshForwardPort;
|
|
41
|
+
sshClientUser;
|
|
42
|
+
sshClientPassword;
|
|
43
|
+
targetProto;
|
|
44
|
+
targetHost;
|
|
45
|
+
targetPort;
|
|
46
|
+
targetAuth;
|
|
47
|
+
constructor({ type, target, region, timeout, domain, subdomain, name, whitelist, tlsSettings, httpSettings, sshSettings, }) {
|
|
48
|
+
this.type = type;
|
|
49
|
+
this.region = region;
|
|
50
|
+
this.target = target;
|
|
51
|
+
this.whitelist = whitelist || [];
|
|
52
|
+
this.domain = domain;
|
|
53
|
+
this.subdomain = subdomain;
|
|
54
|
+
this.name = name;
|
|
55
|
+
this.timeout = timeout || utils_1.DEFAULT_TIMEOUT;
|
|
56
|
+
this.useragents = [];
|
|
57
|
+
this.headers = [];
|
|
58
|
+
this.responseHeaders = [];
|
|
59
|
+
if (tlsSettings) {
|
|
60
|
+
this.terminate = tlsSettings.terminate;
|
|
61
|
+
this.key = tlsSettings.key;
|
|
62
|
+
this.cert = tlsSettings.cert;
|
|
63
|
+
this.ca = tlsSettings.ca;
|
|
64
|
+
}
|
|
65
|
+
if (httpSettings) {
|
|
66
|
+
this.host = httpSettings.host;
|
|
67
|
+
if (httpSettings.authType) {
|
|
68
|
+
this.authType = httpSettings.authType;
|
|
69
|
+
if (this.authType === tunnel_1.TUNNEL_HTTP_AUTH_TYPE.BASIC) {
|
|
70
|
+
this.login = httpSettings.login || '';
|
|
71
|
+
this.password = httpSettings.password || '';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (httpSettings.login || httpSettings.password) {
|
|
75
|
+
this.authType = tunnel_1.TUNNEL_HTTP_AUTH_TYPE.BASIC;
|
|
76
|
+
this.login = httpSettings.login || '';
|
|
77
|
+
this.password = httpSettings.password || '';
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.authType = tunnel_1.TUNNEL_HTTP_AUTH_TYPE.NONE;
|
|
81
|
+
}
|
|
82
|
+
this.ca = httpSettings.ca;
|
|
83
|
+
this.serve = httpSettings.serve;
|
|
84
|
+
this.useragents = httpSettings.userAgents || [];
|
|
85
|
+
this.headers = httpSettings.headers || [];
|
|
86
|
+
this.responseHeaders = httpSettings.responseHeaders || [];
|
|
87
|
+
this.circuitBreaker = httpSettings.circuitBreaker;
|
|
88
|
+
this.log = httpSettings.log;
|
|
89
|
+
this.verify = httpSettings.verify;
|
|
90
|
+
this.compression = httpSettings.compression;
|
|
91
|
+
this.http2 = httpSettings.http2;
|
|
92
|
+
}
|
|
93
|
+
if (sshSettings) {
|
|
94
|
+
this.sshIp = sshSettings.ip;
|
|
95
|
+
this.sshId = sshSettings.sshId;
|
|
96
|
+
this.sshPort = sshSettings.port;
|
|
97
|
+
this.sshUser = sshSettings.user;
|
|
98
|
+
this.sshPassword = sshSettings.password;
|
|
99
|
+
this.sshForwardPort = sshSettings.forwardPort;
|
|
100
|
+
this.sshClientUser = sshSettings.clientUser;
|
|
101
|
+
this.sshClientPassword = sshSettings.clientPassword;
|
|
102
|
+
}
|
|
103
|
+
this.targetProto = 'http';
|
|
104
|
+
this.targetHost = 'localhost';
|
|
105
|
+
this.targetPort = 80;
|
|
106
|
+
this.targetAuth = null;
|
|
107
|
+
if (this.type === tunnel_1.TUNNEL_TYPE.HTTP) {
|
|
108
|
+
let m = this.target.match(utils_1.TARGET_ONLY_PORT_REGEX);
|
|
109
|
+
if (m) {
|
|
110
|
+
this.targetPort = parseInt(m[0], 10);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
m = this.target.match(utils_1.TARGET_HTTP_REGEX);
|
|
114
|
+
if (m) {
|
|
115
|
+
if (m[2])
|
|
116
|
+
this.targetProto = m[2];
|
|
117
|
+
if (m[6])
|
|
118
|
+
this.targetPort = parseInt(m[6], 10);
|
|
119
|
+
else if (this.targetProto === 'https')
|
|
120
|
+
this.targetPort = 443;
|
|
121
|
+
if (m[4])
|
|
122
|
+
this.targetHost = m[4];
|
|
123
|
+
if (m[3])
|
|
124
|
+
this.targetAuth = m[3].replace(/@$/, '');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (this.type === tunnel_1.TUNNEL_TYPE.SSH) {
|
|
129
|
+
this.targetHost = 'localhost';
|
|
130
|
+
this.targetProto = 'ssh://';
|
|
131
|
+
this.targetPort = 22;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const m = this.target.match(utils_1.TARGET_TCP_TLS_REGEX);
|
|
135
|
+
if (m) {
|
|
136
|
+
this.targetPort = parseInt(m[3], 10);
|
|
137
|
+
if (m[2])
|
|
138
|
+
this.targetHost = m[2];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
changed(incoming, tunnelId) {
|
|
143
|
+
const diff = (field, a, b) => {
|
|
144
|
+
logger_1.default.debug(`Tunnel '${tunnelId}' hasChanged: ${field} (running=${JSON.stringify(a)} incoming=${JSON.stringify(b)})`);
|
|
145
|
+
return true;
|
|
146
|
+
};
|
|
147
|
+
if (this.type !== incoming.type)
|
|
148
|
+
return diff('type', this.type, incoming.type);
|
|
149
|
+
if (this.target !== incoming.target)
|
|
150
|
+
return diff('target', this.target, incoming.target);
|
|
151
|
+
if (this.region !== incoming.region)
|
|
152
|
+
return diff('region', this.region, incoming.region);
|
|
153
|
+
if (this.timeout !== incoming.timeout)
|
|
154
|
+
return diff('timeout', this.timeout, incoming.timeout);
|
|
155
|
+
if (this.domain !== incoming.domain)
|
|
156
|
+
return diff('domain', this.domain, incoming.domain);
|
|
157
|
+
if (this.subdomain !== incoming.subdomain)
|
|
158
|
+
return diff('subdomain', this.subdomain, incoming.subdomain);
|
|
159
|
+
if (this.terminate !== incoming.terminate)
|
|
160
|
+
return diff('terminate', this.terminate, incoming.terminate);
|
|
161
|
+
if (this.key !== incoming.key)
|
|
162
|
+
return diff('key', this.key, incoming.key);
|
|
163
|
+
if (this.cert !== incoming.cert)
|
|
164
|
+
return diff('cert', this.cert, incoming.cert);
|
|
165
|
+
if (this.ca !== incoming.ca)
|
|
166
|
+
return diff('ca', this.ca, incoming.ca);
|
|
167
|
+
if (this.host !== incoming.host)
|
|
168
|
+
return diff('host', this.host, incoming.host);
|
|
169
|
+
if (this.login !== incoming.login)
|
|
170
|
+
return diff('login', this.login, incoming.login);
|
|
171
|
+
if (this.password !== incoming.password)
|
|
172
|
+
return diff('password', this.password, incoming.password);
|
|
173
|
+
if (this.authType !== incoming.authType)
|
|
174
|
+
return diff('authType', this.authType, incoming.authType);
|
|
175
|
+
if (this.serve !== incoming.serve)
|
|
176
|
+
return diff('serve', this.serve, incoming.serve);
|
|
177
|
+
if (this.circuitBreaker !== incoming.circuitBreaker)
|
|
178
|
+
return diff('circuitBreaker', this.circuitBreaker, incoming.circuitBreaker);
|
|
179
|
+
if (this.log !== incoming.log)
|
|
180
|
+
return diff('log', this.log, incoming.log);
|
|
181
|
+
if (this.verify !== incoming.verify)
|
|
182
|
+
return diff('verify', this.verify, incoming.verify);
|
|
183
|
+
if (this.http2 !== incoming.http2)
|
|
184
|
+
return diff('http2', this.http2, incoming.http2);
|
|
185
|
+
if (this.compression !== incoming.compression)
|
|
186
|
+
return diff('compression', this.compression, incoming.compression);
|
|
187
|
+
if (this.sshIp !== incoming.sshIp)
|
|
188
|
+
return diff('sshIp', this.sshIp, incoming.sshIp);
|
|
189
|
+
if (this.sshId !== incoming.sshId)
|
|
190
|
+
return diff('sshId', this.sshId, incoming.sshId);
|
|
191
|
+
if (this.sshPort !== incoming.sshPort)
|
|
192
|
+
return diff('sshPort', this.sshPort, incoming.sshPort);
|
|
193
|
+
if (this.sshUser !== incoming.sshUser)
|
|
194
|
+
return diff('sshUser', this.sshUser, incoming.sshUser);
|
|
195
|
+
if (this.sshPassword !== incoming.sshPassword)
|
|
196
|
+
return diff('sshPassword', this.sshPassword, incoming.sshPassword);
|
|
197
|
+
if (this.sshForwardPort !== incoming.sshForwardPort)
|
|
198
|
+
return diff('sshForwardPort', this.sshForwardPort, incoming.sshForwardPort);
|
|
199
|
+
if (this.sshClientPassword !== incoming.sshClientPassword)
|
|
200
|
+
return diff('sshClientPassword', this.sshClientPassword, incoming.sshClientPassword);
|
|
201
|
+
if (this.sshClientUser !== incoming.sshClientUser)
|
|
202
|
+
return diff('sshClientUser', this.sshClientUser, incoming.sshClientUser);
|
|
203
|
+
if (this._stringListChanged('whitelist', this.whitelist, incoming.whitelist, diff))
|
|
204
|
+
return true;
|
|
205
|
+
if (this._stringListChanged('useragents', this.useragents, incoming.useragents, diff))
|
|
206
|
+
return true;
|
|
207
|
+
if (this._headerListChanged('headers', this.headers, incoming.headers, diff))
|
|
208
|
+
return true;
|
|
209
|
+
if (this._headerListChanged('responseHeaders', this.responseHeaders, incoming.responseHeaders, diff))
|
|
210
|
+
return true;
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
_stringListChanged(field, running, incoming, diff) {
|
|
214
|
+
if (running?.length !== incoming?.length)
|
|
215
|
+
return diff(`${field}.length`, running, incoming);
|
|
216
|
+
if (running && incoming) {
|
|
217
|
+
const a = [...running].sort();
|
|
218
|
+
const b = [...incoming].sort();
|
|
219
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
220
|
+
if (a[i] !== b[i])
|
|
221
|
+
return diff(field, running, incoming);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
_headerListChanged(field, running, incoming, diff) {
|
|
227
|
+
if (running?.length !== incoming?.length)
|
|
228
|
+
return diff(`${field}.length`, running, incoming);
|
|
229
|
+
if (running && incoming) {
|
|
230
|
+
const sortHeaders = (a, b) => a.name.localeCompare(b.name);
|
|
231
|
+
const a = [...running].sort(sortHeaders);
|
|
232
|
+
const b = [...incoming].sort(sortHeaders);
|
|
233
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
234
|
+
if (a[i].name !== b[i].name || a[i].value !== b[i].value)
|
|
235
|
+
return diff(field, running, incoming);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
exports.default = TunnelConfig;
|