ssh_tunnel_proxy 1.2.11 → 2.0.3
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 +86 -5
- package/dist/cjs/index.d.ts +65 -0
- package/dist/cjs/index.js +486 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/keypair_storage.d.ts +12 -0
- package/dist/cjs/keypair_storage.js +120 -0
- package/dist/cjs/keypair_storage.js.map +1 -0
- package/dist/cjs/ngrok_service.d.ts +11 -0
- package/dist/cjs/ngrok_service.js +40 -0
- package/dist/cjs/ngrok_service.js.map +1 -0
- package/dist/cjs/ssh2-node.d.ts +1 -0
- package/dist/cjs/ssh2-node.js +188 -0
- package/dist/cjs/ssh2-node.js.map +1 -0
- package/dist/esm/index.d.ts +65 -0
- package/dist/esm/index.js +481 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/keypair_storage.d.ts +12 -0
- package/dist/esm/keypair_storage.js +117 -0
- package/dist/esm/keypair_storage.js.map +1 -0
- package/dist/esm/ngrok_service.d.ts +11 -0
- package/dist/esm/ngrok_service.js +37 -0
- package/dist/esm/ngrok_service.js.map +1 -0
- package/dist/esm/ssh2-node.d.ts +1 -0
- package/dist/esm/ssh2-node.js +184 -0
- package/dist/esm/ssh2-node.js.map +1 -0
- package/dist/types/index.d.ts +65 -0
- package/dist/types/keypair_storage.d.ts +12 -0
- package/dist/types/ngrok_service.d.ts +11 -0
- package/dist/types/ssh2-node.d.ts +1 -0
- package/package.json +39 -9
- package/ssh2-node +2 -181
- package/.eslintrc.json +0 -15
- package/.vscode/launch.json +0 -34
- package/lib/index.js +0 -489
- package/lib/keypair_storage.js +0 -76
- package/lib/ngrok_service.js +0 -47
- package/test/test.js +0 -214
- package/test/test_remote_exec.js +0 -217
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
|
-
*/
|
package/test/test_remote_exec.js
DELETED
|
@@ -1,217 +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
|
-
console.log('\ninvoking ' + lscmd + ' on remote host:\n');
|
|
169
|
-
await sshTunnelProxy.execCmd(lscmd, tunnel);
|
|
170
|
-
|
|
171
|
-
// stream processing complete
|
|
172
|
-
console.log('ls -all completed');
|
|
173
|
-
resolve();
|
|
174
|
-
})
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// lsLongShellProc conversion tests
|
|
178
|
-
async function syslogTest(sshTunnelProxy) {
|
|
179
|
-
return new Promise(async (resolve) => {
|
|
180
|
-
|
|
181
|
-
// exec remote command and pipe data through tunnel until end of data
|
|
182
|
-
const tunnel = new PassThrough();
|
|
183
|
-
pipeline(tunnel,
|
|
184
|
-
split(),
|
|
185
|
-
to_syslogParse(),
|
|
186
|
-
to_JSONString(),
|
|
187
|
-
process.stdout,
|
|
188
|
-
() => { }
|
|
189
|
-
);
|
|
190
|
-
const syslogcmd = 'tail /var/log/syslog';
|
|
191
|
-
console.log('\ninvoking ' + syslogcmd + ' on remote host:\n');
|
|
192
|
-
await sshTunnelProxy.execCmd(syslogcmd, tunnel);
|
|
193
|
-
|
|
194
|
-
// stream processing complete
|
|
195
|
-
console.log(syslogcmd+' completed');
|
|
196
|
-
resolve();
|
|
197
|
-
})
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
async function runTests() {
|
|
201
|
-
|
|
202
|
-
// connect to remote host
|
|
203
|
-
sshTunnelProxy.debug_en = true;
|
|
204
|
-
await sshTunnelProxy.connectSSH(opts);
|
|
205
|
-
|
|
206
|
-
// invoke ls -all on remote host and parse result to json object string
|
|
207
|
-
await lsTest(sshTunnelProxy);
|
|
208
|
-
|
|
209
|
-
// invoke tail /var/log/syslog on remote host and parse result to json object string
|
|
210
|
-
await syslogTest(sshTunnelProxy);
|
|
211
|
-
|
|
212
|
-
process.exit();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
runTests();
|
|
216
|
-
|
|
217
|
-
|