This is a simple app gateway for development purposes. The scenario which it is designed for is to run on Windows and proxy services on WSL/docker (a standalone Windows binary is provided so that it is possible to run in this scenario without installing NodeJS on the host machine).
It can do:
- Multiplexing HTTP requests to various backend based on host or path
- SSL termination (i.e. proxying an HTTP only backend as HTTPS)
- HTTP2
- Basic authentication with usernames and hashed passwords
- Route level authorization
Provide a configuration file in the working directory where the application is launched. The file must be named router.conf.js
or router.conf.json
according to its format.
The file can either be a JSON object or a ESM .js file with default export (yaml
format planned but not yet supported).
If you use ESM keep in mind nodejs restrictions about import
expressions.
The configuration is loaded through a data URL to allow it to work in Node SEA so things such as import.meta.url
will likely not yield the expected result.
See configuration or the conf.d.ts
file in the build output for the schema.
A VHost is a a pair of listener/target that associates a classifier of incoming requests (listener) with a backend that handles them (target). A listener will classify requests by hostname and the start of the pathname. These must be unique for each VHost or you will get an error at initialization.
The application will listen on a single TCP port so it won't classify incoming requests based on that. If you need multiple ports, run multiple instances.
VHost Targets are a specification of how the http request must be resolved. There are 3 types:
file
: serves statically a directory content, specified with thebase
configuration. Will renderindex.html
in directory paths where it is present and provide a rough file listing in directories where it is not (see (template)[packages/router/src/template.ts])http
|https
|http2
|https2
: will proxy requests appending them to the providedbase
HTTP(S) URL. If2
is present in the type name it will resolve the backend using HTTP/2, otherwise it will use HTTP/1.http
andhttps
are treated the same (whether to use SSL/TLS on the backend request depends only on the base url specification) IfstripPrefix
property is present in the listener, the prefix specified in the listener will be removed from the URL otherwise it will be kept in the proxy resolution. For example, a request to/api/myendpoint
:- with VHost configuration of
{ listener: { prefix: '/api', stripPrefix: true }, target: { type: 'http', base: 'https://myhost/apibase/'} }
it will be proxied tohttps://myhost/apibase/myendpoint
- with VHost configuration of
{ listener: { prefix: '/api', stripPrefix: false }, target: { type: 'http', base: 'https://myhost/apibase/'} }
it will be proxied tohttps://myhost/apibase/api/myendpoint
- with VHost configuration of
process
: will spawn a process from the directory specified in thecwd
parameter using the command specified in thecmd
parameter, and proxy requests to that service using http. A${PORT}
string on the command line (e.g.{ cmd: 'node myapi.js -- --listen=http://localhost:${PORT}' }
will be replaced with a randomly generated port number and the spawned process should listen on localhost HTTP on that port.
In the current version SSL/TLS is mandatory. A non-empty list of certificates must be provided for the router to use.
The certificate is chosen among the list of candidates using SNI. When the SNI header is not present or none of the certificate match, the first certificate in the list will be used.
The server will automatically choose between HTTP/1.1 and HTTP/2 using ALPN.
Keys and certificates configuration can either be a literal certificate/key (if the string starts with -----BEGIN CERTIFICATE-----
or -----BEGIN PRIVATE KEY-----
) or a filename where the certificate should be loaded from.
The certificates/keys should be in PEM form.
The chain
parameter should be a list of additional certificates that will be sent to the client to help them verify the certification chain up to a known authority.
It can be empty, for example for self signed certificates.
To support authentication you should provide a list of authentication providers globally and then add an authentication
parameter to the vhost that you want to protect, with an optional list of authorized users (if the list is not present all users recognised by the provider will be authorized)
Only basic auntentication (username/password) is supported at the moment.
The users
parameter of the authentication provider is either an array of { username, pwhash }
objects directly specified in the configuration file or a reference to a credential file.
The credential file format is one username/password hash pair per line, separated by space, e.g.:
alice $pbkdf2-sha512$i=1000$abcde$1234567890abcdef1234567890abcdef
bob $pbkdf2-sha512$i=1000$abcde$1234567890abcdef1234567890abcdef
Password hash must be in the PHC format.
The only supported algorithm is pbkdf2-sha512
at the moment.
It is possible to generate a compatible hash by running the createHash
function in the password.ts file, for example:
$ node
> const { createHash } = await import('./dist/password.js')
undefined
> await createHash('bob password')
'$pbkdf2-sha512$i=200000$69NPrvbFODAHy9gpZe833u2akwO2ji4tBuPq5oJ+oDklsKns/UXrqCKMKpUdZQDexoyQB9pNSeagp2EaF+3hdQ$md1roWZARTcxX0+4JAqkeUZcaPgozh+oiny+OxUYk+/xZcc2heM1zbn/Yg7ufL90w+dGQbi8iAKJVt5HiLiwQQ'
If users are specified with a file, hashes are updated automatically when the user logs in if the hash iteration of the hash in the file is less than the default hash iteration number (see password.ts:17)