bdy 1.9.23-dev → 1.9.25-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,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.9.23-dev",
4
+ "version": "1.9.25-dev",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -42,7 +42,7 @@
42
42
  "socket.io-client": "4.7.5",
43
43
  "ssh2": "1.15.0",
44
44
  "terminal-kit": "3.1.1",
45
- "undici": "6.19.2",
45
+ "undici": "6.21.2",
46
46
  "uuid": "10.0.0",
47
47
  "which": "4.0.0",
48
48
  "ws": "8.18.0",
@@ -39,68 +39,292 @@ class ServerSsh extends events_1.default {
39
39
  const isMatch = (0, crypto_1.timingSafeEqual)(input, allowed);
40
40
  return (!autoReject && isMatch);
41
41
  }
42
- async verifyKey(ctx) {
42
+ async verifyKey(ctx, keys) {
43
43
  try {
44
- logger_js_1.default.info('1');
45
- const { keys } = await buddy_1.default.fetchAgentKeys(this.agent.id, this.agent.host, this.agent.token);
46
44
  for (let i = 0; i < keys.length; i += 1) {
47
- logger_js_1.default.info('2');
48
45
  const publicKey = ssh2_1.default.utils.parseKey(keys[i]);
49
46
  if (ctx.key.algo !== publicKey.type)
50
47
  continue;
51
- logger_js_1.default.info('3');
52
48
  if (!this.checkValueSafe(ctx.key.data, publicKey.getPublicSSH()))
53
49
  continue;
54
- logger_js_1.default.info('4');
55
50
  if (ctx.signature && !publicKey.verify(ctx.blob, ctx.signature, ctx.hashAlgo))
56
51
  continue;
57
- logger_js_1.default.info('5');
58
52
  return true;
59
53
  }
60
54
  }
61
- catch (err) {
62
- logger_js_1.default.info('6');
63
- logger_js_1.default.info(err);
55
+ catch {
64
56
  // do nothing
65
57
  }
66
- logger_js_1.default.info('7');
67
58
  return false;
68
59
  }
69
- processClient(client) {
70
- client.setNoDelay();
71
- client.on('authentication', async (ctx) => {
72
- const allowed = ['publickey', 'password'];
73
- if (!allowed.includes(ctx.method)) {
74
- ctx.reject(allowed);
60
+ createProxyConnection(privateKey) {
61
+ return new Promise((resolve, reject) => {
62
+ const client = new ssh2_1.default.Client();
63
+ client.once('error', (err) => {
64
+ client.removeAllListeners();
65
+ reject(err);
66
+ });
67
+ client.once('ready', () => {
68
+ client.removeAllListeners();
69
+ resolve(client);
70
+ });
71
+ client.connect({
72
+ host: '127.0.0.1',
73
+ port: 22,
74
+ username: 'root',
75
+ privateKey: privateKey
76
+ });
77
+ });
78
+ }
79
+ async authenticateClient(ctx) {
80
+ const allowed = ['publickey', 'password'];
81
+ if (!allowed.includes(ctx.method)) {
82
+ ctx.reject(allowed);
83
+ return null;
84
+ }
85
+ if (ctx.method === 'password') {
86
+ if (!this.checkValueSafe(Buffer.from(ctx.username), Buffer.from(this.login))) {
87
+ ctx.reject();
88
+ return null;
89
+ }
90
+ if (!this.checkValueSafe(Buffer.from(ctx.password), Buffer.from(this.password))) {
91
+ ctx.reject();
92
+ return null;
93
+ }
94
+ ctx.accept();
95
+ return;
96
+ }
97
+ if (ctx.method === 'publickey') {
98
+ logger_js_1.default.info('1');
99
+ let keys;
100
+ let privateKey;
101
+ try {
102
+ const resp = await buddy_1.default.fetchAgentKeys(this.agent.id, this.agent.host, this.agent.token);
103
+ keys = resp.keys;
104
+ privateKey = resp.privateKey;
105
+ logger_js_1.default.info('2');
106
+ logger_js_1.default.info(privateKey);
107
+ }
108
+ catch {
109
+ logger_js_1.default.info('3');
110
+ ctx.reject();
111
+ return null;
112
+ }
113
+ logger_js_1.default.info('4');
114
+ const verified = await this.verifyKey(ctx, keys);
115
+ if (!verified) {
116
+ logger_js_1.default.info('5');
117
+ ctx.reject();
118
+ return null;
119
+ }
120
+ logger_js_1.default.info('6');
121
+ try {
122
+ const proxyClient = await this.createProxyConnection(privateKey);
123
+ logger_js_1.default.info('7');
124
+ ctx.accept();
125
+ return proxyClient;
126
+ }
127
+ catch {
128
+ logger_js_1.default.info('8');
129
+ ctx.reject();
130
+ return null;
131
+ }
132
+ }
133
+ ctx.reject();
134
+ return null;
135
+ }
136
+ localExec(stream, command, env) {
137
+ const s = process.hrtime();
138
+ (0, child_process_1.exec)(command, {
139
+ env,
140
+ }, (err, stdout, stderr) => {
141
+ if (stream) {
142
+ stream.stderr.write(stderr);
143
+ stream.write(stdout);
144
+ stream.exit(!err ? 0 : 1);
145
+ stream.end();
146
+ stream = null;
147
+ }
148
+ const [seconds, nano] = process.hrtime(s);
149
+ const ms = seconds * 1000 + nano / 1000 / 1000;
150
+ logger_js_1.default.debug(`local ssh exec ends: ${ms}ms`);
151
+ });
152
+ }
153
+ clientSession(session, client, proxyClient) {
154
+ let env = {};
155
+ let sftp;
156
+ let channel;
157
+ let pty;
158
+ let x11;
159
+ const closeSession = () => {
160
+ env = null;
161
+ pty = null;
162
+ x11 = null;
163
+ session.removeAllListeners();
164
+ session = null;
165
+ if (sftp) {
166
+ sftp.destroy();
167
+ sftp = null;
168
+ }
169
+ if (channel) {
170
+ channel.removeAllListeners();
171
+ channel = null;
172
+ }
173
+ if (client) {
174
+ client.end();
175
+ }
176
+ };
177
+ session.on('close', closeSession);
178
+ session.on('end', closeSession);
179
+ session.on('pty', (accept, reject, info) => {
180
+ if (!proxyClient) {
181
+ if (reject)
182
+ reject();
183
+ return;
184
+ }
185
+ pty = info;
186
+ if (accept)
187
+ accept();
188
+ });
189
+ session.on('window-change', (accept, reject, info) => {
190
+ if (!proxyClient) {
191
+ if (reject)
192
+ reject();
193
+ return;
194
+ }
195
+ if (!pty)
196
+ pty = {};
197
+ pty.cols = info.cols;
198
+ pty.height = info.height;
199
+ pty.rows = info.rows;
200
+ pty.width = info.width;
201
+ if (channel && channel.setWindow)
202
+ channel.setWindow(pty.rows, pty.cols, pty.height, pty.width);
203
+ if (accept)
204
+ accept();
205
+ });
206
+ session.on('shell', (accept, reject) => {
207
+ if (!proxyClient || !accept) {
208
+ if (reject)
209
+ reject();
75
210
  return;
76
211
  }
77
- if (ctx.method === 'password') {
78
- if (!this.checkValueSafe(Buffer.from(ctx.username), Buffer.from(this.login))) {
79
- ctx.reject();
212
+ const stream = accept();
213
+ proxyClient.shell(pty, {
214
+ env,
215
+ x11
216
+ }, (err, c) => {
217
+ if (err || !c) {
218
+ if (reject)
219
+ reject();
80
220
  return;
81
221
  }
82
- if (!this.checkValueSafe(Buffer.from(ctx.password), Buffer.from(this.password))) {
83
- ctx.reject();
222
+ channel = c;
223
+ stream.pipe(channel);
224
+ channel.pipe(stream);
225
+ });
226
+ });
227
+ session.on('signal', (accept, reject, info) => {
228
+ const { name } = info;
229
+ if (!proxyClient || !name || !channel || !channel.signal) {
230
+ if (reject)
231
+ reject();
232
+ return;
233
+ }
234
+ channel.signal(name);
235
+ if (accept)
236
+ accept();
237
+ });
238
+ session.on('subsystem', (accept, reject, info) => {
239
+ const { name } = info;
240
+ if (!proxyClient || !accept || !name) {
241
+ if (reject)
242
+ reject();
243
+ return;
244
+ }
245
+ const stream = accept();
246
+ proxyClient.subsys(name, (err, c) => {
247
+ if (err || !c) {
248
+ if (reject)
249
+ reject();
84
250
  return;
85
251
  }
86
- ctx.accept();
252
+ channel = c;
253
+ stream.pipe(channel);
254
+ channel.pipe(stream);
255
+ });
256
+ });
257
+ session.on('x11', (accept, reject, info) => {
258
+ if (!proxyClient) {
259
+ if (reject)
260
+ reject();
261
+ return;
262
+ }
263
+ x11 = info;
264
+ if (accept)
265
+ accept();
266
+ });
267
+ session.on('exec', (accept, reject, info) => {
268
+ if (!info.command || !accept) {
269
+ if (reject)
270
+ reject();
87
271
  return;
88
272
  }
89
- if (ctx.method === 'publickey') {
90
- const verified = await this.verifyKey(ctx);
91
- logger_js_1.default.info(`verified: ${verified}`);
92
- if (!verified) {
93
- ctx.reject();
273
+ const stream = accept();
274
+ if (!proxyClient) {
275
+ this.localExec(stream, info.command, env);
276
+ return;
277
+ }
278
+ proxyClient.exec(info.command, {
279
+ env,
280
+ pty,
281
+ x11
282
+ }, (err, c) => {
283
+ if (err || !c) {
284
+ if (reject)
285
+ reject();
94
286
  return;
95
287
  }
96
- ctx.accept();
97
- return;
288
+ channel = c;
289
+ stream.pipe(channel);
290
+ channel.pipe(stream);
291
+ });
292
+ });
293
+ session.on('env', (accept, reject, info) => {
294
+ Object.keys(info || {}).forEach((key) => {
295
+ env[key] = info[key];
296
+ });
297
+ if (accept)
298
+ accept();
299
+ });
300
+ session.on('sftp', (accept, reject) => {
301
+ if (!sftp && accept) {
302
+ sftp = new sftp_1.default(accept());
303
+ }
304
+ else if (reject) {
305
+ reject();
98
306
  }
99
- ctx.reject();
307
+ });
308
+ }
309
+ processClient(client) {
310
+ let proxyClient;
311
+ client.setNoDelay();
312
+ client.on('authentication', async (ctx) => {
313
+ proxyClient = await this.authenticateClient(ctx);
100
314
  });
101
315
  client.on('close', () => {
102
316
  client.removeAllListeners();
103
317
  client = null;
318
+ if (proxyClient) {
319
+ try {
320
+ proxyClient.removeAllListeners();
321
+ proxyClient.end();
322
+ }
323
+ catch {
324
+ // do nothing
325
+ }
326
+ proxyClient = null;
327
+ }
104
328
  });
105
329
  client.on('error', (err) => {
106
330
  logger_js_1.default.debug('Error on ssh server client');
@@ -108,61 +332,7 @@ class ServerSsh extends events_1.default {
108
332
  client.end();
109
333
  });
110
334
  client.on('session', (accept) => {
111
- let session = accept();
112
- session.env = {};
113
- const closeSession = () => {
114
- session.removeAllListeners();
115
- if (session.sftp) {
116
- session.sftp.destroy();
117
- session.sftp = null;
118
- }
119
- session = null;
120
- if (client)
121
- client.end();
122
- };
123
- session.on('close', closeSession);
124
- session.on('end', closeSession);
125
- session.on('exec', (accept, reject, info) => {
126
- logger_js_1.default.debug('sftp exec');
127
- const s = process.hrtime();
128
- if (!info.command) {
129
- if (reject)
130
- reject();
131
- return;
132
- }
133
- let stream;
134
- if (accept)
135
- stream = accept();
136
- (0, child_process_1.exec)(info.command, {
137
- env: session.env,
138
- }, (err, stdout, stderr) => {
139
- if (stream) {
140
- stream.stderr.write(stderr);
141
- stream.write(stdout);
142
- stream.exit(!err ? 0 : 1);
143
- stream.end();
144
- stream = null;
145
- }
146
- const [seconds, nano] = process.hrtime(s);
147
- const ms = seconds * 1000 + nano / 1000 / 1000;
148
- logger_js_1.default.debug(`sftp exec ends: ${ms}ms`);
149
- });
150
- });
151
- session.on('env', (accept, reject, info) => {
152
- Object.keys(info || {}).forEach((key) => {
153
- session.env[key] = info[key];
154
- });
155
- if (accept)
156
- accept();
157
- });
158
- session.on('sftp', (accept, reject) => {
159
- if (!session.sftp) {
160
- session.sftp = new sftp_1.default(accept());
161
- }
162
- else if (reject) {
163
- reject();
164
- }
165
- });
335
+ this.clientSession(accept(), client, proxyClient);
166
336
  });
167
337
  }
168
338
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.9.23-dev",
4
+ "version": "1.9.25-dev",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -42,7 +42,7 @@
42
42
  "socket.io-client": "4.7.5",
43
43
  "ssh2": "1.15.0",
44
44
  "terminal-kit": "3.1.1",
45
- "undici": "6.19.2",
45
+ "undici": "6.21.2",
46
46
  "uuid": "10.0.0",
47
47
  "which": "4.0.0",
48
48
  "ws": "8.18.0",