ssh_tunnel_proxy 1.2.10 → 2.0.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.
Files changed (39) hide show
  1. package/README.md +144 -67
  2. package/dist/cjs/index.d.ts +65 -0
  3. package/dist/cjs/index.js +496 -0
  4. package/dist/cjs/index.js.map +1 -0
  5. package/dist/cjs/keypair_storage.d.ts +12 -0
  6. package/dist/cjs/keypair_storage.js +60 -0
  7. package/dist/cjs/keypair_storage.js.map +1 -0
  8. package/dist/cjs/ngrok_service.d.ts +11 -0
  9. package/dist/cjs/ngrok_service.js +40 -0
  10. package/dist/cjs/ngrok_service.js.map +1 -0
  11. package/dist/cjs/ssh2-node.d.ts +1 -0
  12. package/dist/cjs/ssh2-node.js +188 -0
  13. package/dist/cjs/ssh2-node.js.map +1 -0
  14. package/dist/esm/index.d.ts +65 -0
  15. package/dist/esm/index.js +491 -0
  16. package/dist/esm/index.js.map +1 -0
  17. package/dist/esm/keypair_storage.d.ts +12 -0
  18. package/dist/esm/keypair_storage.js +57 -0
  19. package/dist/esm/keypair_storage.js.map +1 -0
  20. package/dist/esm/ngrok_service.d.ts +11 -0
  21. package/dist/esm/ngrok_service.js +37 -0
  22. package/dist/esm/ngrok_service.js.map +1 -0
  23. package/dist/esm/ssh2-node.d.ts +1 -0
  24. package/dist/esm/ssh2-node.js +184 -0
  25. package/dist/esm/ssh2-node.js.map +1 -0
  26. package/dist/types/index.d.ts +65 -0
  27. package/dist/types/keypair_storage.d.ts +12 -0
  28. package/dist/types/ngrok_service.d.ts +11 -0
  29. package/dist/types/ssh2-node.d.ts +1 -0
  30. package/package.json +38 -8
  31. package/ssh2-node +2 -0
  32. package/.eslintrc.json +0 -15
  33. package/.vscode/launch.json +0 -34
  34. package/lib/index.js +0 -472
  35. package/lib/keypair_storage.js +0 -76
  36. package/lib/ngrok_service.js +0 -47
  37. package/main.js +0 -176
  38. package/test/test.js +0 -214
  39. package/test/test_remote_exec.js +0 -219
package/main.js DELETED
@@ -1,176 +0,0 @@
1
- const process = require('process');
2
- const fs = require('fs');
3
- const { SSHTunnelProxy, KeypairStorage } = require('./lib/index.js');
4
-
5
- const keypairStorage = new KeypairStorage();
6
- const homedir = require('os').homedir();
7
- var debug = false;
8
- var exec = [];
9
- var remote_host = null;
10
- var proxies = [];
11
- var configs = null;
12
- var host = null;
13
- var port = null;
14
- var username = null;
15
- var password = null;
16
- var forwardOut = [];
17
- var forwardIn = [];
18
- var ngrokAPIKey = null;
19
- var privateKey = null;
20
- var service = null;
21
- var account = null;
22
- var shell = null;
23
-
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
- function get_config(filename) {
33
- try {
34
- configs = JSON.parse(fs.readFileSync(filename), 'utf8');
35
- } catch (err) {
36
- console.log('Error loading config file:', err);
37
- }
38
- }
39
-
40
- function get_default_config() {
41
- try {
42
- configs = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
43
- } catch (err) {
44
- console.log('Error loading config file:', err);
45
- }
46
- }
47
-
48
- function main(args) {
49
-
50
- // parse command line args
51
- for (let i = 0; i < args.length; i++) {
52
- const arg = args[i].split('=');
53
- const cmd = arg[0];
54
- const value = arg[1];
55
- switch (cmd) {
56
- case '--a':
57
- account = value;
58
- break;
59
- case '-c':
60
- case '--config':
61
- if (value) get_config(value);
62
- else get_default_config();
63
- break;
64
- case '-d':
65
- case '--debug':
66
- debug = true;
67
- break;
68
- case '-e':
69
- case '--exec':
70
- exec.push(value);
71
- break;
72
- case '-h':
73
- case '--host':
74
- host = value;
75
- break;
76
- case '-p':
77
- case '--port':
78
- port = value;
79
- break;
80
- case '-P':
81
- case '--password':
82
- password = value;
83
- break;
84
- case '-k':
85
- case '--key':
86
- privateKey = value;
87
- break;
88
- case '-L':
89
- case '--LocalForward':
90
- forwardOut.push(value);
91
- break;
92
- case '-n':
93
- case '--ngrok':
94
- ngrokAPIKey = value;
95
- break;
96
- case '-r':
97
- case '--remote':
98
- remote_host = value;
99
- break;
100
- case '-R':
101
- case '--RemoteForward':
102
- forwardIn.push(value);
103
- break;
104
- case '-s':
105
- case '--service':
106
- service = value;
107
- break;
108
- case '-S':
109
- case '--shell':
110
- shell = true;
111
- break;
112
- case '-u':
113
- case '--username':
114
- username = value;
115
- break;
116
- default:
117
- if (i > 1) console.log('Invalid argument:', arg);
118
- }
119
- }
120
-
121
- // prevent process from exiting
122
- process.stdin.resume();
123
-
124
- // if no config file specified, build config from args
125
- if (!configs) {
126
- var config = {
127
- enabled: true,
128
- username: username,
129
- password: password,
130
- host: host,
131
- port: port,
132
- proxy_ports: forwardOut,
133
- remote_ports: forwardIn,
134
- service_name: service,
135
- server_name: account,
136
- ngrok_api: ngrokAPIKey,
137
- private_key: privateKey
138
- };
139
- configs = [config];
140
- }
141
-
142
- // process each tunnel config in ssh proxy tunnel list
143
- configs.forEach(async config => {
144
-
145
- // skip tunnel if not enabled
146
- if (!config.enabled) return;
147
-
148
- // if remote host specified, only select tunnel for remote host
149
- if (remote_host && remote_host !== config.alias) return;
150
-
151
- // if system key storage name specified get key from system keychain
152
- if (config.server_name) {
153
- const service_name = config.service_name || 'ssh_tunnel_proxy';
154
- config.private_key = await keypairStorage.get_keypair(service_name, config.server_name);
155
- }
156
- // if private key filename specified, read private key
157
- else if (config.private_key) {
158
- config.private_key = get_key_from_file(config.private_key);
159
- }
160
- if (shell) {
161
- config.shell = true;
162
- }
163
- if (exec.length > 0) {
164
- config.exec = exec;
165
- }
166
- // create new tunnel and start connection
167
- var sshTunnelProxy = new SSHTunnelProxy();
168
- proxies.push(sshTunnelProxy);
169
- sshTunnelProxy.debug_en = debug;
170
- sshTunnelProxy.connectSSH(config);
171
- });
172
-
173
-
174
- }
175
-
176
- main(process.argv);
package/test/test.js DELETED
@@ -1,214 +0,0 @@
1
- /*
2
- todo:
3
- add generate and store keys test
4
- add ngrok api test with stored api key in ssh_tunnel_proxy config
5
- to run ssh tunnel api tests edit config.json and provide api keys for each service
6
- */
7
-
8
- const assert = require('assert');
9
- const { describe, it } = require('mocha');
10
- const fs = require('fs');
11
-
12
- const { SSHTunnelProxy, KeypairStorage, NgrokApi } = require('../lib/index.js');
13
-
14
- // get config file containing api keys
15
- const homedir = require('os').homedir();
16
- const config = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
17
- const opts = config[0];
18
-
19
- const sshTunnelProxy = new SSHTunnelProxy();
20
- const keypairStorage = new KeypairStorage();
21
- const ngrokApi = new NgrokApi(opts.ngrok_api);
22
-
23
- const whitelist = {
24
- 80: true,
25
- 443: true,
26
- 22: true
27
- }
28
-
29
- // port value validation tests
30
- describe('Validate ports', function () {
31
- //const validate_port_number = sshTunnelProxy.__get__('validate_port_number');
32
- it('valid port number 1024', function () {
33
- assert(sshTunnelProxy.validate_port_number(1024, whitelist), 'should be valid');
34
- });
35
- it('valid port number 80', function () {
36
- assert(sshTunnelProxy.validate_port_number(80, whitelist), 'should be valid');
37
- });
38
- it('invalid port number -1', function () {
39
- assert.equal(false, sshTunnelProxy.validate_port_number(-1, whitelist), 'should be invalid');
40
- });
41
- it('invalid port number 65536', function () {
42
- assert.equal(false, sshTunnelProxy.validate_port_number(65536, whitelist), 'should be invalid');
43
- });
44
- it('invalid system port number 137', function () {
45
- assert.equal(false, sshTunnelProxy.validate_port_number(137, whitelist), 'should be invalid');
46
- });
47
- it('port number nan', function () {
48
- assert.equal(false, sshTunnelProxy.validate_port_number('nan', whitelist), 'should be invalid');
49
- });
50
- });
51
-
52
- // local forward format and value validation test
53
- describe('Validate local forwards', function () {
54
- //const validate_local_forward = sshTunnelProxy.__get__('validate_local_forward');
55
- var local_forward = [
56
- '8080:127.0.0.1:80',
57
- '9000:192.168.43.5:9000',
58
- '9000:192.168.43.5',
59
- '192.168.43.5:9000',
60
- '8137:127.0.0.1:137',
61
- '-1:127.0.0.1:65537',
62
- ];
63
- it('valid local forward to 80 ' + local_forward[0], function () {
64
- assert(() => { return sshTunnelProxy.validate_local_forward([local_forward[0]], whitelist) }, 'should be valid');
65
- });
66
- it('valid local forward to 9000 ' + local_forward[1], function () {
67
- assert(() => { return sshTunnelProxy.validate_local_forward([local_forward[1]], whitelist) }, 'should be valid');
68
- });
69
- it('invalid local forward format ' + local_forward[2], function () {
70
- const err = {
71
- message: 'Invalid local forward',
72
- info: {
73
- local_ports: '',
74
- remote_ports: '',
75
- }
76
- };
77
- assert.throws(() => { sshTunnelProxy.validate_local_forward([local_forward[2]], whitelist) }, err);
78
- });
79
- it('invalid local forward format ' + local_forward[3], function () {
80
- const err = {
81
- message: 'Invalid local forward',
82
- info: {
83
- local_ports: '192.168.43.5',
84
- remote_ports: '',
85
- }
86
- };
87
- assert.throws(() => { sshTunnelProxy.validate_local_forward([local_forward[3]], whitelist) }, err, 'should be invalid');
88
- });
89
- it('invalid local forward system port ' + local_forward[4], function () {
90
- const err = {
91
- message: 'Invalid local forward',
92
- info: {
93
- local_ports: '',
94
- remote_ports: '137',
95
- }
96
- };
97
- assert.throws(() => { sshTunnelProxy.validate_local_forward([local_forward[4]], whitelist) }, err, 'should be invalid');
98
- });
99
- it('invalid local forward port range ' + local_forward[5], function () {
100
- const err = {
101
- message: 'Invalid local forward',
102
- info: {
103
- local_ports: '-1',
104
- remote_ports: '65537',
105
- }
106
- };
107
- assert.throws(() => { sshTunnelProxy.validate_local_forward([local_forward[5]], whitelist) }, err, 'should be invalid');
108
- });
109
- });
110
-
111
- // generate, store and retrieve private key test
112
- describe('Store and retrieve private key in system keychain', function () {
113
- const service_name = 'ssh_proxy_tunnel';
114
- const account = 'test';
115
- const keypair = keypairStorage.generate_keypair();
116
- it('generated key should contain a private key', function () {
117
- assert(keypair.private_key, 'should not be empty');
118
- });
119
- it('generated key should contain a public key', function () {
120
- assert(keypair.public_key, 'should not be empty');
121
- });
122
- it('should store generated private key', () => {
123
- return keypairStorage.set_keypair(service_name, account, keypair.private_key).then(result => {
124
- assert.equal(result, undefined, 'should not return error');
125
- })
126
- });
127
- it('stored private key should match generated key', () => {
128
- return keypairStorage.get_keypair(service_name, account).then(stored_key => {
129
- assert.equal(stored_key, keypair.private_key, 'should match');
130
- });
131
- });
132
- it('stored public key should match generated key', () => {
133
- return keypairStorage.get_public_key_from_keychain(service_name, account).then(stored_key => {
134
- assert.equal(stored_key, keypair.public_key, 'should match');
135
- });
136
- });
137
- it('remove key should return true', () => {
138
- return keypairStorage.delete_keypair(service_name, account).then((result) => {
139
- assert(result, 'should return true');
140
- });
141
- });
142
- it('retrieve stored private key after delete should return null', () => {
143
- return keypairStorage.get_keypair(service_name, account).then((result) => {
144
- assert.equal(result, null, 'should return null');
145
- });
146
- });
147
- it('retrieve stored public key after delete should return null', () => {
148
- return keypairStorage.get_public_key_from_keychain(service_name, account).then((result) => {
149
- assert.equal(result, null, 'should return null');
150
- });
151
- });
152
- });
153
-
154
- /*
155
- // generate and store private key test
156
- describe('', function () {
157
- it('', function () {
158
- assert('', '');
159
- });
160
- });
161
- */
162
-
163
- describe('Get ngrok hostport', function () {
164
- //const parse_ngrok_hostport = ngrok_service.__get__('parse_ngrok_hostport');
165
- var test_endpoint = [{
166
- hostport: '8.tcp.ngrok.io:17632'
167
- }];
168
- var result_opts = ngrokApi.parse_ngrok_hostport(test_endpoint, opts);
169
- it('host should match 8.tcp.ngrok.io', function () {
170
- assert.equal(result_opts.host, '8.tcp.ngrok.io');
171
- });
172
- it('port should match 17632', function () {
173
- assert.equal(result_opts.port, '17632');
174
- });
175
- it('host, port should be obtained from api', async () => {
176
- const result = await ngrokApi.get_hostport(opts);
177
- assert(result.host, 'host should exist');
178
- assert(result.port, 'port should exist');
179
- }, (err) => {
180
- assert.equal(err, null);
181
- });
182
-
183
- });
184
-
185
- /*
186
- describe('ssh connection client ready', function () {
187
- });
188
-
189
- it('myEmitter should be enabled', function () {
190
- assert(myEmitter, 'received event emitter');
191
- });
192
-
193
- describe('ssh connection integration test', function () {
194
-
195
- it('ssh connect should invoke the ssh ready function', function (done) {
196
- this.timeout(10000);
197
- myEmitter.on('ssh_client_ready', (ssh_client) => {
198
- assert(ssh_client, 'ssh client created');
199
- done();
200
- });
201
- });
202
- var ws_socket = false;
203
- it('two websocket servers should be created', function (done) {
204
- this.timeout(18000);
205
- myEmitter.on('websocket_server_created', (socket) => {
206
- assert(socket, 'ssh websocket server created:' + socket.remotePort);
207
- if (!ws_socket) done();
208
- ws_socket = true;
209
- });
210
- });
211
- connect_ssh(opts);
212
-
213
- });
214
- */
@@ -1,219 +0,0 @@
1
- /*
2
- test exec cmd on remote server, parse to json object stream
3
- */
4
-
5
- //const assert = require('assert');
6
- //const { describe, it } = require('mocha');
7
-
8
- const fs = require('fs');
9
- const process = require('node:process');
10
- const { SSHTunnelProxy } = require('..');
11
- const { PassThrough, pipeline } = require("stream");
12
- const through = require('through');
13
- const split = require('split');
14
-
15
- // get config file containing api keys
16
- const homedir = require('os').homedir();
17
- var config = null;
18
- try {
19
- config = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
20
- } catch (err) {
21
- console.log('Error reading config:' + err);
22
- process.exit();
23
- }
24
-
25
- if (!config && !config[1]) {
26
- console.log('Specified config not found in configs');
27
- process.exit();
28
- }
29
-
30
- const opts = config[1];
31
-
32
- const sshTunnelProxy = new SSHTunnelProxy();
33
-
34
- // parse shell command result into array of strings
35
- function parse_cmd(str) {
36
-
37
- let result = [];
38
-
39
- let regex = /(([\w-/_~.\:\[\]]+)|("(.*?)")|('(.*?)'))/g;
40
- let groups = [2, 4, 6];
41
- let match;
42
-
43
- while ((match = regex.exec(str)) !== null) {
44
- // This is necessary to avoid infinite loops
45
- // with zero-width matches
46
- if (match.index === regex.lastIndex) {
47
- regex.lastIndex++;
48
- }
49
-
50
- // For this to work the regex groups need to
51
- // be mutually exclusive
52
- groups.forEach(function (group) {
53
- if (match[group]) {
54
- result.push(match[group]);
55
- }
56
- });
57
- /*
58
- let log_matches = false;
59
- // show matches for debugging
60
- log_matches && match.forEach(function (m, group) {
61
- if (m) {
62
- console.log(`Match '${m}' found in group: ${group}`);
63
- }
64
- });
65
- */
66
- }
67
- return result;
68
- }
69
-
70
- // generate object from arrays of names/values
71
- function toObj(names, values) {
72
- const obj = {};
73
- for (var i = 0; i < names.length; i++) {
74
- obj[names[i]] = values[i];
75
- }
76
- return obj;
77
- }
78
-
79
-
80
- // merge array items beginning at start into single item and append back to array
81
- function mergeArray(ar, start) {
82
- const ar2 = ar.slice(start);
83
- const ar1 = ar.slice(0, start);
84
- const item = ar2.join(' ');
85
- ar1.push(item);
86
- return ar1;
87
- }
88
-
89
- /*
90
- Feb 4 21:34:17 tim-ThinkPad-P50s systemd[1]: Started Run anacron jobs.
91
- */
92
- function parseSyslog(line) {
93
- const format = ['M', 'D', 'T', 'host', 'proc', 'msg'];
94
- const values = parse_cmd(line);
95
- const log = mergeArray(values, 5);
96
- const obj = toObj(format, log);
97
- if (obj.M && obj.D && obj.T) {
98
- obj.date = obj.M + ' ' + obj.D + ' ' + obj.T;
99
- delete obj.M;
100
- delete obj.D;
101
- delete obj.T;
102
- }
103
- return obj;
104
- }
105
-
106
- // parse ls long line output into json object
107
- function processLSLong(line) {
108
- const format10 = ['pm', 'links', 'user', 'group', 'size', 'M', 'D', 'H', 'MM', 'name'];
109
- const format9 = ['pm', 'links', 'user', 'group', 'size', 'M', 'D', 'Y', 'name'];
110
- const values = parse_cmd(line);
111
- const obj = toObj((values.length < 10) ? format9 : format10, values);
112
- if (obj.pm) {
113
- obj.type = obj.pm.substring(0, 1);
114
- obj.pm = obj.pm.substring(1);
115
- }
116
- if (obj.M && obj.D) {
117
- obj.date = obj.M + ' ' + obj.D;
118
- delete obj.M;
119
- delete obj.D;
120
- if (obj.Y) {
121
- obj.date += ' ' + obj.Y;
122
- delete obj.Y;
123
- }
124
- }
125
- if (obj.H && obj.MM) {
126
- obj.time = obj.H + ':' + obj.MM;
127
- delete obj.H;
128
- delete obj.MM;
129
- }
130
- return obj;
131
- }
132
-
133
- function to_lsParse() {
134
- return new through(function (data) {
135
- const obj = processLSLong(data);
136
- if (obj.name) this.queue(obj);
137
- });
138
- }
139
-
140
- function to_syslogParse() {
141
- return new through(function (data) {
142
- const obj = parseSyslog(data);
143
- if (obj.proc) this.queue(obj);
144
- });
145
- }
146
-
147
- function to_JSONString() {
148
- return new through(function (data) {
149
- this.queue(JSON.stringify(data) + '\n');
150
- })
151
- }
152
-
153
- // lsLongShellProc conversion tests
154
- async function lsTest(sshTunnelProxy) {
155
- return new Promise(async (resolve) => {
156
-
157
- // exec remote command and pipe data through tunnel until end of data
158
- const tunnel = new PassThrough();
159
- pipeline(tunnel,
160
- split(),
161
- to_lsParse(),
162
- to_JSONString(),
163
- process.stdout,
164
- () => { }
165
- );
166
-
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
- console.log('\ninvoking ' + lscmd + ' on remote host:\n');
170
- await sshTunnelProxy.execCmd(lscmd, tunnel);
171
-
172
- // stream processing complete
173
- console.log('ls -all completed');
174
- resolve();
175
- })
176
- }
177
-
178
- // lsLongShellProc conversion tests
179
- async function syslogTest(sshTunnelProxy) {
180
- return new Promise(async (resolve) => {
181
-
182
- // exec remote command and pipe data through tunnel until end of data
183
- const tunnel = new PassThrough();
184
- pipeline(tunnel,
185
- split(),
186
- to_syslogParse(),
187
- to_JSONString(),
188
- process.stdout,
189
- () => { }
190
- );
191
- //const syslogcmd = 'tail /var/log/syslog | column --table --table-columns-limit 6 --table-columns month,day,time,host,proc,msg -J';
192
- const syslogcmd = 'tail /var/log/syslog';
193
- console.log('\ninvoking ' + syslogcmd + ' on remote host:\n');
194
- await sshTunnelProxy.execCmd(syslogcmd, tunnel);
195
-
196
- // stream processing complete
197
- console.log(syslogcmd+' completed');
198
- resolve();
199
- })
200
- }
201
-
202
- async function runTests() {
203
-
204
- // connect to remote host
205
- sshTunnelProxy.debug_en = true;
206
- await sshTunnelProxy.connectSSH(opts);
207
-
208
- // invoke ls -all on remote host and parse result to json object string
209
- await lsTest(sshTunnelProxy);
210
-
211
- // invoke tail /var/log/syslog on remote host and parse result to json object string
212
- await syslogTest(sshTunnelProxy);
213
-
214
- process.exit();
215
- }
216
-
217
- runTests();
218
-
219
-