fix: allow more granular trust proxy settings

Allow setting all variant of supported trust proxy settings from
https://fastify.dev/docs/latest/Reference/Server/#trustproxy, except for
a custom function. Allows for specifying >=1 IP or CIDR ranges, n-th hop
trust.
This commit is contained in:
Christian Benincasa
2026-03-23 12:31:19 -04:00
parent c98fc89233
commit 3f5e536d71
2 changed files with 28 additions and 4 deletions

View File

@@ -34,6 +34,7 @@ import fs from 'node:fs/promises';
import { networkInterfaces } from 'node:os';
import path, { dirname } from 'node:path';
import 'reflect-metadata';
import { match, P } from 'ts-pattern';
import { z } from 'zod/v4';
import { HdhrApiRouter } from './api/hdhrApi.js';
import { apiRouter } from './api/index.js';
@@ -61,6 +62,19 @@ export class Server {
) {}
async configureServer() {
if (this.serverOptions.trustProxy) {
const trustProxyString = match(this.serverOptions.trustProxy)
.with(P.string, (str) => str.replaceAll(',', ', '))
.with(P.number, (n) => `${n}th hop`)
.with(P.array(P.string), (arr) => arr.join(', '))
.with(true, () => `all proxies`)
.exhaustive();
this.logger.info(
'Initializing Tunarr with trust proxy setting: %s',
trustProxyString,
);
}
this.app = fastify({
logger: false,
bodyLimit: 50 * 1024 * 1024,

View File

@@ -2,6 +2,7 @@ import { container } from '@/container.js';
import assert from 'node:assert';
import { type MarkOptional } from 'ts-essentials';
import type { ArgumentsCamelCase, CommandModule } from 'yargs';
import z from 'zod';
import { App } from '../App.ts';
import { DBAccess } from '../db/DBAccess.ts';
import { type ISettingsDB } from '../db/interfaces/ISettingsDB.ts';
@@ -9,16 +10,19 @@ import { setServerOptions } from '../globals.ts';
import { KEYS } from '../types/inject.ts';
import {
getBooleanEnvVar,
getEnvVar,
getNumericEnvVar,
TUNARR_ENV_VARS,
} from '../util/env.ts';
import { LoggerFactory } from '../util/logging/LoggerFactory.ts';
import type { GlobalArgsType } from './types.ts';
const trustProxySchema = z.stringbool().or(z.coerce.number()).or(z.string());
export type ServerArgsType = GlobalArgsType & {
port: number;
printRoutes: boolean;
trustProxy: boolean;
trustProxy?: string | string[] | number | boolean;
searchPort?: number;
};
@@ -33,16 +37,22 @@ export const RunServerCommand: CommandModule<GlobalArgsType, ServerArgsType> = {
},
printRoutes: {
type: 'boolean',
description: 'Whether to print all available routes at startup.',
default: () =>
getBooleanEnvVar(TUNARR_ENV_VARS.PRINT_ROUTES_ENV_VAR, false),
},
trustProxy: {
type: 'boolean',
default: () =>
getBooleanEnvVar(TUNARR_ENV_VARS.TRUST_PROXY_ENV_VAR, false),
type: 'string',
description:
'Trust proxy passed directly to Fastify. See valid options here: https://fastify.dev/docs/latest/Reference/Server/#trustproxy',
default: () => getEnvVar(TUNARR_ENV_VARS.TRUST_PROXY_ENV_VAR),
coerce: (arg) => {
return trustProxySchema.parse(arg);
},
},
searchPort: {
type: 'number',
description: 'Statically define the port to start the search server on.',
},
},
handler: async (