bdy 1.14.1-dev → 1.14.2-dev

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.
@@ -1,446 +0,0 @@
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 events_1 = __importDefault(require("events"));
7
- const ssh2_1 = __importDefault(require("ssh2"));
8
- const logger_js_1 = __importDefault(require("../logger.js"));
9
- const crypto_1 = require("crypto");
10
- const child_process_1 = require("child_process");
11
- const node_net_1 = __importDefault(require("node:net"));
12
- const sftp_1 = __importDefault(require("./sftp"));
13
- const buddy_1 = __importDefault(require("../api/buddy"));
14
- const pipeStreamToChannel = (stream, channel) => {
15
- channel.once('exit', (code, signal) => {
16
- logger_js_1.default.debug(`shell channel exit: ${code}, signal: ${signal}`);
17
- if (stream.exit)
18
- stream.exit(signal || code);
19
- });
20
- channel.on('close', () => {
21
- stream.end();
22
- });
23
- channel.pipe(stream, { end: false }).pipe(channel);
24
- channel.stderr.pipe(stream.stderr);
25
- };
26
- class ServerSsh extends events_1.default {
27
- constructor(agent, login, password, hostKey) {
28
- super();
29
- this.agent = agent;
30
- this.login = login;
31
- this.password = password;
32
- this.server = new ssh2_1.default.Server({
33
- keepAlive: true,
34
- hostKeys: [hostKey],
35
- highWaterMark: 16 * 1024 * 1024,
36
- ident: 'ssh2 server',
37
- }, (client) => this.processClient(client));
38
- this.server.listen();
39
- }
40
- stop() {
41
- this.server.close();
42
- this.server.removeAllListeners();
43
- this.server = null;
44
- }
45
- handleSshTunnel(stream) {
46
- this.server.injectSocket(stream);
47
- }
48
- checkValueSafe(input, allowed) {
49
- const autoReject = input.length !== allowed.length;
50
- if (autoReject)
51
- allowed = input;
52
- const isMatch = (0, crypto_1.timingSafeEqual)(input, allowed);
53
- return (!autoReject && isMatch);
54
- }
55
- async verifyKey(ctx, keys) {
56
- try {
57
- for (let i = 0; i < keys.length; i += 1) {
58
- const publicKey = ssh2_1.default.utils.parseKey(keys[i]);
59
- if (ctx.key.algo !== publicKey.type)
60
- continue;
61
- if (!this.checkValueSafe(ctx.key.data, publicKey.getPublicSSH()))
62
- continue;
63
- if (ctx.signature && !publicKey.verify(ctx.blob, ctx.signature, ctx.hashAlgo))
64
- continue;
65
- return true;
66
- }
67
- }
68
- catch {
69
- // do nothing
70
- }
71
- return false;
72
- }
73
- createProxyConnection(privateKey) {
74
- return new Promise((resolve, reject) => {
75
- const client = new ssh2_1.default.Client();
76
- client.once('error', (err) => {
77
- client.removeAllListeners();
78
- reject(err);
79
- });
80
- client.once('ready', () => {
81
- client.removeAllListeners();
82
- resolve(client);
83
- });
84
- client.connect({
85
- host: '127.0.0.1',
86
- port: 22,
87
- username: 'root',
88
- privateKey: privateKey
89
- });
90
- });
91
- }
92
- async authenticateClient(ctx) {
93
- const allowed = ['publickey', 'password'];
94
- logger_js_1.default.debug(`auth method: ${ctx.method}`);
95
- if (!allowed.includes(ctx.method)) {
96
- ctx.reject(allowed);
97
- return null;
98
- }
99
- if (ctx.method === 'password') {
100
- if (!this.checkValueSafe(Buffer.from(ctx.username), Buffer.from(this.login))) {
101
- ctx.reject(allowed);
102
- return null;
103
- }
104
- if (!this.checkValueSafe(Buffer.from(ctx.password), Buffer.from(this.password))) {
105
- ctx.reject(allowed);
106
- return null;
107
- }
108
- ctx.accept();
109
- return;
110
- }
111
- if (ctx.method === 'publickey') {
112
- let keys;
113
- let privateKey;
114
- try {
115
- const resp = await buddy_1.default.fetchAgentKeys(this.agent.id, this.agent.host, this.agent.token);
116
- logger_js_1.default.debug(resp);
117
- keys = resp.keys;
118
- privateKey = resp.privateKey;
119
- }
120
- catch {
121
- ctx.reject(allowed);
122
- return null;
123
- }
124
- const verified = await this.verifyKey(ctx, keys);
125
- if (!verified) {
126
- logger_js_1.default.debug('not verified');
127
- ctx.reject(allowed);
128
- return null;
129
- }
130
- logger_js_1.default.debug('verified');
131
- try {
132
- const proxyClient = await this.createProxyConnection(privateKey);
133
- logger_js_1.default.debug('proxy connected');
134
- ctx.accept();
135
- return proxyClient;
136
- }
137
- catch (err) {
138
- logger_js_1.default.debug('proxy not connected');
139
- logger_js_1.default.debug(err);
140
- ctx.reject(allowed);
141
- return null;
142
- }
143
- }
144
- ctx.reject(allowed);
145
- return null;
146
- }
147
- localExec(stream, command, env) {
148
- const s = process.hrtime();
149
- (0, child_process_1.exec)(command, {
150
- env,
151
- }, (err, stdout, stderr) => {
152
- if (stream) {
153
- stream.stderr.write(stderr);
154
- stream.write(stdout);
155
- stream.exit(!err ? 0 : 1);
156
- stream.end();
157
- stream = null;
158
- }
159
- const [seconds, nano] = process.hrtime(s);
160
- const ms = seconds * 1000 + nano / 1000 / 1000;
161
- logger_js_1.default.debug(`local ssh exec ends: ${ms}ms`);
162
- });
163
- }
164
- clientSession(session, client, proxyClient) {
165
- let env = {};
166
- let sftp;
167
- let channel;
168
- let pty;
169
- let x11;
170
- const closeSession = () => {
171
- logger_js_1.default.debug('ssh close session');
172
- env = null;
173
- pty = null;
174
- x11 = null;
175
- session.removeAllListeners();
176
- session = null;
177
- if (sftp) {
178
- sftp.destroy();
179
- sftp = null;
180
- }
181
- if (channel) {
182
- channel.removeAllListeners();
183
- channel = null;
184
- }
185
- client.removeListener('close', closeSession);
186
- client = null;
187
- };
188
- client.on('close', closeSession);
189
- session.on('close', closeSession);
190
- session.on('end', closeSession);
191
- session.on('pty', (accept, reject, info) => {
192
- logger_js_1.default.debug('ssh pty');
193
- logger_js_1.default.debug(info);
194
- if (!proxyClient) {
195
- if (reject)
196
- reject();
197
- return;
198
- }
199
- pty = info;
200
- if (accept)
201
- accept();
202
- });
203
- session.on('window-change', (accept, reject, info) => {
204
- logger_js_1.default.debug('ssh window-change');
205
- logger_js_1.default.debug(info);
206
- if (!proxyClient) {
207
- if (reject)
208
- reject();
209
- return;
210
- }
211
- if (!pty)
212
- pty = {};
213
- pty.cols = info.cols;
214
- pty.height = info.height;
215
- pty.rows = info.rows;
216
- pty.width = info.width;
217
- if (channel && channel.setWindow)
218
- channel.setWindow(pty.rows, pty.cols, pty.height, pty.width);
219
- if (accept)
220
- accept();
221
- });
222
- session.on('shell', (accept, reject) => {
223
- logger_js_1.default.debug('ssh shell');
224
- if (!proxyClient || !accept) {
225
- if (reject)
226
- reject();
227
- return;
228
- }
229
- const stream = accept();
230
- proxyClient.shell(pty, {
231
- env,
232
- x11
233
- }, (err, c) => {
234
- if (err || !c) {
235
- if (reject)
236
- reject();
237
- return;
238
- }
239
- pipeStreamToChannel(stream, c);
240
- });
241
- });
242
- session.on('signal', (accept, reject, info) => {
243
- logger_js_1.default.debug('ssh signal');
244
- logger_js_1.default.debug(info);
245
- const { name } = info;
246
- if (!proxyClient || !name || !channel || !channel.signal) {
247
- if (reject)
248
- reject();
249
- return;
250
- }
251
- channel.signal(name);
252
- if (accept)
253
- accept();
254
- });
255
- session.on('subsystem', (accept, reject, info) => {
256
- logger_js_1.default.debug('ssh subsystem');
257
- logger_js_1.default.debug(info);
258
- const { name } = info;
259
- if (!proxyClient || !accept || !name) {
260
- if (reject)
261
- reject();
262
- return;
263
- }
264
- const stream = accept();
265
- proxyClient.subsys(name, (err, c) => {
266
- if (err || !c) {
267
- if (reject)
268
- reject();
269
- return;
270
- }
271
- pipeStreamToChannel(stream, c);
272
- });
273
- });
274
- session.on('x11', (accept, reject, info) => {
275
- logger_js_1.default.debug('ssh x11');
276
- logger_js_1.default.debug(info);
277
- if (!proxyClient) {
278
- if (reject)
279
- reject();
280
- return;
281
- }
282
- x11 = info;
283
- if (accept)
284
- accept();
285
- });
286
- session.on('exec', (accept, reject, info) => {
287
- logger_js_1.default.debug('ssh exec');
288
- logger_js_1.default.debug(info);
289
- if (!info.command || !accept) {
290
- if (reject)
291
- reject();
292
- return;
293
- }
294
- const stream = accept();
295
- if (!proxyClient) {
296
- this.localExec(stream, info.command, env);
297
- return;
298
- }
299
- proxyClient.exec(info.command, {
300
- env,
301
- pty,
302
- x11
303
- }, (err, c) => {
304
- if (err || !c) {
305
- if (reject)
306
- reject();
307
- return;
308
- }
309
- pipeStreamToChannel(stream, c);
310
- });
311
- });
312
- session.on('env', (accept, reject, info) => {
313
- logger_js_1.default.debug('ssh env');
314
- logger_js_1.default.debug(info);
315
- Object.keys(info || {}).forEach((key) => {
316
- env[key] = info[key];
317
- });
318
- if (accept)
319
- accept();
320
- });
321
- session.on('sftp', (accept, reject) => {
322
- logger_js_1.default.debug('sftp');
323
- if (!sftp && accept) {
324
- sftp = new sftp_1.default(accept());
325
- }
326
- else if (reject) {
327
- reject();
328
- }
329
- });
330
- }
331
- processClient(client) {
332
- logger_js_1.default.debug('new ssh client');
333
- let proxyClient;
334
- client.setNoDelay();
335
- client.on('authentication', async (ctx) => {
336
- logger_js_1.default.debug('ssh authentication');
337
- proxyClient = await this.authenticateClient(ctx);
338
- if (proxyClient) {
339
- proxyClient.on('tcp connection', (details, accept, reject) => {
340
- logger_js_1.default.debug('new ssh client forward connection');
341
- logger_js_1.default.debug(details);
342
- const { destIP, destPort, srcIP, srcPort } = details;
343
- client.forwardOut(destIP, destPort, srcIP, srcPort, (err, channel) => {
344
- logger_js_1.default.debug('new ssh client forward out connection');
345
- logger_js_1.default.debug({ destIP, destPort, srcIP, srcPort });
346
- if (err) {
347
- reject();
348
- return;
349
- }
350
- const stream = accept();
351
- channel.pipe(stream).pipe(channel);
352
- });
353
- });
354
- }
355
- });
356
- client.on('close', () => {
357
- logger_js_1.default.debug('ssh close');
358
- if (proxyClient) {
359
- try {
360
- proxyClient.removeAllListeners();
361
- proxyClient.end();
362
- }
363
- catch {
364
- // do nothing
365
- }
366
- proxyClient = null;
367
- }
368
- setTimeout(() => {
369
- client.removeAllListeners();
370
- client = null;
371
- }, 1000);
372
- });
373
- client.on('tcpip', (accept, reject, info) => {
374
- logger_js_1.default.debug('ssh tcpip request');
375
- logger_js_1.default.debug(info);
376
- const { destIP, destPort } = info;
377
- let accepted = false;
378
- const socket = node_net_1.default.connect({
379
- host: destIP,
380
- port: destPort
381
- });
382
- socket.on('connect', () => {
383
- accepted = true;
384
- const channel = accept();
385
- channel.pipe(socket).pipe(channel);
386
- });
387
- const closeSocket = () => {
388
- try {
389
- socket.removeAllListeners();
390
- socket.end();
391
- }
392
- catch {
393
- // do nothing
394
- }
395
- if (!accepted)
396
- reject();
397
- };
398
- socket.setTimeout(10000);
399
- socket.on('timeout', closeSocket);
400
- socket.on('error', closeSocket);
401
- socket.on('close', closeSocket);
402
- });
403
- client.on('request', (accept, reject, name, info) => {
404
- logger_js_1.default.debug('ssh forward request');
405
- logger_js_1.default.debug(name);
406
- logger_js_1.default.debug(info);
407
- if (!proxyClient) {
408
- reject();
409
- return;
410
- }
411
- if (name === 'tcpip-forward') {
412
- let { bindAddr, bindPort } = info;
413
- proxyClient.forwardIn(bindAddr, bindPort, (err, port) => {
414
- if (err) {
415
- reject();
416
- return;
417
- }
418
- accept(port);
419
- });
420
- return;
421
- }
422
- if (name === 'cancel-tcpip-forward') {
423
- const { bindAddr, bindPort } = info;
424
- proxyClient.unforwardIn(bindAddr, bindPort, (err) => {
425
- if (err) {
426
- reject();
427
- return;
428
- }
429
- accept();
430
- });
431
- return;
432
- }
433
- reject();
434
- });
435
- client.on('error', (err) => {
436
- logger_js_1.default.debug('Error on ssh server client');
437
- logger_js_1.default.debug(err);
438
- client.end();
439
- });
440
- client.on('session', (accept) => {
441
- logger_js_1.default.debug('ssh session');
442
- this.clientSession(accept(), client, proxyClient);
443
- });
444
- }
445
- }
446
- exports.default = ServerSsh;
@@ -1,41 +0,0 @@
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 events_1 = __importDefault(require("events"));
7
- const tls_1 = __importDefault(require("tls"));
8
- const cert_js_1 = __importDefault(require("./cert.js"));
9
- const utils_1 = require("../utils");
10
- class ServerTls extends events_1.default {
11
- constructor(key, cert, ca) {
12
- super();
13
- this.key = key;
14
- this.cert = cert;
15
- this.ca = ca;
16
- if (!this.key || !this.cert) {
17
- const c = new cert_js_1.default();
18
- this.key = c.key;
19
- this.cert = c.cert;
20
- }
21
- this.server = tls_1.default.createServer({
22
- handshakeTimeout: 60000,
23
- key: this.key,
24
- cert: this.cert,
25
- ca: this.ca,
26
- rejectUnauthorized: !!this.ca,
27
- requestCert: !!this.ca,
28
- }, (socket) => this.processSocket(socket));
29
- }
30
- async processSocket(socket) {
31
- this.emit(utils_1.TLS_SOCKET, socket);
32
- }
33
- handleSshTunnel(channel) {
34
- this.server.emit('connection', channel);
35
- }
36
- stop() {
37
- this.server.removeAllListeners();
38
- this.server = null;
39
- }
40
- }
41
- exports.default = ServerTls;
@@ -1,197 +0,0 @@
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 ssh2_1 = require("ssh2");
7
- const events_1 = __importDefault(require("events"));
8
- const logger_js_1 = __importDefault(require("../logger.js"));
9
- const texts_js_1 = require("../texts.js");
10
- const utils_1 = require("../utils");
11
- class SshClient extends events_1.default {
12
- constructor(ip, port, username, password) {
13
- super();
14
- this.ip = ip;
15
- this.port = port;
16
- this.username = username;
17
- this.password = password;
18
- this.keepalive = false;
19
- logger_js_1.default.debug(`New ssh client: ${username}:${password}@${ip}:${port}`);
20
- }
21
- async open() {
22
- return new Promise((resolve, reject) => {
23
- this.close();
24
- this.client = new ssh2_1.Client();
25
- this.client.setNoDelay();
26
- this.client.on('ready', () => {
27
- this.client.removeAllListeners();
28
- this.client.on('error', () => { });
29
- resolve();
30
- });
31
- this.client.once('error', (err) => {
32
- this.client.removeAllListeners();
33
- reject(err);
34
- });
35
- try {
36
- this.client.connect({
37
- algorithms: {
38
- cipher: ['aes256-gcm@openssh.com'],
39
- compress: ['none'],
40
- hmac: ['hmac-sha2-256-etm@openssh.com'],
41
- kex: ['ecdh-sha2-nistp256'],
42
- },
43
- keepaliveInterval: 10000,
44
- host: this.ip,
45
- port: this.port,
46
- username: this.username,
47
- password: this.password,
48
- });
49
- }
50
- catch (err) {
51
- reject(err);
52
- }
53
- });
54
- }
55
- waitForStreamReadable(s) {
56
- return new Promise((resolve) => {
57
- s.once('readable', resolve);
58
- });
59
- }
60
- async readStreamBytes(s, toRead) {
61
- await this.waitForStreamReadable(s);
62
- const data = s.read(toRead) ?? s.read();
63
- if (data === null) {
64
- return this.readStreamBytes(s, toRead);
65
- }
66
- const newToRead = toRead - data.length;
67
- if (newToRead <= 0)
68
- return data;
69
- const newData = await this.readStreamBytes(s, newToRead);
70
- return Buffer.concat([data, newData]);
71
- }
72
- async processStream(stream, info) {
73
- stream.allowHalfOpen = false;
74
- if (stream.readableFlowing)
75
- stream.pause();
76
- stream.on('error', () => {
77
- try {
78
- stream.removeAllListeners();
79
- stream.once('error', () => { });
80
- stream.end();
81
- }
82
- catch {
83
- // do nothing
84
- }
85
- setTimeout(() => {
86
- try {
87
- stream.destroy();
88
- }
89
- catch {
90
- // do nothing
91
- }
92
- }, 1000);
93
- });
94
- stream.on('close', () => {
95
- stream.removeAllListeners();
96
- });
97
- const triggerStream = (type, ip) => {
98
- stream.removeAllListeners();
99
- this.emit(type, stream, info, ip);
100
- };
101
- let type;
102
- let ip;
103
- try {
104
- const b = await this.readStreamBytes(stream, 50);
105
- const s = b.toString('utf8').replace(/\s+/, ' ').split(' ');
106
- if (s.length !== 2)
107
- throw new Error(texts_js_1.ERR_WRONG_HANDSHAKE);
108
- type = parseInt(s[0]);
109
- ip = s[1];
110
- }
111
- catch (err) {
112
- stream.emit('error', err);
113
- return;
114
- }
115
- if (type === utils_1.SOCKET_TYPE_TLS) {
116
- logger_js_1.default.debug((0, texts_js_1.LOG_DETECTED_STREAM)('TLS'));
117
- triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_TLS, ip);
118
- return;
119
- }
120
- if (type === utils_1.SOCKET_TYPE_HTTP1) {
121
- logger_js_1.default.debug((0, texts_js_1.LOG_DETECTED_STREAM)('HTTP1'));
122
- triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_HTTP1, ip);
123
- return;
124
- }
125
- if (type === utils_1.SOCKET_TYPE_HTTP2) {
126
- logger_js_1.default.debug((0, texts_js_1.LOG_DETECTED_STREAM)('HTTP2'));
127
- triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_HTTP2, ip);
128
- return;
129
- }
130
- if (type === utils_1.SOCKET_TYPE_TCP) {
131
- logger_js_1.default.debug((0, texts_js_1.LOG_DETECTED_STREAM)('TCP'));
132
- triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_TCP, ip);
133
- return;
134
- }
135
- if (type === utils_1.SOCKET_TYPE_SSH) {
136
- logger_js_1.default.debug((0, texts_js_1.LOG_DETECTED_STREAM)('SSH'));
137
- triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_SSH, ip);
138
- return;
139
- }
140
- logger_js_1.default.debug(texts_js_1.LOG_WRONG_STREAM);
141
- stream.emit('error', new Error(texts_js_1.ERR_WRONG_STREAM));
142
- }
143
- forwardIn(port) {
144
- return new Promise((resolve, reject) => {
145
- this.client.on('tcp connection', async (info, accept) => {
146
- logger_js_1.default.debug(texts_js_1.LOG_SSH_CONNECTION);
147
- return this.processStream(accept(), info);
148
- });
149
- this.client.forwardIn('0.0.0.0', port, (err) => {
150
- if (err)
151
- reject(err);
152
- else
153
- resolve(err);
154
- });
155
- });
156
- }
157
- closeKeepAlive() {
158
- this.keepalive = false;
159
- this.close();
160
- }
161
- openKeepAlive() {
162
- logger_js_1.default.debug(`SSH client open`);
163
- this.keepalive = true;
164
- this.open()
165
- .then(() => {
166
- logger_js_1.default.debug(`SSH client opened`);
167
- this.emit(utils_1.SSH_CLIENT_EVENT_CONNECTED);
168
- this.client.on('close', () => {
169
- logger_js_1.default.debug(`SSH client closed`);
170
- if (this.keepalive)
171
- this.openKeepAlive();
172
- });
173
- })
174
- .catch(() => {
175
- logger_js_1.default.debug(`SSH client closed`);
176
- this.emit(utils_1.SSH_CLIENT_EVENT_DISCONNECTED);
177
- setTimeout(() => {
178
- if (this.keepalive)
179
- this.openKeepAlive();
180
- }, 3000);
181
- });
182
- }
183
- close() {
184
- if (this.client) {
185
- try {
186
- this.client.removeAllListeners();
187
- this.client.on('error', () => { });
188
- this.client.end();
189
- }
190
- catch {
191
- // nic nie rob
192
- }
193
- this.client = null;
194
- }
195
- }
196
- }
197
- exports.default = SshClient;