ssh_tunnel_proxy 1.2.10 → 1.2.11
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/README.md +65 -69
- package/lib/index.js +68 -51
- package/package.json +1 -1
- package/{main.js → ssh2-node} +23 -18
- package/test/test_remote_exec.js +0 -2
package/README.md
CHANGED
|
@@ -1,111 +1,107 @@
|
|
|
1
1
|
# ssh_tunnel_proxy
|
|
2
2
|
Initiate a ssh reverse tunnel proxy with forwarding ports
|
|
3
3
|
|
|
4
|
-
ssh_tunnel_proxy
|
|
4
|
+
ssh_tunnel_proxy is a wrapper to ssh2 that provides async functionality as well as an extension to the api to include methods to setup a list of proxy forwards, exec a list of commands or startup a terminal shell. To setup a ssh tunnel, parameters are suppled for host, port, authentication and a list of proxy ports or commands to invoke. If a ngrok api key is provided the host and port of the ngrok tunnel are obtained. If the connection is interrupted or connection errors occur, attempts are made re-establish the tunnel.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
In addition to the node api, a command line function called ssh-node2 is included to start ssh sessions in a manner similar to the ssh command line utility.
|
|
7
|
+
|
|
8
|
+
### Command line examples
|
|
9
|
+
|
|
10
|
+
Connect to remote host and establish local forwards:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
./ssh2-node -u=<username> -h=192.168.1.1 -k=~/.ssh/<private_key> -L=8180:192.168.1.1:80
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Connect to host using parameters stored in ~/.config/ssh_tunnel_proxy/config.json:
|
|
7
17
|
|
|
8
18
|
```
|
|
9
|
-
node
|
|
19
|
+
./ssh2-node rh2
|
|
10
20
|
```
|
|
11
21
|
|
|
12
22
|
default config file, located at:
|
|
13
23
|
~/.config/ssh_tunnel_proxy/config.json
|
|
14
24
|
```json
|
|
15
25
|
[{
|
|
16
|
-
"
|
|
26
|
+
"hostname":"rh2",
|
|
17
27
|
"username": "<username>",
|
|
18
|
-
"password": "",
|
|
19
|
-
"host": "",
|
|
20
|
-
"port": "",
|
|
21
28
|
"proxy_ports": [
|
|
22
29
|
"8280:127.0.0.1:80",
|
|
23
30
|
"9000:127.0.0.1:9000",
|
|
24
31
|
"8122:192.168.2.1:22"
|
|
25
32
|
],
|
|
26
|
-
"
|
|
27
|
-
"80": true,
|
|
28
|
-
"443": true,
|
|
29
|
-
"22": true
|
|
30
|
-
},
|
|
31
|
-
"service_name": "ssh_proxy_client",
|
|
32
|
-
"server_name": "test",
|
|
33
|
+
"private_key_filename":"~/.ssh/<private key>",
|
|
33
34
|
"ngrok_api": "<ngrok api key>"
|
|
34
35
|
}]
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
node main.js -c -e='uptime' -e='ls -all'
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Or execute commands defined in default config:
|
|
38
|
+
Execute a series of commands on remote host
|
|
43
39
|
```
|
|
44
|
-
|
|
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
|
-
}]
|
|
40
|
+
./ssh-node2 rh2 -e='uptime' -e='ls -all'
|
|
58
41
|
```
|
|
59
42
|
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
node main.js -c -S
|
|
63
|
-
```
|
|
43
|
+
### Api examples
|
|
64
44
|
|
|
65
|
-
|
|
45
|
+
Exec remote commands using async await and processing result through streams.
|
|
66
46
|
|
|
67
47
|
```js
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
48
|
+
// send result of cmd through pipeline, generating a stream of json objects
|
|
49
|
+
function lsTest(cmdProxy, cmd, destination) {
|
|
50
|
+
|
|
51
|
+
return new Promise( (resolveCmd) => {
|
|
52
|
+
|
|
53
|
+
// set input of pipeline to split data into lines (npm i split)
|
|
54
|
+
const tunnel = split();
|
|
55
|
+
|
|
56
|
+
// when pipeline is ready exec shell cmd
|
|
57
|
+
const pipelineReady = (socket) => {
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
|
|
61
|
+
// invoke command on remote host and send results to pipeline
|
|
62
|
+
cmdProxy.execCmd(cmd, tunnel)
|
|
63
|
+
|
|
64
|
+
// stream processing complete, cleanup pipeline and exit
|
|
65
|
+
.then(() => {
|
|
66
|
+
//self.cleanupPipeline(socket);
|
|
67
|
+
resolveCmd();
|
|
68
|
+
});
|
|
69
|
+
resolve();
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// pipe shell cmd result through json parser pipeline to destination
|
|
74
|
+
pipeline(tunnel,
|
|
75
|
+
self.parse(),
|
|
76
|
+
self.toJSONString(),
|
|
77
|
+
destination,
|
|
78
|
+
pipelineReady
|
|
79
|
+
);
|
|
89
80
|
})
|
|
90
81
|
}
|
|
91
82
|
|
|
92
|
-
async function
|
|
83
|
+
async function runCmd() {
|
|
93
84
|
|
|
94
|
-
|
|
85
|
+
const opts = {
|
|
86
|
+
"hostname":"rh2",
|
|
87
|
+
"username": "<username>",
|
|
88
|
+
"private_key_filename":"~/.ssh/<private key>",
|
|
89
|
+
"ngrok_api": "<ngrok api key>"
|
|
90
|
+
}
|
|
95
91
|
|
|
96
|
-
|
|
97
|
-
await sshTunnelProxy.connectSSH(opts);
|
|
92
|
+
const sshTunnelProxy = new SSHTunnelProxy();
|
|
98
93
|
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
// connect to remote host
|
|
95
|
+
await sshTunnelProxy.connectSSH(opts);
|
|
101
96
|
|
|
102
|
-
|
|
97
|
+
// invoke ls -all on remote host and parse result to json object string
|
|
98
|
+
await lsTest(sshTunnelProxy, 'ls -all', process.stdout);
|
|
103
99
|
}
|
|
104
100
|
|
|
105
|
-
|
|
101
|
+
runCmd();
|
|
106
102
|
```
|
|
107
103
|
|
|
108
|
-
|
|
104
|
+
Example of use of the api with electronjs.
|
|
109
105
|
|
|
110
106
|
main.js:
|
|
111
107
|
```js
|
package/lib/index.js
CHANGED
|
@@ -17,6 +17,7 @@ License: MIT
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
const net = require('net');
|
|
20
|
+
const fs = require('fs');
|
|
20
21
|
const process = require('process');
|
|
21
22
|
const { Buffer } = require('node:buffer');
|
|
22
23
|
const { Client } = require('electron-ssh2');
|
|
@@ -26,6 +27,8 @@ const NgrokApi = require('./ngrok_service');
|
|
|
26
27
|
|
|
27
28
|
const keypairStorage = new KeypairStorage();
|
|
28
29
|
|
|
30
|
+
const homedir = require('os').homedir();
|
|
31
|
+
|
|
29
32
|
class SSHTunnelProxy extends Client {
|
|
30
33
|
|
|
31
34
|
constructor() {
|
|
@@ -38,15 +41,16 @@ class SSHTunnelProxy extends Client {
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
// function to close all active sockets for tunnel restart and exception handling
|
|
41
|
-
close_sockets(
|
|
44
|
+
close_sockets(proxy_ports) {
|
|
42
45
|
|
|
43
|
-
if (!
|
|
46
|
+
if (!proxy_ports) return;
|
|
44
47
|
|
|
45
48
|
proxy_ports.forEach(proxy_port => {
|
|
46
|
-
const server = this.listener[
|
|
49
|
+
const server = this.listener[proxy_port];
|
|
47
50
|
if (server && server.listening) {
|
|
48
51
|
this.debug_en && this.debug('SSH Server :: closing forward:', proxy_port)
|
|
49
52
|
server.close();
|
|
53
|
+
delete this.listener[proxy_port];
|
|
50
54
|
}
|
|
51
55
|
});
|
|
52
56
|
}
|
|
@@ -102,17 +106,17 @@ class SSHTunnelProxy extends Client {
|
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
// function to setup proxy forwards for ssh tunnel
|
|
105
|
-
setupProxyPorts(
|
|
109
|
+
setupProxyPorts(proxy_ports) {
|
|
106
110
|
|
|
107
111
|
return new Promise((resolve, reject) => {
|
|
108
|
-
|
|
112
|
+
|
|
109
113
|
if (!proxy_ports) {
|
|
110
114
|
resolve();
|
|
111
115
|
return;
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
var listeners = 0;
|
|
115
|
-
|
|
119
|
+
this.listener.isConnected = true;
|
|
116
120
|
const _this = this;
|
|
117
121
|
|
|
118
122
|
// iterate through a list of local forward ports and create a local proxy port
|
|
@@ -121,13 +125,12 @@ class SSHTunnelProxy extends Client {
|
|
|
121
125
|
const [local_port, remote_hostname, remote_port] = proxy_port.split(':');
|
|
122
126
|
|
|
123
127
|
// create local socket server
|
|
124
|
-
const server =
|
|
128
|
+
const server = _this.listener[proxy_port];
|
|
125
129
|
if (server && server.listening) {
|
|
126
|
-
|
|
130
|
+
_this.debug_en && this.debug('SSH Server :: closing forward 2:', proxy_port)
|
|
127
131
|
server.close();
|
|
128
132
|
}
|
|
129
|
-
|
|
130
|
-
this.listener[server_name][proxy_port] = net.createServer({ keepAlive: true, allowHalfOpen: false }, socket => {
|
|
133
|
+
_this.listener[proxy_port] = net.createServer({ keepAlive: true, allowHalfOpen: false }, socket => {
|
|
131
134
|
|
|
132
135
|
if (this.debug_en) {
|
|
133
136
|
var debug_msg = 'SSH Server :: connection on ' + local_port + ' ' + socket.remotePort;
|
|
@@ -143,7 +146,7 @@ class SSHTunnelProxy extends Client {
|
|
|
143
146
|
try {
|
|
144
147
|
var status_msg = 'SSH Server :: before listen on ' + local_port;
|
|
145
148
|
_this.emit('debug', status_msg);
|
|
146
|
-
_this.listener[
|
|
149
|
+
_this.listener[proxy_port].listen(local_port, () => {
|
|
147
150
|
|
|
148
151
|
// emit server listening on port message
|
|
149
152
|
var status_msg = 'SSH Server :: bound on ' + local_port;
|
|
@@ -154,7 +157,7 @@ class SSHTunnelProxy extends Client {
|
|
|
154
157
|
_this.emit('ssh_tunnel_ready', {});
|
|
155
158
|
if (_this._tunnelReadyTimeout) {
|
|
156
159
|
clearTimeout(this._tunnelReadyTimeout);
|
|
157
|
-
_this._tunnelReadyTimeout = undefined
|
|
160
|
+
_this._tunnelReadyTimeout = undefined;
|
|
158
161
|
}
|
|
159
162
|
resolve();
|
|
160
163
|
}
|
|
@@ -178,12 +181,14 @@ class SSHTunnelProxy extends Client {
|
|
|
178
181
|
reject(err);
|
|
179
182
|
return;
|
|
180
183
|
}
|
|
181
|
-
|
|
184
|
+
|
|
185
|
+
const onClose = () => {
|
|
182
186
|
var buf = Buffer.concat(bufs);
|
|
183
187
|
var err = Buffer.concat(errs);
|
|
184
|
-
|
|
188
|
+
stream.removeListener('close', onClose);
|
|
185
189
|
resolve(buf, err);
|
|
186
|
-
}
|
|
190
|
+
}
|
|
191
|
+
stream.on('close', onClose);
|
|
187
192
|
|
|
188
193
|
stream.on('data', (data) => {
|
|
189
194
|
if (dataStream) dataStream.write(data);
|
|
@@ -238,14 +243,10 @@ class SSHTunnelProxy extends Client {
|
|
|
238
243
|
return new Promise((resolve) => {
|
|
239
244
|
|
|
240
245
|
// exit if connection already established, avoid redundant connection on retry
|
|
241
|
-
if (_this.listener
|
|
246
|
+
if (_this.listener.isConnected) resolve();
|
|
242
247
|
|
|
243
248
|
// close open sockets on server, otherwise initialize open sockets storage
|
|
244
|
-
|
|
245
|
-
_this.close_sockets(opts.server_name, opts.proxy_ports);
|
|
246
|
-
} else {
|
|
247
|
-
_this.listener[opts.server_name] = {};
|
|
248
|
-
}
|
|
249
|
+
_this.close_sockets(opts.proxy_ports);
|
|
249
250
|
|
|
250
251
|
_this.on('ready', async () => {
|
|
251
252
|
|
|
@@ -255,47 +256,48 @@ class SSHTunnelProxy extends Client {
|
|
|
255
256
|
_this._tunnelReadyTimeout = undefined;
|
|
256
257
|
}
|
|
257
258
|
|
|
259
|
+
// if local forwarding requested, setup local forwarding ports to remote host
|
|
260
|
+
if (opts.proxy_ports) {
|
|
261
|
+
await _this.setupProxyPorts(opts.proxy_ports);
|
|
262
|
+
}
|
|
263
|
+
|
|
258
264
|
// if shell requested, enable remote terminal
|
|
259
265
|
if (opts.shell) {
|
|
260
266
|
_this.shell(_this.remote_shell);
|
|
261
267
|
|
|
262
|
-
// if exec requested, exec series of cmds
|
|
268
|
+
// otherwise, if exec requested, exec series of cmds
|
|
263
269
|
} else {
|
|
264
270
|
if (opts.exec && opts.exec.length > 0) {
|
|
265
271
|
for (var i = 0; i < opts.exec.length; i++) {
|
|
266
272
|
var cmd = opts.exec[i];
|
|
267
|
-
|
|
273
|
+
_this.debug_en && _this.debug(opts.username + '@' + opts.hostname + ':' + cmd);
|
|
268
274
|
var result = await _this.execCmd(cmd);
|
|
269
|
-
|
|
275
|
+
_this.debug_en && _this.debug(result.toString());
|
|
270
276
|
}
|
|
271
277
|
process.exit();
|
|
272
278
|
}
|
|
273
279
|
}
|
|
274
280
|
|
|
275
|
-
// if local forward requested, setup local forwarding ports to remote host
|
|
276
|
-
if (opts.proxy_ports) {
|
|
277
|
-
await _this.setupProxyPorts(opts.server_name, opts.proxy_ports);
|
|
278
|
-
}
|
|
279
281
|
resolve();
|
|
280
282
|
});
|
|
281
283
|
|
|
282
284
|
_this.on('end', () => {
|
|
283
285
|
_this.debug_en && _this.debug('SSH Client :: end');
|
|
284
|
-
_this.close_sockets(opts.
|
|
286
|
+
_this.close_sockets(opts.proxy_ports);
|
|
285
287
|
});
|
|
286
288
|
|
|
287
289
|
_this.on('close', () => {
|
|
288
290
|
_this.debug_en && _this.debug('SSH Client :: close');
|
|
289
|
-
_this.close_sockets(opts.
|
|
291
|
+
_this.close_sockets(opts.proxy_ports);
|
|
290
292
|
});
|
|
291
293
|
|
|
292
294
|
_this.on('error', err => {
|
|
293
295
|
_this.debug_en && _this.debug('SSH Client :: error :: ' + err);
|
|
294
296
|
_this.emit('debug', err);
|
|
295
|
-
|
|
297
|
+
_this.listener.isConnected = false;
|
|
296
298
|
if (_this._tunnelReadyTimeout) {
|
|
297
|
-
clearTimeout(this._tunnelReadyTimeout);
|
|
298
|
-
_this._tunnelReadyTimeout = undefined
|
|
299
|
+
clearTimeout(this._tunnelReadyTimeout.elReadyTimeout);
|
|
300
|
+
_this._tunnelReadyTimeout = undefined;
|
|
299
301
|
}
|
|
300
302
|
_this.ssh_retry_connect(opts);
|
|
301
303
|
resolve();
|
|
@@ -309,19 +311,19 @@ class SSHTunnelProxy extends Client {
|
|
|
309
311
|
_this.debug_en && _this.debug('SSH Client :: greeting:', message);
|
|
310
312
|
});
|
|
311
313
|
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
const debug_client = (this.debug_ssh) ? (...args) => { console.log(...args); } : null;
|
|
315
|
-
_this.connect({
|
|
316
|
-
host: opts.host,
|
|
317
|
-
port: opts.port,
|
|
318
|
-
username: opts.username,
|
|
319
|
-
password: opts.password,
|
|
320
|
-
privateKey: opts.private_key,
|
|
321
|
-
debug: debug_client,
|
|
322
|
-
keepaliveInterval: 10000
|
|
323
|
-
});
|
|
314
|
+
// create deep copy of opts as connect options
|
|
315
|
+
const config = JSON.parse(JSON.stringify(opts));
|
|
324
316
|
|
|
317
|
+
// add default ssh2 config options
|
|
318
|
+
config.debug = (this.debug_ssh) ? (...args) => { console.log(...args); } : null;
|
|
319
|
+
config.keepaliveInterval = opts.keepaliveInterval || 10000;
|
|
320
|
+
|
|
321
|
+
// remove ssh_tunnel_proxy config extensions
|
|
322
|
+
const ssh_tunnel_extensions = ['alias', 'disabled', 'hostname', 'ngrok_api', 'server_name', 'service_name', 'whitelist', 'private_key_filename'];
|
|
323
|
+
for (var i = 0; i < ssh_tunnel_extensions.length; i++) delete config[ssh_tunnel_extensions[i]];
|
|
324
|
+
|
|
325
|
+
// connect to remote host
|
|
326
|
+
_this.connect(config);
|
|
325
327
|
});
|
|
326
328
|
}
|
|
327
329
|
|
|
@@ -330,7 +332,7 @@ class SSHTunnelProxy extends Client {
|
|
|
330
332
|
if (isNaN(port_str)) return false;
|
|
331
333
|
const port = parseInt(port_str);
|
|
332
334
|
if (port < 1 || port > 65535) return false;
|
|
333
|
-
if (port < 1024 && whitelist[port] === undefined) return false;
|
|
335
|
+
if (port < 1024 && whitelist && whitelist[port] === undefined) return false;
|
|
334
336
|
return true;
|
|
335
337
|
}
|
|
336
338
|
|
|
@@ -396,13 +398,11 @@ class SSHTunnelProxy extends Client {
|
|
|
396
398
|
// todo: if opts have changed while service is running, shutdown current service and restart with new opts
|
|
397
399
|
async connectSSH(opts, whitelist) {
|
|
398
400
|
|
|
399
|
-
this.debug_en && this.debug('connectSSH:\n', JSON.stringify(opts, null, 2));
|
|
400
|
-
|
|
401
401
|
// make deep copy of opts for modification
|
|
402
402
|
var _opts = JSON.parse(JSON.stringify(opts));
|
|
403
403
|
|
|
404
404
|
// setup whitelist from param or opts in case of error retry
|
|
405
|
-
_opts.whitelist = (whitelist !== undefined) ? whitelist : (_opts.whitelist) ? _opts.whitelist :
|
|
405
|
+
_opts.whitelist = (whitelist !== undefined) ? whitelist : (_opts.whitelist) ? _opts.whitelist : null;
|
|
406
406
|
|
|
407
407
|
// validate local port forwards, emit error and quit if invalid
|
|
408
408
|
try {
|
|
@@ -418,9 +418,24 @@ class SSHTunnelProxy extends Client {
|
|
|
418
418
|
_opts.password = undefined;
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
if (_opts.private_key_filename) {
|
|
422
|
+
try {
|
|
423
|
+
if (_opts.private_key_filename[0] == '~') {
|
|
424
|
+
_opts.private_key_filename = homedir + _opts.private_key_filename.substr(1);
|
|
425
|
+
}
|
|
426
|
+
_opts.privateKey = fs.readFileSync(_opts.private_key_filename).toString();
|
|
427
|
+
} catch (err) {
|
|
428
|
+
console.log('Error loading key:', err);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
421
432
|
// retrieve private key from system keychain with supplied service name and server
|
|
422
433
|
// if no key found, authentication is supplied username, password
|
|
423
|
-
|
|
434
|
+
if (_opts.service_name && _opts.server_name) {
|
|
435
|
+
_opts.privateKey = await keypairStorage.get_keypair(opts.service_name, opts.server_name);
|
|
436
|
+
} else {
|
|
437
|
+
if (!_opts.server_name) _opts.server_name = 'sshtun';
|
|
438
|
+
}
|
|
424
439
|
|
|
425
440
|
// if ngrok api key provided, obtain hostname and hostport from ngrok api
|
|
426
441
|
// if no ngrok tunnel specified, use supplied host and port
|
|
@@ -430,7 +445,7 @@ class SSHTunnelProxy extends Client {
|
|
|
430
445
|
var hostport = await ngrokApi.get_hostport()
|
|
431
446
|
.catch(() => {
|
|
432
447
|
_this.debug_en && _this.debug('ngrok get_hostport connection error');
|
|
433
|
-
|
|
448
|
+
_this.listener.isConnected = false;
|
|
434
449
|
_this.ssh_retry_connect(_opts);
|
|
435
450
|
});
|
|
436
451
|
if (hostport && hostport.host) {
|
|
@@ -440,6 +455,8 @@ class SSHTunnelProxy extends Client {
|
|
|
440
455
|
else return null;
|
|
441
456
|
}
|
|
442
457
|
|
|
458
|
+
this.debug_en && this.debug('connectSSH:\n', JSON.stringify(_opts, null, 2));
|
|
459
|
+
|
|
443
460
|
// initiate ssh tunnel, block until tunnel is established or error
|
|
444
461
|
return await this.ssh_start_tunnel(_opts)
|
|
445
462
|
.catch((err) => {
|
package/package.json
CHANGED
package/{main.js → ssh2-node}
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/node
|
|
1
2
|
const process = require('process');
|
|
2
3
|
const fs = require('fs');
|
|
3
4
|
const { SSHTunnelProxy, KeypairStorage } = require('./lib/index.js');
|
|
@@ -21,14 +22,6 @@ var service = null;
|
|
|
21
22
|
var account = null;
|
|
22
23
|
var shell = null;
|
|
23
24
|
|
|
24
|
-
function get_key_from_file(filename) {
|
|
25
|
-
try {
|
|
26
|
-
return fs.readFileSync(filename);
|
|
27
|
-
} catch (err) {
|
|
28
|
-
console.log('Error loading key:', err);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
25
|
function get_config(filename) {
|
|
33
26
|
try {
|
|
34
27
|
configs = JSON.parse(fs.readFileSync(filename), 'utf8');
|
|
@@ -41,7 +34,7 @@ function get_default_config() {
|
|
|
41
34
|
try {
|
|
42
35
|
configs = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
|
|
43
36
|
} catch (err) {
|
|
44
|
-
console.log('Error loading config file:', err);
|
|
37
|
+
//console.log('Error loading config file:', err);
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
40
|
|
|
@@ -114,15 +107,21 @@ function main(args) {
|
|
|
114
107
|
username = value;
|
|
115
108
|
break;
|
|
116
109
|
default:
|
|
117
|
-
if (i
|
|
110
|
+
if (i == 2) remote_host = cmd;
|
|
111
|
+
if (i > 2) console.log('Invalid argument:', arg);
|
|
118
112
|
}
|
|
119
113
|
}
|
|
120
114
|
|
|
121
115
|
// prevent process from exiting
|
|
122
|
-
process.stdin.resume();
|
|
116
|
+
//process.stdin.resume();
|
|
123
117
|
|
|
124
|
-
// if no config file specified
|
|
118
|
+
// if no config file specified attempt to load default config
|
|
125
119
|
if (!configs) {
|
|
120
|
+
get_default_config();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// no default config build config from args
|
|
124
|
+
if (!remote_host) {
|
|
126
125
|
var config = {
|
|
127
126
|
enabled: true,
|
|
128
127
|
username: username,
|
|
@@ -140,24 +139,28 @@ function main(args) {
|
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
// process each tunnel config in ssh proxy tunnel list
|
|
142
|
+
var remote_server = null;
|
|
143
143
|
configs.forEach(async config => {
|
|
144
144
|
|
|
145
145
|
// skip tunnel if not enabled
|
|
146
|
-
if (
|
|
146
|
+
if (config.disabled) return;
|
|
147
147
|
|
|
148
148
|
// if remote host specified, only select tunnel for remote host
|
|
149
|
-
if (remote_host && remote_host !== config.
|
|
149
|
+
if (remote_host && remote_host !== config.hostname) return;
|
|
150
|
+
config.server_name = config.hostname || remote_host || 'ssh_tunnel_proxy';
|
|
151
|
+
remote_server = remote_host;
|
|
150
152
|
|
|
151
153
|
// if system key storage name specified get key from system keychain
|
|
152
154
|
if (config.server_name) {
|
|
153
155
|
const service_name = config.service_name || 'ssh_tunnel_proxy';
|
|
154
156
|
config.private_key = await keypairStorage.get_keypair(service_name, config.server_name);
|
|
155
157
|
}
|
|
158
|
+
|
|
156
159
|
// if private key filename specified, read private key
|
|
157
|
-
|
|
158
|
-
config.
|
|
160
|
+
if (privateKey) {
|
|
161
|
+
config.private_key_filename = privateKey;
|
|
159
162
|
}
|
|
160
|
-
if (shell) {
|
|
163
|
+
if (shell || exec.length === 0) {
|
|
161
164
|
config.shell = true;
|
|
162
165
|
}
|
|
163
166
|
if (exec.length > 0) {
|
|
@@ -169,7 +172,9 @@ function main(args) {
|
|
|
169
172
|
sshTunnelProxy.debug_en = debug;
|
|
170
173
|
sshTunnelProxy.connectSSH(config);
|
|
171
174
|
});
|
|
172
|
-
|
|
175
|
+
if (remote_host && !remote_server) {
|
|
176
|
+
console.log(`Remote host ${remote_host} not found`);
|
|
177
|
+
}
|
|
173
178
|
|
|
174
179
|
}
|
|
175
180
|
|
package/test/test_remote_exec.js
CHANGED
|
@@ -165,7 +165,6 @@ async function lsTest(sshTunnelProxy) {
|
|
|
165
165
|
);
|
|
166
166
|
|
|
167
167
|
const lscmd = 'ls -all';
|
|
168
|
-
//const lscmd = 'ls -all | column --table --table-columns pm,links,user,group,size,month,day,time,name -J';
|
|
169
168
|
console.log('\ninvoking ' + lscmd + ' on remote host:\n');
|
|
170
169
|
await sshTunnelProxy.execCmd(lscmd, tunnel);
|
|
171
170
|
|
|
@@ -188,7 +187,6 @@ async function syslogTest(sshTunnelProxy) {
|
|
|
188
187
|
process.stdout,
|
|
189
188
|
() => { }
|
|
190
189
|
);
|
|
191
|
-
//const syslogcmd = 'tail /var/log/syslog | column --table --table-columns-limit 6 --table-columns month,day,time,host,proc,msg -J';
|
|
192
190
|
const syslogcmd = 'tail /var/log/syslog';
|
|
193
191
|
console.log('\ninvoking ' + syslogcmd + ' on remote host:\n');
|
|
194
192
|
await sshTunnelProxy.execCmd(syslogcmd, tunnel);
|