@interopio/gateway-server
Version:
[](https://www.npmjs.com/package/@interopio/gateway-server)
351 lines (280 loc) • 12.9 kB
Markdown
# io.Gateway Server
[](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)