feat(server): add IP trust list for reverse proxy (#11286)

* feat(server): add IP trust list for reverse proxy

Signed-off-by: hitech95 <nicveronese@gmail.com>

* feat(docs): add documentation of `IMMICH_TRUSTED_PROXIES` env

Signed-off-by: hitech95 <nicveronese@gmail.com>

---------

Signed-off-by: hitech95 <nicveronese@gmail.com>
This commit is contained in:
Nicolò 2024-07-26 16:23:58 +02:00 committed by GitHub
parent ea5d6780f2
commit a3799b3053
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 2 deletions

View File

@ -50,6 +50,7 @@ Regardless of filesystem, it is not recommended to use a network share for your
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
| `IMMICH_TRUSTED_PROXIES` | List of comma separated IPs set as trusted proxies | | server | api |
\*1: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
It only need to be set if the Immich deployment method is changing.

View File

@ -4,7 +4,7 @@ import { CronExpression } from '@nestjs/schedule';
import { QueueOptions } from 'bullmq';
import { Request, Response } from 'express';
import { RedisOptions } from 'ioredis';
import Joi from 'joi';
import Joi, { Root } from 'joi';
import { CLS_ID, ClsModuleOptions } from 'nestjs-cls';
import { ImmichHeader } from 'src/dtos/auth.dto';
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
@ -388,6 +388,20 @@ export const immichAppConfig: ConfigModuleOptions = {
IMMICH_API_METRICS_PORT: Joi.number().optional(),
IMMICH_MICROSERVICES_METRICS_PORT: Joi.number().optional(),
IMMICH_TRUSTED_PROXIES: Joi.extend((joi: Root) => ({
type: 'stringArray',
base: joi.array(),
coerce: (value) => (value.split ? value.split(',') : value),
}))
.stringArray()
.single()
.items(
Joi.string().ip({
version: ['ipv4', 'ipv6'],
cidr: 'optional',
}),
),
IMMICH_METRICS: Joi.boolean().optional().default(false),
IMMICH_HOST_METRICS: Joi.boolean().optional().default(false),
IMMICH_API_METRICS: Joi.boolean().optional().default(false),

View File

@ -14,9 +14,18 @@ import { useSwagger } from 'src/utils/misc';
const host = process.env.HOST;
function parseTrustedProxy(input?: string) {
if (!input) {
return [];
}
// Split on ',' char to allow multiple IPs
return input.split(',');
}
async function bootstrap() {
process.title = 'immich-api';
const otelPort = Number.parseInt(process.env.IMMICH_API_METRICS_PORT ?? '8081');
const trustedProxies = parseTrustedProxy(process.env.IMMICH_TRUSTED_PROXIES ?? '');
otelStart(otelPort);
@ -27,7 +36,7 @@ async function bootstrap() {
logger.setAppName('Api');
logger.setContext('Bootstrap');
app.useLogger(logger);
app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal', ...trustedProxies]);
app.set('etag', 'strong');
app.use(cookieParser());
app.use(json({ limit: '10mb' }));