From 70daca868d809064de137c81abda21d07f88677f Mon Sep 17 00:00:00 2001 From: Jim Groffen Date: Mon, 28 Dec 2020 00:48:38 +1030 Subject: [PATCH] Make ADDRESS option optional, determined automatically if not provided. The ADDRESS env variable is used for advertising the address of the service only. If it is not provided, it can be determined automatically using the HTTP Request headers 'Host' and 'X-Forwarded-*'. This approach uses ExpressJS provided functionality but requires 'trusting' headers forwarded from proxies. Deployers can control the trusted proxies using the TRUST_PROXY option, documented in the code only as it's very edge-case. This approach supports the best of both worlds; ADDRESS will use a logical (probably accurate) value by default, but can be overridden in the case of unusual proxy scenario or a canonical URL is preferred. --- README.md | 4 ++-- src/index.ts | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1927748..3be8cd4 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ This is the relay server for CrewLink, an Among Us proximity voice chat program. Optional environment variables: - `PORT`: Specifies the port that the server runs on. Defaults to `443` if `HTTPS` is enabled, and `9736` if not. - - `ADDRESS` **(REQUIRED)**: Specifies the server domain - - `NAME`: Specifies the server name + - `ADDRESS` : Specifies the server domain. If not provided, `ADDRESS` is determined automatically (and possibly incorrectly) from HTTP Request headers. + - `NAME`: Specifies the server name. - `HTTPS`: Enables https. You must place `privkey.pem` and `fullchain.pem` in your CWD. - `SSLPATH`: Specifies an alternate path to SSL certificates. diff --git a/src/index.ts b/src/index.ts index 090103a..c33dbd0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,25 +42,46 @@ interface Signal { to: string; } -app.set('view engine', 'pug') -app.use(morgan('combined')) +app.set('view engine', 'pug'); +app.use(morgan('combined')); + +// TRUST_PROXY can be used to set which proxies can be trusted based on IP +// address. This is an advanced, undocumented environment variable. +// For more details see: https://expressjs.com/en/guide/behind-proxies.html +app.set('trust proxy', process.env.TRUST_PROXY ?? true ); let connectionCount = 0; let address = process.env.ADDRESS; + +/** + * Derives the address used from ExpressJS Request properties. + * The ExpressJS Request properties for hostname and protocol take into account + * the x-forwarded- parameters, so the resulting address is proxy friendly. + * @param req An HTTPRequest object. + */ +function addressFromRequest(req: express.Request) { + let p = ( 'x-forwarded-port' in req.headers ? req.headers['x-forwarded-port'] : port); + if ( p == '80' || p == '443' ) { + return `${req.protocol}://${req.hostname}`; + } else { + return `${req.protocol}://${req.hostname}:${p}` + } +} + if (!address) { - logger.error('You must set the ADDRESS environment variable.'); - process.exit(1); + logger.info('ADDRESS environment variable not set.'); + logger.info('Advertised server address will be derived from HTTP request headers.'); } -app.get('/', (_, res) => { - res.render('index', { connectionCount, address }); +app.get('/', (req, res) => { + res.render('index', { connectionCount, address: (address || addressFromRequest(req)), headers: req.headers }); }); app.get('/health', (req, res) => { res.json({ uptime: process.uptime(), connectionCount, - address, + address: (address || addressFromRequest(req)), name: process.env.NAME }); }) @@ -168,5 +189,5 @@ io.on('connection', (socket: socketIO.Socket) => { server.listen(port); (async () => { - logger.info('CrewLink Server started: %s', address); -})(); + logger.info('CrewLink Server started: %s', address || '[ADDRESS not set]'); +})(); \ No newline at end of file