ssh_tunnel_proxy 1.2.6 → 1.2.8
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/.vscode/launch.json +1 -1
- package/README.md +33 -6
- package/lib/index.js +49 -23
- package/main.js +8 -0
- package/package.json +1 -1
package/.vscode/launch.json
CHANGED
package/README.md
CHANGED
|
@@ -3,9 +3,13 @@ Initiate a ssh reverse tunnel proxy with forwarding ports
|
|
|
3
3
|
|
|
4
4
|
ssh_tunnel_proxy can function as a stand-alone api, nodejs command line script or as part of an electron_js app. To setup a ssh tunnel, parameters are suppled for host, port, authentication and a list of proxy ports. If a ngrok api key is provided the host and port of the ngrok tunnel are obtained. A list of port forwards is provided and is validated to restrict connections to system ports on the remote host to a list of pre-defined ports such as http or https. After establishing a ssh connection on the remote server, local proxy port forwards are opened. If the connection is interrupted or connection errors occur, attempts are made re-establish the tunnel.
|
|
5
5
|
|
|
6
|
-
Example command line
|
|
6
|
+
Example command line to establish a list of local forwards:
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
```
|
|
9
|
+
node main.js -c
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
default config file, located at:
|
|
9
13
|
~/.config/ssh_tunnel_proxy/config.json
|
|
10
14
|
```json
|
|
11
15
|
[{
|
|
@@ -28,21 +32,44 @@ config file, located at:
|
|
|
28
32
|
"server_name": "test",
|
|
29
33
|
"ngrok_api": "<ngrok api key>"
|
|
30
34
|
}]
|
|
35
|
+
```
|
|
31
36
|
|
|
37
|
+
Command line to execute a series of commands on remote host:
|
|
38
|
+
```
|
|
39
|
+
node main.js -c -e='uptime' -e='ls -all'
|
|
32
40
|
```
|
|
33
|
-
start tunnel with default config, located at ~/.config/ssh_tunnel_proxy/config.json
|
|
34
41
|
|
|
42
|
+
Or execute commands defined in default config:
|
|
35
43
|
```
|
|
36
|
-
node main.js -c
|
|
44
|
+
node main.js -c
|
|
37
45
|
```
|
|
46
|
+
```json
|
|
47
|
+
[{
|
|
48
|
+
"enabled": true,
|
|
49
|
+
"username": "<username>",
|
|
50
|
+
"service_name": "ssh_proxy_client",
|
|
51
|
+
"server_name": "test",
|
|
52
|
+
"exec" : [
|
|
53
|
+
"uptime",
|
|
54
|
+
"ls -all"
|
|
55
|
+
],
|
|
56
|
+
"ngrok_api": "<ngrok api key>"
|
|
57
|
+
}]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Start a terminal session on the remote host:
|
|
61
|
+
```
|
|
62
|
+
node main.js -c -S
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The following code is an example of use of the api with electronjs.
|
|
38
66
|
|
|
39
|
-
The following code is an example of use of the api with electronjs and is to be installed in main.js, preload.js and the render process.
|
|
40
67
|
main.js:
|
|
41
68
|
```js
|
|
42
69
|
|
|
43
70
|
async function init_sshTunnelProxy(win) {
|
|
44
71
|
|
|
45
|
-
const SSHTunnelProxy = require('ssh_tunnel_proxy');
|
|
72
|
+
const { SSHTunnelProxy } = require('ssh_tunnel_proxy');
|
|
46
73
|
const sshTunnelProxy = new SSHTunnelProxy();
|
|
47
74
|
|
|
48
75
|
ipcMain.on('connect_ssh_sync', async function(event,opts) {
|
package/lib/index.js
CHANGED
|
@@ -18,6 +18,7 @@ License: MIT
|
|
|
18
18
|
|
|
19
19
|
const net = require('net');
|
|
20
20
|
const process = require('process');
|
|
21
|
+
const { Buffer } = require('node:buffer');
|
|
21
22
|
const { Client } = require('electron-ssh2');
|
|
22
23
|
|
|
23
24
|
const KeypairStorage = require('./keypair_storage');
|
|
@@ -157,27 +158,42 @@ class SSHTunnelProxy extends Client {
|
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
160
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
161
|
+
exec_cmd(cmd, dataStream, errStream) {
|
|
162
|
+
|
|
163
|
+
return new Promise((resolve, reject) => {
|
|
164
|
+
var bufs = [];
|
|
165
|
+
var errs = [];
|
|
166
|
+
|
|
167
|
+
const remote_exec = (err, stream) => {
|
|
168
|
+
if (err) {
|
|
169
|
+
reject(err);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
stream.on('close', () => {
|
|
173
|
+
var buf = Buffer.concat(bufs);
|
|
174
|
+
var err = Buffer.concat(errs);
|
|
175
|
+
resolve(buf, err);
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
if (dataStream) {
|
|
179
|
+
stream.pipe(dataStream);
|
|
180
|
+
} else {
|
|
181
|
+
stream.on('data', (data) => {
|
|
182
|
+
bufs.push(data);
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (errStream) {
|
|
187
|
+
stream.stderr.pipe(errStream);
|
|
188
|
+
} else {
|
|
189
|
+
stream.stderr.on('data', (data) => {
|
|
190
|
+
errs.push(data);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
}
|
|
177
195
|
this.exec(cmd, remote_exec);
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
return false;
|
|
196
|
+
})
|
|
181
197
|
}
|
|
182
198
|
|
|
183
199
|
// handle remote shell stream processing
|
|
@@ -227,7 +243,7 @@ class SSHTunnelProxy extends Client {
|
|
|
227
243
|
_this.listener[opts.server_name] = {};
|
|
228
244
|
}
|
|
229
245
|
|
|
230
|
-
_this.on('ready', () => {
|
|
246
|
+
_this.on('ready', async () => {
|
|
231
247
|
|
|
232
248
|
// stop any pending retry timeouts
|
|
233
249
|
if (_this._tunnelReadyTimeout) {
|
|
@@ -242,14 +258,21 @@ class SSHTunnelProxy extends Client {
|
|
|
242
258
|
// if exec requested, exec series of cmds
|
|
243
259
|
} else {
|
|
244
260
|
if (opts.exec && opts.exec.length > 0) {
|
|
245
|
-
|
|
246
|
-
|
|
261
|
+
for (var i = 0; i < opts.exec.length; i++) {
|
|
262
|
+
var cmd = opts.exec[i];
|
|
263
|
+
console.log(opts.username + '@' + opts.hostname + ':' + cmd);
|
|
264
|
+
var result = await _this.exec_cmd(cmd);
|
|
265
|
+
console.log(result.toString());
|
|
266
|
+
}
|
|
267
|
+
process.exit();
|
|
247
268
|
}
|
|
248
269
|
}
|
|
249
270
|
|
|
250
271
|
// if local forward requested, setup local forwarding ports to remote host
|
|
251
272
|
if (opts.proxy_ports) {
|
|
252
273
|
_this.on_ssh_client_ready(opts.server_name, opts.proxy_ports, resolve);
|
|
274
|
+
} else {
|
|
275
|
+
resolve();
|
|
253
276
|
}
|
|
254
277
|
});
|
|
255
278
|
|
|
@@ -310,6 +333,9 @@ class SSHTunnelProxy extends Client {
|
|
|
310
333
|
|
|
311
334
|
// validate local forwards for correct format and valid ports
|
|
312
335
|
validate_local_forward(proxy_ports, whitelist) {
|
|
336
|
+
|
|
337
|
+
if (!proxy_ports) return;
|
|
338
|
+
|
|
313
339
|
const local_ports = [];
|
|
314
340
|
const remote_ports = [];
|
|
315
341
|
proxy_ports.forEach(proxy_port => {
|
package/main.js
CHANGED
|
@@ -6,6 +6,7 @@ const keypairStorage = new KeypairStorage();
|
|
|
6
6
|
const homedir = require('os').homedir();
|
|
7
7
|
var debug = false;
|
|
8
8
|
var exec = [];
|
|
9
|
+
var remote_host = null;
|
|
9
10
|
var proxies = [];
|
|
10
11
|
var configs = null;
|
|
11
12
|
var host = null;
|
|
@@ -92,6 +93,10 @@ function main(args) {
|
|
|
92
93
|
case '--ngrok':
|
|
93
94
|
ngrokAPIKey = value;
|
|
94
95
|
break;
|
|
96
|
+
case '-r':
|
|
97
|
+
case '--remote':
|
|
98
|
+
remote_host = value;
|
|
99
|
+
break;
|
|
95
100
|
case '-R':
|
|
96
101
|
case '--RemoteForward':
|
|
97
102
|
forwardIn.push(value);
|
|
@@ -140,6 +145,9 @@ function main(args) {
|
|
|
140
145
|
// skip tunnel if not enabled
|
|
141
146
|
if (!config.enabled) return;
|
|
142
147
|
|
|
148
|
+
// if remote host specified, only select tunnel for remote host
|
|
149
|
+
if (remote_host && remote_host !== config.alias) return;
|
|
150
|
+
|
|
143
151
|
// if system key storage name specified get key from system keychain
|
|
144
152
|
if (config.server_name) {
|
|
145
153
|
const service_name = config.service_name || 'ssh_tunnel_proxy';
|