@interopio/gateway-server 0.20.0 → 0.22.0

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/gateway-server DELETED
@@ -1,442 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- `use strict`;
4
-
5
- import { writeFileSync } from 'node:fs';
6
- import { configure } from '@interopio/gateway/logging/core';
7
-
8
- // import { GatewayServer } from './src/index.ts';
9
- // import { mkcert, argon2 } from './src/tools/index.ts';
10
-
11
- import { GatewayServer } from './dist/index.js';
12
- import { mkcert, argon2 } from './dist/tools/index.js';
13
-
14
- function showHelp() {
15
- console.log(`
16
- Usage: gateway-server <command> [options]
17
-
18
- Commands:
19
- run Run the gateway server (default if no command specified)
20
- passwd Generate password hash (default using Argon2)
21
- mkcert Generate client and/or server certificate signed by Dev CA
22
-
23
- run options:
24
- -p, --port <port> Specify port or port range to bind the server to (default: 0 i.e. random available port)
25
- examples: 8385, 8000-8100, 3000,4000-4050
26
- -H, --host <host> Network address to bind to (default: unspecified, listens on all interfaces)
27
- examples: localhost, 127.0.0.1, 0.0.0.0, ::1
28
- -u, --user <name> Enables basic authentication and sets the admin username
29
- -S, --ssl, --tls Enable HTTPS. Auto-generates Dev CA and/or server certificates if not present. Enables x509 client cert auth if --user not specified.
30
- --cert <file> File with SSL/TLS certificate (default: ./gateway-server.crt)
31
- --key <file> File with SSL/TLS private key (default: ./gateway-server.key)
32
- --ca <file> File with CA certificates (default: ./gateway-ca.crt)
33
- --ca-key <file> File with CA private key (default: ./gateway-ca.key)
34
- --debug Enable debug logging (default: info level)
35
-
36
- passwd options:
37
- (no args) Prompt for password interactively (masked input)
38
- --stdin Read password from stdin (for piping, e.g., echo "pass" | ...)
39
-
40
- mkcert options:
41
- --client Generate client certificate (default: server certificate)
42
- -u, --user <name> Common Name for the certificate (default: dev-user for client certs) (--client only)
43
- --ca <file> File with CA certificate (default: ./gateway-ca.crt)
44
- --ca-key <file> File with CA private key (default: ./gateway-ca.key)
45
- --key <file> Output file for private key (default: ./gateway-server.key for server, ./gateway-client.key for client)
46
- --cert <file> Output file for certificate (default: ./gateway-server.crt for server, ./gateway-client.crt for client)
47
- [name...] DNS names, IP addresses, or email addresses for subjectAltName
48
- (DNS by default, prefix with 'IP:' or 'EMAIL:' for other types)
49
-
50
- Global Options:
51
- -v, --version Show version information and exit
52
- --help Show this help message and exit
53
-
54
- Examples:
55
- gateway-server run -p 3000
56
- gateway-server run -u admin --port 8385,8388 --debug
57
- gateway-server run -p 8443 --ssl --user admin
58
- gateway-server passwd
59
- echo "mySecret123" | gateway-server passwd --stdin
60
- gateway-server mkcert
61
- gateway-server mkcert localhost 127.0.0.1 IP:192.168.1.100
62
- gateway-server mkcert --client EMAIL:john.doe@example.com
63
- gateway-server mkcert example.com *.example.com --key ./gateway-server.key --cert ./gateway-server.crt
64
- `);
65
- process.exit(0);
66
- }
67
-
68
- function showVersion() {
69
- console.log(GatewayServer.VERSION);
70
- process.exit(0);
71
- }
72
-
73
-
74
- // Parse command-line arguments
75
- const args = process.argv.slice(2);
76
-
77
- // Check for global flags first
78
- if (args.includes('--version') || args.includes('-v')) {
79
- showVersion();
80
- }
81
- if (args.includes('--help') || args.includes('-h')) {
82
- showHelp();
83
- }
84
-
85
- // Determine command (default to 'run' for backward compatibility)
86
- let command = 'run';
87
- let commandArgs = args;
88
-
89
- if (args.length > 0 && !args[0].startsWith('-')) {
90
- command = args[0];
91
- commandArgs = args.slice(1);
92
- }
93
-
94
- // Route to appropriate command handler
95
- switch (command) {
96
- case 'run':
97
- await runServer(commandArgs);
98
- break;
99
- case 'passwd':
100
- await passwdCommand(commandArgs);
101
- break;
102
- case 'mkcert':
103
- await mkcertCommand(commandArgs);
104
- break;
105
- default:
106
- console.error(`Error: Unknown command "${command}"`);
107
- console.log('Use --help to see available commands');
108
- process.exit(1);
109
- }
110
-
111
- // Command: passwd - Generate password hash
112
- async function passwdCommand(args) {
113
- let password = undefined;
114
- let fromStdin = false;
115
-
116
- for (let i = 0; i < args.length; i++) {
117
- if (args[i] === '--stdin') {
118
- fromStdin = true;
119
- } else {
120
- console.error(`Error: Unknown option "${args[i]}"`);
121
- console.log('Use: gateway-server passwd');
122
- console.log(' or: gateway-server passwd --stdin');
123
- process.exit(1);
124
- }
125
- }
126
-
127
- // Read password from stdin if specified
128
- if (fromStdin) {
129
- password = await readPasswordFromStdin();
130
- }
131
-
132
- // If no password provided, prompt interactively
133
- if (!password) {
134
- password = await promptPassword('Enter password: ');
135
- }
136
-
137
- if (!password) {
138
- console.error('Error: password is required');
139
- process.exit(1);
140
- }
141
-
142
- const hashValue = await argon2.hash(password);
143
- const hash = `{argon2id}${hashValue}`;
144
- console.log(hash);
145
- process.exit(0);
146
- }
147
-
148
- // Read password from piped stdin (non-interactive)
149
- async function readPasswordFromStdin() {
150
- // Check if stdin is a TTY (interactive terminal)
151
- if (process.stdin.isTTY) {
152
- // Interactive mode - use readline with hidden input
153
- return promptPassword('Password: ');
154
- }
155
-
156
- // Piped mode - read until EOF or newline
157
- const chunks = [];
158
- for await (const chunk of process.stdin) {
159
- chunks.push(chunk);
160
- }
161
- const content = Buffer.concat(chunks).toString('utf8');
162
- // Strip trailing newline
163
- return content.replace(/\r?\n$/, '');
164
- }
165
-
166
- // Prompt for password with masked input
167
- async function promptPassword(prompt) {
168
- return new Promise((resolve) => {
169
- process.stdout.write(prompt);
170
-
171
- // Only set raw mode if stdin is a TTY
172
- if (!process.stdin.isTTY) {
173
- console.error('Error: Interactive password prompt requires a terminal');
174
- process.exit(1);
175
- }
176
-
177
- process.stdin.setRawMode(true);
178
- process.stdin.resume();
179
- process.stdin.setEncoding('utf8');
180
-
181
- let password = '';
182
-
183
- const cleanup = () => {
184
- process.stdin.setRawMode(false);
185
- process.stdin.pause();
186
- process.stdin.removeListener('data', onData);
187
- process.stdout.write('\n');
188
- };
189
-
190
- const onData = (char) => {
191
- // Ctrl+C
192
- if (char === '\u0003') {
193
- cleanup();
194
- process.exit(1);
195
- }
196
-
197
- // Enter
198
- if (char === '\r' || char === '\n') {
199
- cleanup();
200
- resolve(password);
201
- return;
202
- }
203
-
204
- // Backspace
205
- if (char === '\u007f' || char === '\b') {
206
- if (password.length > 0) {
207
- password = password.slice(0, -1);
208
- process.stdout.write('\b \b');
209
- }
210
- return;
211
- }
212
-
213
- // Regular character
214
- password += char;
215
- process.stdout.write('*');
216
- };
217
-
218
- process.stdin.on('data', onData);
219
- });
220
- }
221
-
222
- // Command: mkcert - Generate client or server certificate
223
- async function mkcertCommand(args) {
224
- let isClientCert = false;
225
- let user = 'dev-user';
226
- let caCert = './gateway-ca.crt';
227
- let caKey = './gateway-ca.key';
228
- let keyPath = undefined;
229
- let certPath = undefined;
230
- const sanEntries = [];
231
-
232
- for (let i = 0; i < args.length; i++) {
233
- if (args[i] === '--client') {
234
- isClientCert = true;
235
- } else if ((args[i] === '--user' || args[i] === '-u') && i + 1 < args.length) {
236
- user = args[i + 1];
237
- i++;
238
- } else if (args[i] === '--ca' && i + 1 < args.length) {
239
- caCert = args[i + 1];
240
- i++;
241
- } else if (args[i] === '--ca-key' && i + 1 < args.length) {
242
- caKey = args[i + 1];
243
- i++;
244
- } else if (args[i] === '--key' && i + 1 < args.length) {
245
- keyPath = args[i + 1];
246
- i++;
247
- } else if (args[i] === '--cert' && i + 1 < args.length) {
248
- certPath = args[i + 1];
249
- i++;
250
- } else if (!args[i].startsWith('-')) {
251
- // Positional argument - add to SAN entries
252
- sanEntries.push(args[i]);
253
- } else {
254
- console.error(`Error: Unknown option "${args[i]}"`);
255
- console.log('Use: gateway-server mkcert [--client] [--user <name>] [--ca <file>] [--ca-key <file>] [--key <file>] [--cert <file>] [name...]');
256
- process.exit(1);
257
- }
258
- }
259
-
260
- // Default output paths
261
- // Users typically specify just --cert, and key should go in the same file
262
- if (!keyPath && !certPath) {
263
- // Neither specified: use defaults based on certificate type
264
- if (isClientCert) {
265
- // Client certificate: combined file by default
266
- keyPath = './gateway-client.key';
267
- certPath = './gateway-client.crt';
268
- } else {
269
- // Server certificate: separate files by default
270
- keyPath = './gateway-server.key';
271
- certPath = './gateway-server.crt';
272
- }
273
- } else if (keyPath && !certPath) {
274
- // Only --key specified: cert goes in the same file (combined)
275
- certPath = keyPath;
276
- } else if (!keyPath && certPath) {
277
- // Only --cert specified: key goes in the same file (combined)
278
- keyPath = certPath;
279
- }
280
- // else: both specified, use as-is
281
-
282
- if (sanEntries.length === 0) {
283
- if (isClientCert) {
284
- sanEntries.push(user);
285
- }
286
- else {
287
- // Default SAN entry for server certs
288
- sanEntries.push('localhost');
289
- }
290
- }
291
-
292
- const { readFileSync } = await import('node:fs');
293
- const { KEYUTIL, X509 } = await import('jsrsasign');
294
-
295
- // Load CA key
296
- let caKeyObj;
297
- try {
298
- const caKeyPem = readFileSync(caKey, 'utf8');
299
- caKeyObj = KEYUTIL.getKey(caKeyPem);
300
- } catch (error) {
301
- console.error(`Error: Cannot read CA key from ${caKey}`);
302
- console.error(error.message);
303
- process.exit(1);
304
- }
305
-
306
- // Extract issuer from CA certificate
307
- let issuer;
308
- try {
309
- const caCertPem = readFileSync(caCert, 'utf8');
310
- const caCertObj = new X509();
311
- caCertObj.readCertPEM(caCertPem);
312
- issuer = caCertObj.getSubjectString();
313
- } catch (error) {
314
- console.error(`Error: Cannot read CA certificate from ${caCert}`);
315
- console.error(error.message);
316
- process.exit(1);
317
- }
318
-
319
- // Generate certificate using SAN entries
320
- const cert = mkcert.generateCert(caKeyObj, issuer, sanEntries, isClientCert);
321
-
322
- // Write files
323
- try {
324
- if (keyPath === certPath) {
325
- // Concatenate cert and key into single file (cert first, then key)
326
- const combined = cert.cert + cert.key;
327
- writeFileSync(keyPath, combined, { mode: 0o600 });
328
- console.log(`${isClientCert ? 'Client' : 'Server'} certificate generated successfully:`);
329
- console.log(` Combined (cert+key): ${keyPath}`);
330
- if (sanEntries.length > 0) {
331
- console.log(` SAN: ${sanEntries.join(', ')}`);
332
- }
333
- } else {
334
- // Write separate files
335
- writeFileSync(keyPath, cert.key, { mode: 0o600 });
336
- writeFileSync(certPath, cert.cert, { mode: 0o644 });
337
- console.log(`${isClientCert ? 'Client' : 'Server'} certificate generated successfully:`);
338
- console.log(` Private key: ${keyPath}`);
339
- console.log(` Certificate: ${certPath}`);
340
- if (sanEntries.length > 0) {
341
- console.log(` SAN: ${sanEntries.join(', ')}`);
342
- }
343
- }
344
-
345
- process.exit(0);
346
- } catch (error) {
347
- console.error(`Error: Cannot write certificate files`);
348
- console.error(error.message);
349
- process.exit(1);
350
- }
351
- }
352
-
353
- // Command: run - Start the server
354
- async function runServer(args) {
355
- const options = {
356
- port: 0,
357
- host: undefined,
358
- user: undefined,
359
- debug: false,
360
- ssl: false,
361
- cert: undefined,
362
- key: undefined,
363
- ca: undefined,
364
- caKey: undefined
365
- };
366
-
367
- for (let i = 0; i < args.length; i++) {
368
- if ((args[i] === '--port' || args[i] === '-p') && i + 1 < args.length) {
369
- options.port = args[i + 1];
370
- i++;
371
- } else if ((args[i] === '--host' || args[i] === '-H') && i + 1 < args.length) {
372
- options.host = args[i + 1];
373
- i++;
374
- } else if ((args[i] === '--user' || args[i] === '-u') && i + 1 < args.length) {
375
- options.user = args[i + 1];
376
- i++;
377
- } else if (args[i] === '--ssl' || args[i] === '-S' || args[i] === '--tls') {
378
- options.ssl = true;
379
- } else if (args[i] === '--cert' && i + 1 < args.length) {
380
- options.cert = args[i + 1];
381
- i++;
382
- } else if (args[i] === '--key' && i + 1 < args.length) {
383
- options.key = args[i + 1];
384
- i++;
385
- } else if (args[i] === '--ca' && i + 1 < args.length) {
386
- options.ca = args[i + 1];
387
- i++;
388
- } else if (args[i] === '--ca-key' && i + 1 < args.length) {
389
- options.caKey = args[i + 1];
390
- i++;
391
- } else if (args[i] === '--debug') {
392
- options.debug = true;
393
- } else {
394
- console.error(`Error: Unknown option "${args[i]}"`);
395
- console.log('Use --help to see available options');
396
- process.exit(1);
397
- }
398
- }
399
-
400
- configure({
401
- level: options.debug ? 'debug' : 'info',
402
- });
403
-
404
- const auth = {
405
- type: options.user ? 'basic' : options.ssl ? 'x509' : 'none'
406
- };
407
-
408
- if (options.user) {
409
- auth.user = { name: options.user, roles: ['admin'] };
410
- }
411
-
412
- if (options.ssl) {
413
- auth.x509 = {
414
- key: options.caKey || './gateway-ca.key',
415
- // principalAltName: 'email',
416
- };
417
- }
418
-
419
- const server = await GatewayServer.Factory({
420
- port: options.port,
421
- host: options.host,
422
- ssl: options.ssl ? {
423
- key: options.key,
424
- cert: options.cert,
425
- ca: options.ca,
426
- requestCert: auth.type === 'x509',
427
- rejectUnauthorized: false
428
- } : undefined,
429
- auth,
430
- gateway: { route: '/gw', authorize: { access: auth.type !== 'none' ? 'authenticated' : 'permitted' } },
431
- app: async (_configurer) => {
432
- // No custom app logic for now
433
- },
434
- });
435
-
436
- process.on('SIGINT', async () => {
437
- await server.close();
438
- process.exit(0);
439
- });
440
-
441
- process.title = `${GatewayServer.VERSION} ${server.address.port}`;
442
- }