bdy 1.9.24-dev → 1.9.26-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.26-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,265 @@ 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
+ let keys;
99
+ let privateKey;
100
+ try {
101
+ const resp = await buddy_1.default.fetchAgentKeys(this.agent.id, this.agent.host, this.agent.token);
102
+ keys = resp.keys;
103
+ privateKey = resp.privateKey;
104
+ }
105
+ catch {
106
+ ctx.reject();
107
+ return null;
108
+ }
109
+ const verified = await this.verifyKey(ctx, keys);
110
+ if (!verified) {
111
+ ctx.reject();
112
+ return null;
113
+ }
114
+ try {
115
+ const proxyClient = await this.createProxyConnection(privateKey);
116
+ ctx.accept();
117
+ return proxyClient;
118
+ }
119
+ catch {
120
+ ctx.reject();
121
+ return null;
122
+ }
123
+ }
124
+ ctx.reject();
125
+ return null;
126
+ }
127
+ localExec(stream, command, env) {
128
+ const s = process.hrtime();
129
+ (0, child_process_1.exec)(command, {
130
+ env,
131
+ }, (err, stdout, stderr) => {
132
+ if (stream) {
133
+ stream.stderr.write(stderr);
134
+ stream.write(stdout);
135
+ stream.exit(!err ? 0 : 1);
136
+ stream.end();
137
+ stream = null;
138
+ }
139
+ const [seconds, nano] = process.hrtime(s);
140
+ const ms = seconds * 1000 + nano / 1000 / 1000;
141
+ logger_js_1.default.debug(`local ssh exec ends: ${ms}ms`);
142
+ });
143
+ }
144
+ clientSession(session, client, proxyClient) {
145
+ let env = {};
146
+ let sftp;
147
+ let channel;
148
+ let pty;
149
+ let x11;
150
+ const closeSession = () => {
151
+ env = null;
152
+ pty = null;
153
+ x11 = null;
154
+ session.removeAllListeners();
155
+ session = null;
156
+ if (sftp) {
157
+ sftp.destroy();
158
+ sftp = null;
159
+ }
160
+ if (channel) {
161
+ channel.removeAllListeners();
162
+ channel = null;
163
+ }
164
+ if (client) {
165
+ client.end();
166
+ }
167
+ };
168
+ session.on('close', closeSession);
169
+ session.on('end', closeSession);
170
+ session.on('pty', (accept, reject, info) => {
171
+ if (!proxyClient) {
172
+ if (reject)
173
+ reject();
174
+ return;
175
+ }
176
+ pty = info;
177
+ if (accept)
178
+ accept();
179
+ });
180
+ session.on('window-change', (accept, reject, info) => {
181
+ if (!proxyClient) {
182
+ if (reject)
183
+ reject();
184
+ return;
185
+ }
186
+ if (!pty)
187
+ pty = {};
188
+ pty.cols = info.cols;
189
+ pty.height = info.height;
190
+ pty.rows = info.rows;
191
+ pty.width = info.width;
192
+ if (channel && channel.setWindow)
193
+ channel.setWindow(pty.rows, pty.cols, pty.height, pty.width);
194
+ if (accept)
195
+ accept();
196
+ });
197
+ session.on('shell', (accept, reject) => {
198
+ if (!proxyClient || !accept) {
199
+ if (reject)
200
+ reject();
67
201
  return;
68
202
  }
69
- if (ctx.method === 'password') {
70
- if (!this.checkValueSafe(Buffer.from(ctx.username), Buffer.from(this.login))) {
71
- ctx.reject();
203
+ const stream = accept();
204
+ proxyClient.shell(pty, {
205
+ env,
206
+ x11
207
+ }, (err, c) => {
208
+ if (err || !c) {
209
+ if (reject)
210
+ reject();
72
211
  return;
73
212
  }
74
- if (!this.checkValueSafe(Buffer.from(ctx.password), Buffer.from(this.password))) {
75
- ctx.reject();
213
+ channel = c;
214
+ stream.pipe(channel);
215
+ channel.pipe(stream);
216
+ });
217
+ });
218
+ session.on('signal', (accept, reject, info) => {
219
+ const { name } = info;
220
+ if (!proxyClient || !name || !channel || !channel.signal) {
221
+ if (reject)
222
+ reject();
223
+ return;
224
+ }
225
+ channel.signal(name);
226
+ if (accept)
227
+ accept();
228
+ });
229
+ session.on('subsystem', (accept, reject, info) => {
230
+ const { name } = info;
231
+ if (!proxyClient || !accept || !name) {
232
+ if (reject)
233
+ reject();
234
+ return;
235
+ }
236
+ const stream = accept();
237
+ proxyClient.subsys(name, (err, c) => {
238
+ if (err || !c) {
239
+ if (reject)
240
+ reject();
76
241
  return;
77
242
  }
78
- ctx.accept();
243
+ channel = c;
244
+ stream.pipe(channel);
245
+ channel.pipe(stream);
246
+ });
247
+ });
248
+ session.on('x11', (accept, reject, info) => {
249
+ if (!proxyClient) {
250
+ if (reject)
251
+ reject();
252
+ return;
253
+ }
254
+ x11 = info;
255
+ if (accept)
256
+ accept();
257
+ });
258
+ session.on('exec', (accept, reject, info) => {
259
+ if (!info.command || !accept) {
260
+ if (reject)
261
+ reject();
262
+ return;
263
+ }
264
+ const stream = accept();
265
+ if (!proxyClient) {
266
+ this.localExec(stream, info.command, env);
79
267
  return;
80
268
  }
81
- if (ctx.method === 'publickey') {
82
- const verified = await this.verifyKey(ctx);
83
- if (!verified) {
84
- ctx.reject();
269
+ proxyClient.exec(info.command, {
270
+ env,
271
+ pty,
272
+ x11
273
+ }, (err, c) => {
274
+ if (err || !c) {
275
+ if (reject)
276
+ reject();
85
277
  return;
86
278
  }
87
- ctx.accept();
88
- return;
279
+ channel = c;
280
+ stream.pipe(channel);
281
+ channel.pipe(stream);
282
+ });
283
+ });
284
+ session.on('env', (accept, reject, info) => {
285
+ Object.keys(info || {}).forEach((key) => {
286
+ env[key] = info[key];
287
+ });
288
+ if (accept)
289
+ accept();
290
+ });
291
+ session.on('sftp', (accept, reject) => {
292
+ if (!sftp && accept) {
293
+ sftp = new sftp_1.default(accept());
89
294
  }
90
- ctx.reject();
295
+ else if (reject) {
296
+ reject();
297
+ }
298
+ });
299
+ }
300
+ processClient(client) {
301
+ let proxyClient;
302
+ client.setNoDelay();
303
+ client.on('authentication', async (ctx) => {
304
+ proxyClient = await this.authenticateClient(ctx);
91
305
  });
92
306
  client.on('close', () => {
93
307
  client.removeAllListeners();
94
308
  client = null;
309
+ if (proxyClient) {
310
+ try {
311
+ proxyClient.removeAllListeners();
312
+ proxyClient.end();
313
+ }
314
+ catch {
315
+ // do nothing
316
+ }
317
+ proxyClient = null;
318
+ }
95
319
  });
96
320
  client.on('error', (err) => {
97
321
  logger_js_1.default.debug('Error on ssh server client');
@@ -99,88 +323,7 @@ class ServerSsh extends events_1.default {
99
323
  client.end();
100
324
  });
101
325
  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
- });
326
+ this.clientSession(accept(), client, proxyClient);
184
327
  });
185
328
  }
186
329
  }
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.26-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",