UNPKG

@interopio/gateway-server

Version:

[![npm version](https://img.shields.io/npm/v/@interopio/gateway-server.svg)](https://www.npmjs.com/package/@interopio/gateway-server)

351 lines (280 loc) 12.9 kB
# io.Gateway Server [![npm version](https://img.shields.io/npm/v/@interopio/gateway-server.svg)](https://www.npmjs.com/package/@interopio/gateway-server) ## Overview The `@interopio/gateway-server` package is the web server used to run the gateway as a standalone server app accessible via WebSocket. The server is general purpose http server with support for: - HTTP and HTTPS - CORS configuration - Basic, mTLS, and OAuth2 authentication - Custom API routes (both for HTTP and WebSocket) ```shell npm install @interopio/gateway-server ``` ## Table of Contents - [Getting Started](#getting-started) - [Configure HTTPS](#configure-https) - [Management](#management) - [@glue42/gateway-ent Compatibility](#glue42gateway-ent-compatibility) ## Getting Started ### CLI: ```shell npx @interopio/gateway-server run --port 8385 --gateway ``` ### API: ```typescript import GatewayServer, { type Server } from '@interopio/gateway-server'; const server: Server = await GatewayServer({ port: 8385, gateway: { route: '/gw', authorize: { access: 'authenticated' }, ping: { interval: 30000, // 30 seconds data: 'timestamp' } } }); server.address; // Bound address info server.gateway; // Gateway instance await server.close(); ``` ## Configure HTTPS SSL/TLS configuration supports PEM-formatted certificates. Two modes are available: **1. Explicit certificates** - Provide your own key and certificate files (both must exist): ```typescript const server = await GatewayServer({ port: 8443, ssl: { key: "./ssl/gateway-server.key", cert: "./ssl/gateway-server.crt", passphrase: "secret" // optional, if key is encrypted } }); ``` > **Recommended for production:** Generate your own server certificates using a trusted CA or create your own CA infrastructure. See [Generating Server Certificates with OpenSSL](#generating-server-certificates-with-openssl) below. **2. Auto-generated server certificates** - Provide a CA key via `auth.x509.key` to auto-generate server certificates: > **⚠️ Development only:** Auto-generated certificates are intended for development and testing. For production environments, use explicit certificates (mode 1) with proper certificate management. > **Note:** The CA private key is stored under `auth.x509.key` since its primary purpose is generating client certificates for X.509 authentication. It is incidentally used to generate self-signed server certificates when no explicit server cert is provided and ssl is enabled. ```typescript // Option A: Specify custom CA paths await GatewayServer({ port: 8443, host: "example.com", // optional: used in generated certificate's CN and SAN ssl: { ca: "./ssl/gateway-ca.crt", // CA certificate for client verification (optional) key: "./ssl/gateway-server.key", // optional: save generated server key to file cert: "./ssl/gateway-server.crt", // optional: save generated server cert to file passphrase: undefined // optional, if key is encrypted }, auth: { type: 'x509', x509: { key: "./ssl/gateway-ca.key", // CA private key (auto-generates CA if missing) passphrase: undefined, // optional, if CA key is encrypted } } }); // Option B: Use default development CA (simplest for development) await GatewayServer({ port: 8443, ssl: {}, auth: { type: 'x509', x509: { key: "./ssl/gateway-ca.key" // ca cert derives to ./ssl/gateway-ca.crt } } }); // Option C: Minimal mode (uses gateway-ca.key and gateway-ca.crt) await GatewayServer({ port: 8443, ssl: {}, auth: { type: 'x509', x509: { key: undefined // defaults to gateway-ca.key in current directory } } }); ``` > **Client Configuration:** For mode 2, distribute the Root CA certificate (`.crt` file) to clients. Clients must import this CA into their trust store. Server certificates are regenerated on each startup (in memory or saved to disk if key/cert paths specified) and are automatically trusted by clients who trust the Root CA. > > **Auto-generation:** If CA files don't exist (specified in `auth.x509.key`), they are automatically generated and saved to disk. The CA certificate path is derived from the key path by replacing the extension with `.crt` (if not explicitly specified via `ssl.ca`). The CA is valid for 10 years and uses ECDSA secp384r1. Server certificates are regenerated on startup with 7-day validity. > > **Hostname in certificates:** When auto-generating server certificates (mode 2), the `host` parameter (if specified) is used as the Common Name (CN) and in the Subject Alternative Name (SAN) of the certificate. If `host` is not specified, defaults to `localhost`. > > **Note:** When both `ssl.key` and `ssl.crt` files exist, mode 1 is used (explicit certificates). Otherwise mode 2 is used (auto-generated from CA key in `auth.x509.key`). **For production deployments, always use mode 1 with properly managed certificates.** ### Generating Server Certificates with OpenSSL To generate your own server certificates for mode 1 (explicit certificates) using OpenSSL: > **Production Best Practice:** Use this approach to create properly signed certificates for your production environment. Ensure certificates are renewed before expiration and follow your organization's certificate management policies. ```shell # Set the domain name for the server certificate DOMAIN=gateway.localhost # Set paths to your CA certificate and key CA_CERT=./ssl/gateway-ca.crt CA_KEY=./ssl/gateway-ca.key # Generate server private key (ECDSA secp256r1) openssl ecparam -name prime256v1 -genkey -noout -out ./ssl/gateway-server.key # Create a certificate signing request (CSR) with extensions openssl req -new -key ./ssl/gateway-server.key -out ./ssl/gateway-server.csr \ -subj "/CN=${DOMAIN}" \ -addext "basicConstraints=CA:FALSE" \ -addext "keyUsage=critical,digitalSignature,keyEncipherment" \ -addext "extendedKeyUsage=serverAuth" \ -addext "subjectAltName=DNS:${DOMAIN},DNS:*.${DOMAIN}" # Sign the CSR with your Root CA openssl x509 -req -in ./ssl/gateway-server.csr \ -CA ${CA_CERT} -CAkey ${CA_KEY} \ -out ./ssl/gateway-server.crt -days 365 -sha256 \ -copy_extensions copyall ``` > **Important:** The `subjectAltName` with DNS entries is **required** for server certificates. Modern browsers and clients reject certificates without SAN, even if the Common Name (CN) matches the hostname. ### Mutual TLS (Client Certificate Authentication) Enable client certificate verification for mutual TLS authentication: ```typescript const server = await GatewayServer({ port: 8443, ssl: { key: "./ssl/gateway-server.key", // Server private key cert: "./ssl/gateway-server.crt", // Server certificate ca: "./ssl/gateway-client-ca.crt", // CA that signed client certificates requestCert: true, // Ask clients to send certificates rejectUnauthorized: true // Reject clients without valid certs } }); ``` **Configuration options:** - `requestCert: false` (default) - Server does not request client certificates - `requestCert: true, rejectUnauthorized: false` - Client certs optional (allow anonymous) - `requestCert: true, rejectUnauthorized: true` - Client certs required (enforce mutual TLS) > **Note:** When `requestCert` is `false`, clients will not send certificates even if they have them. The server must explicitly request them during the TLS handshake. #### Generating Client Certificates with OpenSSL To generate client certificates for mutual TLS authentication using OpenSSL: ```shell # Set paths to your CA certificate and key CA_CERT=./ssl/gateway-ca.crt CA_KEY=./ssl/gateway-ca.key # Generate client private key (ECDSA secp256r1) openssl ecparam -name prime256v1 -genkey -noout -out ./ssl/gateway-client.key # Create a certificate signing request (CSR) with extensions openssl req -new -key ./ssl/gateway-client.key -out ./ssl/gateway-client.csr \ -subj "/CN=dev-user" \ -addext "basicConstraints=CA:FALSE" \ -addext "keyUsage=critical,digitalSignature,keyEncipherment" \ -addext "extendedKeyUsage=clientAuth" \ -addext "subjectAltName=email:test@example.com" # Sign the CSR with your Root CA openssl x509 -req -in ./ssl/gateway-client.csr \ -CA ${CA_CERT} -CAkey ${CA_KEY} \ -out ./ssl/gateway-client.crt -days 365 -sha256 \ -copy_extensions copyall openssl pkcs12 -export -out ./ssl/gateway-client.p12 \ -inkey ./ssl/gateway-client.key -in ./ssl/gateway-client.crt \ -passout pass:changeit ``` > **Important:** The `extendedKeyUsage=clientAuth` is critical for client certificates. Without it, the certificate may be rejected during mutual TLS authentication. ### Full Example with Authentication ```typescript import GatewayServer, {type Server} from '@interopio/gateway-server'; const server: Server = await GatewayServer({ port: 8443, // Enable HTTPS with Development CA ssl: { ca: './ssl/gateway-ca.crt', // CA cert for client verification rejectUnauthorized: false, // allow anonymous if no client cert requestCert: true, // request client certificates for mutual TLS }, auth: { type: 'oauth2', // or 'basic' or 'x509' oauth2: { jwt: { issuerUri: 'https://auth.example.com', audience: 'https://api.example.com', principalClaimName: 'sub', // claim to use as principal } }, x509: { principalAltName: 'email', // extract principal from certificate email SAN (default: uses subject) key: './ssl/gateway-ca.key' // CA key for generating certs }, basic: { realm: 'My Gateway' }, }, app: async ({handle}) => { handle( { request: {method: 'GET', path: '/api/metrics'}, options: {cors: true, authorize: {access: 'authenticated'}}, handler: async ({response}) => { response.setStatusCode({value: 200}); // OK await response.end(); } }, { request: {method: 'POST', path: '/api/metrics'}, options: {cors: true, authorize: {access: 'authenticated'}}, handler: async ({request, response}) => { response.statusCode({value: 202}); // Accepted await response.end(); try { const update = await request.json(); console.log(`${JSON.stringify(update)}`); } catch (e) { console.error('Error processing metrics:', e); } } }); }, }); await server.close(); ``` ## Management The gateway server supports a management interface for remote administration via named pipes (Windows) or Unix sockets. This allows external tools to send commands like `info` (get server status) or `shutdown` (gracefully stop the server). ### CLI Use the `manage` command to send management commands to a running gateway server: ```shell # Get server info npx @interopio/gateway-server manage --path \\.\pipe\glue42-gateway-xxx info # Shutdown the server npx @interopio/gateway-server manage --path \\.\pipe\glue42-gateway-xxx shutdown ``` ### API ```typescript import { manage } from '@interopio/gateway-server/tools'; // Send a command to a running gateway server const result = await manage.sendCommand( { path: '\\\\.\\pipe\\glue42-gateway-xxx' }, { command: 'info' } ); ``` ### Server Configuration ```typescript function getPath() { const prefix = `glue42-gateway`; const env = process.env['GLUE-ENV'] || 'DEMO'; const region = process.env['GLUE-REGION'] || 'INTEROP.IO'; return process.platform === 'win32' ? `\\\\.\\pipe\\glue42-gateway-${env}-${region}-${process.env.USERNAME}` : `${tmpdir()}/${prefix}-${env}-${region}-${process.env.USERNAME}.sock`; } const server: GatewayServer.Server = await GatewayServerFactory({ management: { server: { path: getPath() }, commands: { shutdown: { enabled: false }, } } }); ``` ## `@glue42/gateway-ent` Compatibility This package aims to provide compatibility with `@glue42/gateway-ent` proprietary package used in Glue42 Desktop (now io.Connect Desktop) ```javascript import * as gw from '@interopio/gateway-server/gateway-ent'; ``` ## Changelog See [changelog](./changelog.md) ## License [Interop.io Developer License Agreement](license.md)