bdy 1.9.24-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.24-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,9 +39,8 @@ 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
- const { keys } = await buddy_1.default.fetchAgentKeys(this.agent.id, this.agent.host, this.agent.token);
45
44
  for (let i = 0; i < keys.length; i += 1) {
46
45
  const publicKey = ssh2_1.default.utils.parseKey(keys[i]);
47
46
  if (ctx.key.algo !== publicKey.type)
@@ -58,40 +57,274 @@ class ServerSsh extends events_1.default {
58
57
  }
59
58
  return false;
60
59
  }
61
- processClient(client) {
62
- client.setNoDelay();
63
- client.on('authentication', async (ctx) => {
64
- const allowed = ['publickey', 'password'];
65
- if (!allowed.includes(ctx.method)) {
66
- 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();
67
210
  return;
68
211
  }
69
- if (ctx.method === 'password') {
70
- if (!this.checkValueSafe(Buffer.from(ctx.username), Buffer.from(this.login))) {
71
- 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();
72
220
  return;
73
221
  }
74
- if (!this.checkValueSafe(Buffer.from(ctx.password), Buffer.from(this.password))) {
75
- 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();
76
250
  return;
77
251
  }
78
- 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();
271
+ return;
272
+ }
273
+ const stream = accept();
274
+ if (!proxyClient) {
275
+ this.localExec(stream, info.command, env);
79
276
  return;
80
277
  }
81
- if (ctx.method === 'publickey') {
82
- const verified = await this.verifyKey(ctx);
83
- if (!verified) {
84
- ctx.reject();
278
+ proxyClient.exec(info.command, {
279
+ env,
280
+ pty,
281
+ x11
282
+ }, (err, c) => {
283
+ if (err || !c) {
284
+ if (reject)
285
+ reject();
85
286
  return;
86
287
  }
87
- ctx.accept();
88
- 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());
89
303
  }
90
- ctx.reject();
304
+ else if (reject) {
305
+ reject();
306
+ }
307
+ });
308
+ }
309
+ processClient(client) {
310
+ let proxyClient;
311
+ client.setNoDelay();
312
+ client.on('authentication', async (ctx) => {
313
+ proxyClient = await this.authenticateClient(ctx);
91
314
  });
92
315
  client.on('close', () => {
93
316
  client.removeAllListeners();
94
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
+ }
95
328
  });
96
329
  client.on('error', (err) => {
97
330
  logger_js_1.default.debug('Error on ssh server client');
@@ -99,88 +332,7 @@ class ServerSsh extends events_1.default {
99
332
  client.end();
100
333
  });
101
334
  client.on('session', (accept) => {
102
- let session = accept();
103
- session.env = {};
104
- const closeSession = () => {
105
- session.removeAllListeners();
106
- if (session.sftp) {
107
- session.sftp.destroy();
108
- session.sftp = null;
109
- }
110
- session = null;
111
- if (client)
112
- client.end();
113
- };
114
- session.on('close', closeSession);
115
- session.on('end', closeSession);
116
- // session.on('pty', (accept, reject, info) => {
117
- // reject();
118
- // const { term, cols, height, modes, rows, width } = info;
119
- // });
120
- // session.on('window-change', (accept, reject, info) => {
121
- // const { cols, height, rows, width } = info;
122
- // });
123
- session.on('shell', (accept) => {
124
- logger_js_1.default.info('s1');
125
- if (!accept)
126
- return;
127
- const stream = accept();
128
- logger_js_1.default.info('s2');
129
- const proc = (0, child_process_1.spawn)('/bin/bash', {
130
- env: session.env,
131
- });
132
- logger_js_1.default.info('s3');
133
- proc.stdout.pipe(stream);
134
- proc.stderr.pipe(stream.stderr);
135
- stream.pipe(proc.stdin);
136
- proc.on('close', (code) => {
137
- logger_js_1.default.info('s5');
138
- stream.exit(code);
139
- stream.end();
140
- });
141
- logger_js_1.default.info('s4');
142
- });
143
- session.on('exec', (accept, reject, info) => {
144
- logger_js_1.default.debug('sftp exec');
145
- const s = process.hrtime();
146
- if (!info.command) {
147
- if (reject)
148
- reject();
149
- return;
150
- }
151
- let stream;
152
- if (accept)
153
- stream = accept();
154
- (0, child_process_1.exec)(info.command, {
155
- env: session.env,
156
- }, (err, stdout, stderr) => {
157
- if (stream) {
158
- stream.stderr.write(stderr);
159
- stream.write(stdout);
160
- stream.exit(!err ? 0 : 1);
161
- stream.end();
162
- stream = null;
163
- }
164
- const [seconds, nano] = process.hrtime(s);
165
- const ms = seconds * 1000 + nano / 1000 / 1000;
166
- logger_js_1.default.debug(`sftp exec ends: ${ms}ms`);
167
- });
168
- });
169
- session.on('env', (accept, reject, info) => {
170
- Object.keys(info || {}).forEach((key) => {
171
- session.env[key] = info[key];
172
- });
173
- if (accept)
174
- accept();
175
- });
176
- session.on('sftp', (accept, reject) => {
177
- if (!session.sftp) {
178
- session.sftp = new sftp_1.default(accept());
179
- }
180
- else if (reject) {
181
- reject();
182
- }
183
- });
335
+ this.clientSession(accept(), client, proxyClient);
184
336
  });
185
337
  }
186
338
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.9.24-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",