ssh_tunnel_proxy 1.1.5 → 1.2.1
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 +2 -2
- package/README.md +41 -7
- package/lib/index.js +38 -25
- package/lib/keypair_storage.js +0 -13
- package/main.js +153 -0
- package/package.json +1 -1
- package/test/test.js +3 -4
package/.vscode/launch.json
CHANGED
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"type": "node",
|
|
23
23
|
"runtimeVersion": "19.4.0",
|
|
24
24
|
"request": "launch",
|
|
25
|
-
"name": "Launch
|
|
25
|
+
"name": "Launch node cli",
|
|
26
26
|
"cwd": "${fileDirname}",
|
|
27
27
|
"skipFiles": [
|
|
28
28
|
"<node_internals>/**"
|
|
29
29
|
],
|
|
30
30
|
"program": "${file}",
|
|
31
|
-
"args":["
|
|
31
|
+
"args":["-c","-d"]
|
|
32
32
|
}
|
|
33
33
|
]
|
|
34
34
|
}
|
package/README.md
CHANGED
|
@@ -1,14 +1,44 @@
|
|
|
1
1
|
# ssh_tunnel_proxy
|
|
2
2
|
Initiate a ssh reverse tunnel proxy with forwarding ports
|
|
3
3
|
|
|
4
|
-
ssh_tunnel_proxy can function as a stand-alone api or part of an electron_js app. To establish a ssh tunnel proxy a keypair is generated on the client and stored in the system keychain.
|
|
5
|
-
If a ngrok api key is provided the ngrok api endpoint method is invoked to obtain the hostport of
|
|
6
|
-
the tunnel. A list of port forwards is provided to the connect_api function and ports validated to restrict connections to system ports on the remote host to a set of pre-defined ports such as http,https. After establishing a ssh connection to the remote server, local proxy port forwards are opened. If the connection is interrupted then the ssh_connect method will attempt to re-establish the connection.
|
|
4
|
+
ssh_tunnel_proxy can function as a stand-alone api, nodejs command line script or part of an electron_js app. To establish a ssh tunnel proxy a keypair is generated on the client and stored in the system keychain.
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
If a ngrok api key is provided the ngrok api endpoint method is invoked to obtain the hostport of the tunnel. A list of port forwards is provided to the connect_api function and ports validated to restrict connections to system ports on the remote host to a set of pre-defined ports such as http,https. After establishing a ssh connection to the remote server, local proxy port forwards are opened. If the connection is interrupted then the ssh_connect method will attempt to re-establish the connection.
|
|
7
|
+
|
|
8
|
+
Example command line usage:
|
|
9
|
+
|
|
10
|
+
config file, located at:
|
|
11
|
+
~/.config/ssh_tunnel_proxy/config.json
|
|
12
|
+
```json
|
|
13
|
+
[{
|
|
14
|
+
"enabled": true,
|
|
15
|
+
"username": "<username>",
|
|
16
|
+
"password": "",
|
|
17
|
+
"host": "",
|
|
18
|
+
"port": "",
|
|
19
|
+
"proxy_ports": [
|
|
20
|
+
"8280:127.0.0.1:80",
|
|
21
|
+
"9000:127.0.0.1:9000",
|
|
22
|
+
"8122:192.168.2.1:22"
|
|
23
|
+
],
|
|
24
|
+
"whitelist": {
|
|
25
|
+
"80": true,
|
|
26
|
+
"443": true,
|
|
27
|
+
"22": true
|
|
28
|
+
},
|
|
29
|
+
"service_name": "ssh_proxy_client",
|
|
30
|
+
"server_name": "test",
|
|
31
|
+
"ngrok_api": "<ngrok api key>"
|
|
32
|
+
}]
|
|
33
|
+
|
|
34
|
+
start tunnel with default config, located at ~/.config/ssh_tunnel_proxy/config.json
|
|
35
|
+
```
|
|
36
|
+
node main.js -c -d
|
|
37
|
+
|
|
38
|
+
The following code is an example of use of the api with electronjs and needs to be install in main.js, preload.js and the render process.
|
|
39
|
+
main.js:
|
|
9
40
|
```js
|
|
10
41
|
|
|
11
|
-
// electronjs main.js
|
|
12
42
|
async function init_sshTunnelProxy(win) {
|
|
13
43
|
|
|
14
44
|
const SSHTunnelProxy = require('ssh_tunnel_proxy');
|
|
@@ -40,8 +70,10 @@ async function init_sshTunnelProxy(win) {
|
|
|
40
70
|
sshTunnelProxy.on('error',(...args) => win.webContents.send('error',...args));
|
|
41
71
|
|
|
42
72
|
};
|
|
73
|
+
```
|
|
43
74
|
|
|
44
|
-
|
|
75
|
+
preload.js:
|
|
76
|
+
```js
|
|
45
77
|
const { contextBridge, ipcRenderer } = require('electron')
|
|
46
78
|
|
|
47
79
|
contextBridge.exposeInMainWorld('sshModule', {
|
|
@@ -54,8 +86,10 @@ contextBridge.exposeInMainWorld('sshModule', {
|
|
|
54
86
|
debug: (callback) => ipcRenderer.on('debug', callback),
|
|
55
87
|
error: (callback) => ipcRenderer.on('error', callback)
|
|
56
88
|
});
|
|
89
|
+
```
|
|
57
90
|
|
|
58
|
-
//
|
|
91
|
+
// render process
|
|
92
|
+
```js
|
|
59
93
|
// initialize ssh message handlers
|
|
60
94
|
sshModule.ready((event, data) => {
|
|
61
95
|
});
|
package/lib/index.js
CHANGED
|
@@ -24,7 +24,6 @@ const KeypairStorage = require('./keypair_storage');
|
|
|
24
24
|
const NgrokApi = require('./ngrok_service');
|
|
25
25
|
|
|
26
26
|
// debug logging options
|
|
27
|
-
const en = true;
|
|
28
27
|
const debug_ssh = false;
|
|
29
28
|
|
|
30
29
|
const keypairStorage = new KeypairStorage();
|
|
@@ -35,6 +34,8 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
35
34
|
super();
|
|
36
35
|
this.listener = {};
|
|
37
36
|
this.retries = 0;
|
|
37
|
+
this.debug_en = false;
|
|
38
|
+
this.debug_ssh = false;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
// function to close all active sockets for tunnel restart and exception handling
|
|
@@ -45,7 +46,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
45
46
|
proxy_ports.forEach(proxy_port => {
|
|
46
47
|
const server = this.listener[server_name][proxy_port];
|
|
47
48
|
if (server && server.listening) {
|
|
48
|
-
|
|
49
|
+
this.debug_en &&this.debug('SSH Server :: closing forward:', proxy_port)
|
|
49
50
|
server.close();
|
|
50
51
|
}
|
|
51
52
|
});
|
|
@@ -59,8 +60,8 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
59
60
|
const on_setup_ssh_forward = (err, stream) => {
|
|
60
61
|
|
|
61
62
|
if (err) {
|
|
62
|
-
_this.emit('
|
|
63
|
-
|
|
63
|
+
_this.emit('debug', err);
|
|
64
|
+
_this.debug_en &&_this.debug('socket forward error:', err);
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -75,8 +76,8 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
75
76
|
|
|
76
77
|
// if socket error, emit error and close stream
|
|
77
78
|
socket.on('error', (err) => {
|
|
78
|
-
_this.emit('
|
|
79
|
-
|
|
79
|
+
_this.emit('debug', err);
|
|
80
|
+
_this.debug_en &&_this.debug('socket on error:', err);
|
|
80
81
|
stream.end();
|
|
81
82
|
});
|
|
82
83
|
}
|
|
@@ -105,9 +106,14 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
105
106
|
const [local_port, remote_hostname, remote_port] = proxy_port.split(':');
|
|
106
107
|
|
|
107
108
|
// create local socket server
|
|
109
|
+
const server = this.listener[server_name][proxy_port];
|
|
110
|
+
if (server && server.listening) {
|
|
111
|
+
this.debug_en &&this.debug('SSH Server :: closing forward:', proxy_port)
|
|
112
|
+
server.close();
|
|
113
|
+
}
|
|
108
114
|
this.listener[server_name][proxy_port] = net.createServer({ keepAlive: true, allowHalfOpen: false }, socket => {
|
|
109
115
|
|
|
110
|
-
if (
|
|
116
|
+
if (this.debug_en) {
|
|
111
117
|
var debug_msg = 'SSH Server :: connection on ' + local_port + ' ' + socket.remotePort;
|
|
112
118
|
_this.emit('debug', debug_msg);
|
|
113
119
|
_this.debug(debug_msg);
|
|
@@ -132,7 +138,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
132
138
|
}
|
|
133
139
|
});
|
|
134
140
|
} catch (err) {
|
|
135
|
-
|
|
141
|
+
_this.debug_en &&_this.debug('listen err:', err);
|
|
136
142
|
}
|
|
137
143
|
});
|
|
138
144
|
};
|
|
@@ -162,30 +168,29 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
162
168
|
});
|
|
163
169
|
|
|
164
170
|
ssh_client.on('end', () => {
|
|
165
|
-
|
|
171
|
+
_this.debug_en &&_this.debug('SSH Client :: end');
|
|
166
172
|
_this.close_sockets(opts.server_name, opts.proxy_ports);
|
|
167
173
|
});
|
|
168
174
|
|
|
169
175
|
ssh_client.on('close', () => {
|
|
170
|
-
|
|
176
|
+
_this.debug_en &&_this.debug('SSH Client :: close');
|
|
171
177
|
_this.close_sockets(opts.server_name, opts.proxy_ports);
|
|
172
178
|
});
|
|
173
179
|
|
|
174
180
|
ssh_client.on('error', err => {
|
|
175
|
-
|
|
181
|
+
_this.debug_en &&_this.debug('SSH Client :: error :: ' + err);
|
|
176
182
|
_this.emit('error', err);
|
|
177
|
-
_this.close_sockets(opts.server_name, opts.proxy_ports);
|
|
178
183
|
if (_this.listener[opts.server_name]) _this.listener[opts.server_name].isConnected = false;
|
|
179
184
|
_this.ssh_retry_connect(opts);
|
|
180
185
|
resolve();
|
|
181
186
|
});
|
|
182
187
|
|
|
183
188
|
ssh_client.on('handshake', negotiated => {
|
|
184
|
-
|
|
189
|
+
_this.debug_en &&_this.debug('SSH Client :: handshake:', JSON.stringify(negotiated));
|
|
185
190
|
});
|
|
186
191
|
|
|
187
192
|
ssh_client.on('greeting', (message, language) => {
|
|
188
|
-
|
|
193
|
+
_this.debug_en &&_this.debug('SSH Client :: greeting:', message);
|
|
189
194
|
});
|
|
190
195
|
|
|
191
196
|
// initiate ssh client connection to remote host
|
|
@@ -223,7 +228,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
223
228
|
if (local_ports.length || remote_ports.length) {
|
|
224
229
|
const err = new Error('Invalid local forward');
|
|
225
230
|
|
|
226
|
-
|
|
231
|
+
this.debug_en &&this.debug('invalid ports found:\n', JSON.stringify(proxy_ports, null, 2),
|
|
227
232
|
'\n', JSON.stringify(whitelist, null, 2));
|
|
228
233
|
|
|
229
234
|
err.info = {
|
|
@@ -237,7 +242,11 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
237
242
|
|
|
238
243
|
// on network error, attempt to re-establish connection until 10 retries
|
|
239
244
|
ssh_retry_connect = (opts) => {
|
|
240
|
-
|
|
245
|
+
const _this = this;
|
|
246
|
+
const invoke = () => {
|
|
247
|
+
_this.connectSSH(opts);
|
|
248
|
+
}
|
|
249
|
+
if (this.retries++ < 10) setTimeout(invoke, 5000);
|
|
241
250
|
}
|
|
242
251
|
|
|
243
252
|
// attempt to establish ssh tunnel to server with supplied parameters.
|
|
@@ -248,7 +257,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
248
257
|
// after successful tunnel setup complete, send events and reset retry counter
|
|
249
258
|
await _this.do_ssh_connect(opts).then(() => {
|
|
250
259
|
const hostport = opts.host + ':' + opts.port;
|
|
251
|
-
if (
|
|
260
|
+
if (_this.debug_en) {
|
|
252
261
|
const msg = 'SSH connection to ' + opts.server_name + ' established at ' + hostport;
|
|
253
262
|
_this.debug(msg);
|
|
254
263
|
}
|
|
@@ -256,7 +265,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
256
265
|
_this.retries = 0;
|
|
257
266
|
resolve();
|
|
258
267
|
}, (err) => {
|
|
259
|
-
|
|
268
|
+
_this.debug_en &&_this.debug('ssh_retry error:', err);
|
|
260
269
|
reject();
|
|
261
270
|
});
|
|
262
271
|
});
|
|
@@ -266,7 +275,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
266
275
|
// todo: if opts have changed while service is running, shutdown current service and restart with new opts
|
|
267
276
|
async connectSSH(opts, whitelist) {
|
|
268
277
|
|
|
269
|
-
|
|
278
|
+
this.debug_en &&this.debug('connectSSH:\n', JSON.stringify(opts, null, 2));
|
|
270
279
|
|
|
271
280
|
// make deep copy of opts for modification
|
|
272
281
|
var _opts = JSON.parse(JSON.stringify(opts));
|
|
@@ -279,7 +288,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
279
288
|
this.validate_local_forward(_opts.proxy_ports, _opts.whitelist);
|
|
280
289
|
} catch (err) {
|
|
281
290
|
this.emit('error', err);
|
|
282
|
-
|
|
291
|
+
this.debug_en &&this.debug(err);
|
|
283
292
|
return err;
|
|
284
293
|
}
|
|
285
294
|
|
|
@@ -299,7 +308,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
299
308
|
const ngrokApi = new NgrokApi(_opts.ngrok_api);
|
|
300
309
|
var hostport = await ngrokApi.get_hostport()
|
|
301
310
|
.catch((err) => {
|
|
302
|
-
|
|
311
|
+
_this.debug_en &&_this.debug('ngrok get_hostport connection error');
|
|
303
312
|
if (this.listener[opts.server_name]) this.listener[opts.server_name].isConnected = false;
|
|
304
313
|
_this.ssh_retry_connect(_opts);
|
|
305
314
|
});
|
|
@@ -313,7 +322,7 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
313
322
|
// initiate ssh tunnel, block until tunnel is established or error
|
|
314
323
|
return await this.ssh_start_tunnel(_opts)
|
|
315
324
|
.catch((err) => {
|
|
316
|
-
|
|
325
|
+
this.debug_en &&this.debug('ssh_start_tunnel catch:' + err);
|
|
317
326
|
});
|
|
318
327
|
}
|
|
319
328
|
|
|
@@ -332,10 +341,14 @@ class sshTunnelProxy extends EventEmitter {
|
|
|
332
341
|
}
|
|
333
342
|
|
|
334
343
|
// ssh client debug function for verbose ssh connection details
|
|
335
|
-
debug_client = (debug_ssh) ? (msg) => { console.log(msg); } : null;
|
|
344
|
+
debug_client = (this.debug_ssh) ? (msg) => { console.log(msg); } : null;
|
|
336
345
|
|
|
337
|
-
debug
|
|
346
|
+
debug(...args) { console.log(...args); }
|
|
338
347
|
|
|
339
348
|
}
|
|
340
349
|
|
|
341
|
-
module.exports =
|
|
350
|
+
module.exports = {
|
|
351
|
+
SSHTunnelProxy:sshTunnelProxy,
|
|
352
|
+
KeypairStorage:KeypairStorage,
|
|
353
|
+
NgrokApi:NgrokApi
|
|
354
|
+
}
|
package/lib/keypair_storage.js
CHANGED
|
@@ -74,16 +74,3 @@ class KeypairStorage {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
module.exports = KeypairStorage;
|
|
77
|
-
/*
|
|
78
|
-
module.exports = {
|
|
79
|
-
store_private_key: keytar.setPassword,
|
|
80
|
-
retrieve_private_key: keytar.getPassword,
|
|
81
|
-
remove_keypair: keytar.deletePassword,
|
|
82
|
-
generate_keypair: generate_keypair,
|
|
83
|
-
generate_and_store_keypair: generate_and_store_keypair,
|
|
84
|
-
get_public_key_from_keychain: get_public_key_from_keychain,
|
|
85
|
-
get_keypair: get_keypair,
|
|
86
|
-
set_keypair: set_keypair,
|
|
87
|
-
delete_keypair: delete_keypair
|
|
88
|
-
}
|
|
89
|
-
*/
|
package/main.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const process = require('process');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { SSHTunnelProxy, KeypairStorage } = require('./lib/index.js');
|
|
4
|
+
const { config } = require('process');
|
|
5
|
+
|
|
6
|
+
const keypairStorage = new KeypairStorage();
|
|
7
|
+
const homedir = require('os').homedir();
|
|
8
|
+
var debug = false;
|
|
9
|
+
var proxies = [];
|
|
10
|
+
var configs = null;
|
|
11
|
+
var host = null;
|
|
12
|
+
var port = null;
|
|
13
|
+
var username = null;
|
|
14
|
+
var forwardOut = [];
|
|
15
|
+
var forwardIn = [];
|
|
16
|
+
var ngrokAPIKey = null;
|
|
17
|
+
var privateKey = null;
|
|
18
|
+
var service = null;
|
|
19
|
+
var account = null;
|
|
20
|
+
|
|
21
|
+
function get_key_from_file(filename) {
|
|
22
|
+
try {
|
|
23
|
+
return fs.readFileSync(filename);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.log('Error loading key:', err);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function get_config(filename) {
|
|
30
|
+
try {
|
|
31
|
+
configs = JSON.parse(fs.readFileSync(filename), 'utf8');
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.log('Error loading config file:', err);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function get_default_config() {
|
|
38
|
+
try {
|
|
39
|
+
configs = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.log('Error loading config file:', err);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function main(args) {
|
|
46
|
+
|
|
47
|
+
// parse command line args
|
|
48
|
+
for (let i = 0; i < args.length; i++) {
|
|
49
|
+
const arg = args[i].split('=');
|
|
50
|
+
const cmd = arg[0];
|
|
51
|
+
const value = arg[1];
|
|
52
|
+
switch (cmd) {
|
|
53
|
+
case '-d':
|
|
54
|
+
case '--debug':
|
|
55
|
+
debug = true;
|
|
56
|
+
break;
|
|
57
|
+
case '-c':
|
|
58
|
+
case '--config':
|
|
59
|
+
if (value) get_config(value);
|
|
60
|
+
else get_default_config();
|
|
61
|
+
break;
|
|
62
|
+
case '-h':
|
|
63
|
+
case '--host':
|
|
64
|
+
host = value;
|
|
65
|
+
break;
|
|
66
|
+
case '-p':
|
|
67
|
+
case '--port':
|
|
68
|
+
port = value;
|
|
69
|
+
break;
|
|
70
|
+
case '-u':
|
|
71
|
+
case '--username':
|
|
72
|
+
username = value;
|
|
73
|
+
break;
|
|
74
|
+
case '-P':
|
|
75
|
+
case '--password':
|
|
76
|
+
password = value;
|
|
77
|
+
break;
|
|
78
|
+
case '-k':
|
|
79
|
+
case '--key':
|
|
80
|
+
privateKey = value;
|
|
81
|
+
break;
|
|
82
|
+
case '-s':
|
|
83
|
+
case '--service':
|
|
84
|
+
service = value;
|
|
85
|
+
break;
|
|
86
|
+
case '--a':
|
|
87
|
+
account = value;
|
|
88
|
+
break;
|
|
89
|
+
case '-L':
|
|
90
|
+
case '--LocalForward':
|
|
91
|
+
forwardOut.push(value);
|
|
92
|
+
break;
|
|
93
|
+
case '-R':
|
|
94
|
+
case '--RemoteForward':
|
|
95
|
+
forwardIn.push(value);
|
|
96
|
+
break;
|
|
97
|
+
case '-n':
|
|
98
|
+
case '--ngrok':
|
|
99
|
+
ngrokAPIKey = value;
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
if (i > 1) console.log('Invalid argument:', arg);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// prevent process from exiting
|
|
107
|
+
process.stdin.resume();
|
|
108
|
+
|
|
109
|
+
// if no config file specified, build config from args
|
|
110
|
+
if (!configs) {
|
|
111
|
+
config = {
|
|
112
|
+
enabled: true,
|
|
113
|
+
username: username,
|
|
114
|
+
password: password,
|
|
115
|
+
host: host,
|
|
116
|
+
port: port,
|
|
117
|
+
proxy_ports: forwardOut,
|
|
118
|
+
remote_ports: forwardIn,
|
|
119
|
+
service_name: service,
|
|
120
|
+
server_name: account,
|
|
121
|
+
ngrok_api: ngrokAPIKey,
|
|
122
|
+
private_key: privateKey
|
|
123
|
+
};
|
|
124
|
+
configs = [config];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// process each tunnel config in ssh proxy tunnel list
|
|
128
|
+
configs.forEach(async config => {
|
|
129
|
+
|
|
130
|
+
// skip tunnel if not enabled
|
|
131
|
+
if (!config.enabled) return;
|
|
132
|
+
|
|
133
|
+
// if system key storage name specified get key from system keychain
|
|
134
|
+
if (config.server_name) {
|
|
135
|
+
const service_name = config.service_name || 'ssh_tunnel_proxy';
|
|
136
|
+
config.private_key = await keypairStorage.get_keypair(service_name, config.server_name);
|
|
137
|
+
}
|
|
138
|
+
// if private key filename specified, read private key
|
|
139
|
+
else if (config.private_key) {
|
|
140
|
+
config.private_key = get_key_from_file(config.private_key);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// create new tunnel and start connection
|
|
144
|
+
var sshTunnelProxy = new SSHTunnelProxy();
|
|
145
|
+
proxies.push(sshTunnelProxy);
|
|
146
|
+
sshTunnelProxy.debug_en = debug;
|
|
147
|
+
sshTunnelProxy.connectSSH(config);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
main(process.argv);
|
package/package.json
CHANGED
package/test/test.js
CHANGED
|
@@ -10,13 +10,12 @@ const fs = require('fs');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const sinon = require('sinon');
|
|
12
12
|
|
|
13
|
-
const SSHTunnelProxy = require('../lib/index.js');
|
|
14
|
-
const KeypairStorage = require('../lib/keypair_storage.js');
|
|
15
|
-
const NgrokApi = require('../lib/ngrok_service');
|
|
13
|
+
const { SSHTunnelProxy, KeypairStorage, NgrokApi } = require('../lib/index.js');
|
|
16
14
|
|
|
17
15
|
// get config file containing api keys
|
|
18
16
|
const homedir = require('os').homedir();
|
|
19
|
-
const
|
|
17
|
+
const config = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
|
|
18
|
+
const opts = config[0];
|
|
20
19
|
|
|
21
20
|
const sshTunnelProxy = new SSHTunnelProxy();
|
|
22
21
|
const keypairStorage = new KeypairStorage();
|