From b5da234ce72696239cfe843cb5047845cfbf44fc Mon Sep 17 00:00:00 2001 From: Yau <33013947+chungyau97@users.noreply.github.com> Date: Sat, 27 Sep 2025 21:08:55 +0800 Subject: [PATCH] Add environment variable control for trust proxy setting (#5226) * feat: allow trust proxy setting to be configured via environment variable * fix: restore HTTP_DENY_LIST in .env.example after merge conflict * feat: add conditional handling for trust proxy * feat: add trust proxy environment variable documentation * feat: add trust proxy environment variable sample value * fix: handle empty trust proxy string in docker environment --------- Co-authored-by: Henry Heng --- CONTRIBUTING.md | 79 ++++++++++++------------ docker/.env.example | 1 + docker/docker-compose-queue-prebuilt.yml | 6 +- docker/docker-compose.yml | 3 +- docker/worker/.env.example | 1 + docker/worker/docker-compose.yml | 2 +- packages/server/.env.example | 1 + packages/server/src/commands/base.ts | 4 +- packages/server/src/index.ts | 14 ++++- 9 files changed, 66 insertions(+), 45 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 90a7acafb..194bf6623 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -120,45 +120,46 @@ Flowise has 3 different modules in a single mono repository. Flowise support different environment variables to configure your instance. You can specify the following variables in the `.env` file inside `packages/server` folder. Read [more](https://docs.flowiseai.com/environment-variables) -| Variable | Description | Type | Default | -| ---------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | -| PORT | The HTTP port Flowise runs on | Number | 3000 | -| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | | -| IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | | -| FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb | -| DEBUG | Print logs from components | Boolean | | -| LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` | -| LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` | -| LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 | -| TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Custom Tool or Function | String | | -| TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Custom Tool or Function | String | | -| ALLOW_BUILTIN_DEP | Allow project dependencies to be used for Custom Tool or Function | Boolean | false | -| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` | -| DATABASE_PATH | Location where database is saved (When DATABASE_TYPE is sqlite) | String | `your-home-dir/.flowise` | -| DATABASE_HOST | Host URL or IP address (When DATABASE_TYPE is not sqlite) | String | | -| DATABASE_PORT | Database port (When DATABASE_TYPE is not sqlite) | String | | -| DATABASE_USER | Database username (When DATABASE_TYPE is not sqlite) | String | | -| DATABASE_PASSWORD | Database password (When DATABASE_TYPE is not sqlite) | String | | -| DATABASE_NAME | Database name (When DATABASE_TYPE is not sqlite) | String | | -| DATABASE_SSL_KEY_BASE64 | Database SSL client cert in base64 (takes priority over DATABASE_SSL) | Boolean | false | -| DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false | -| SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` | -| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String | | -| MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` | -| STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local`, `gcs` | `local` | -| BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` | -| S3_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `s3` | String | | -| S3_STORAGE_ACCESS_KEY_ID | AWS Access Key | String | | -| S3_STORAGE_SECRET_ACCESS_KEY | AWS Secret Key | String | | -| S3_STORAGE_REGION | Region for S3 bucket | String | | -| S3_ENDPOINT_URL | Custom Endpoint for S3 | String | | -| S3_FORCE_PATH_STYLE | Set this to true to force the request to use path-style addressing | Boolean | false | -| GOOGLE_CLOUD_STORAGE_PROJ_ID | The GCP project id for cloud storage & logging when `STORAGE_TYPE` is `gcs` | String | | -| GOOGLE_CLOUD_STORAGE_CREDENTIAL | The credential key file path when `STORAGE_TYPE` is `gcs` | String | | -| GOOGLE_CLOUD_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `gcs` | String | | -| GOOGLE_CLOUD_UNIFORM_BUCKET_ACCESS | Enable uniform bucket level access when `STORAGE_TYPE` is `gcs` | Boolean | true | -| SHOW_COMMUNITY_NODES | Show nodes created by community | Boolean | | -| DISABLED_NODES | Hide nodes from UI (comma separated list of node names) | String | | +| Variable | Description | Type | Default | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | +| PORT | The HTTP port Flowise runs on | Number | 3000 | +| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | | +| IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | | +| FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb | +| DEBUG | Print logs from components | Boolean | | +| LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` | +| LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` | +| LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 | +| TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Custom Tool or Function | String | | +| TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Custom Tool or Function | String | | +| ALLOW_BUILTIN_DEP | Allow project dependencies to be used for Custom Tool or Function | Boolean | false | +| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` | +| DATABASE_PATH | Location where database is saved (When DATABASE_TYPE is sqlite) | String | `your-home-dir/.flowise` | +| DATABASE_HOST | Host URL or IP address (When DATABASE_TYPE is not sqlite) | String | | +| DATABASE_PORT | Database port (When DATABASE_TYPE is not sqlite) | String | | +| DATABASE_USER | Database username (When DATABASE_TYPE is not sqlite) | String | | +| DATABASE_PASSWORD | Database password (When DATABASE_TYPE is not sqlite) | String | | +| DATABASE_NAME | Database name (When DATABASE_TYPE is not sqlite) | String | | +| DATABASE_SSL_KEY_BASE64 | Database SSL client cert in base64 (takes priority over DATABASE_SSL) | Boolean | false | +| DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false | +| SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` | +| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String | | +| MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` | +| STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local`, `gcs` | `local` | +| BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` | +| S3_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `s3` | String | | +| S3_STORAGE_ACCESS_KEY_ID | AWS Access Key | String | | +| S3_STORAGE_SECRET_ACCESS_KEY | AWS Secret Key | String | | +| S3_STORAGE_REGION | Region for S3 bucket | String | | +| S3_ENDPOINT_URL | Custom Endpoint for S3 | String | | +| S3_FORCE_PATH_STYLE | Set this to true to force the request to use path-style addressing | Boolean | false | +| GOOGLE_CLOUD_STORAGE_PROJ_ID | The GCP project id for cloud storage & logging when `STORAGE_TYPE` is `gcs` | String | | +| GOOGLE_CLOUD_STORAGE_CREDENTIAL | The credential key file path when `STORAGE_TYPE` is `gcs` | String | | +| GOOGLE_CLOUD_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `gcs` | String | | +| GOOGLE_CLOUD_UNIFORM_BUCKET_ACCESS | Enable uniform bucket level access when `STORAGE_TYPE` is `gcs` | Boolean | true | +| SHOW_COMMUNITY_NODES | Show nodes created by community | Boolean | | +| DISABLED_NODES | Hide nodes from UI (comma separated list of node names) | String | | +| TRUST_PROXY | Configure proxy trust settings for proper IP detection. Values: 'true' (trust all), 'false' (disable), number (hop count), or Express proxy values (e.g., 'loopback', 'linklocal', 'uniquelocal', IP addresses). [Learn More](https://expressjs.com/en/guide/behind-proxies.html) | Boolean/String/Number | true | You can also specify the env variables when using `npx`. For example: diff --git a/docker/.env.example b/docker/.env.example index 5459d8142..b6cc050b0 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -174,3 +174,4 @@ JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200 # HTTP_DENY_LIST= # CUSTOM_MCP_SECURITY_CHECK=true # CUSTOM_MCP_PROTOCOL=sse #(stdio | sse) +# TRUST_PROXY=true #(true | false | 1 | loopback| linklocal | uniquelocal | IP addresses | loopback, IP addresses) diff --git a/docker/docker-compose-queue-prebuilt.yml b/docker/docker-compose-queue-prebuilt.yml index af881f0f4..51a18e96d 100644 --- a/docker/docker-compose-queue-prebuilt.yml +++ b/docker/docker-compose-queue-prebuilt.yml @@ -140,10 +140,11 @@ services: - REDIS_KEEP_ALIVE=${REDIS_KEEP_ALIVE} - ENABLE_BULLMQ_DASHBOARD=${ENABLE_BULLMQ_DASHBOARD} - # SECURITY + # SECURITY - CUSTOM_MCP_SECURITY_CHECK=${CUSTOM_MCP_SECURITY_CHECK} - CUSTOM_MCP_PROTOCOL=${CUSTOM_MCP_PROTOCOL} - HTTP_DENY_LIST=${HTTP_DENY_LIST} + - TRUST_PROXY=${TRUST_PROXY} healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:${PORT:-3000}/api/v1/ping'] interval: 10s @@ -282,10 +283,11 @@ services: - REDIS_KEEP_ALIVE=${REDIS_KEEP_ALIVE} - ENABLE_BULLMQ_DASHBOARD=${ENABLE_BULLMQ_DASHBOARD} - # SECURITY + # SECURITY - CUSTOM_MCP_SECURITY_CHECK=${CUSTOM_MCP_SECURITY_CHECK} - CUSTOM_MCP_PROTOCOL=${CUSTOM_MCP_PROTOCOL} - HTTP_DENY_LIST=${HTTP_DENY_LIST} + - TRUST_PROXY=${TRUST_PROXY} healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:${WORKER_PORT:-5566}/healthz'] interval: 10s diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2ffcfb0b9..54bcac359 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -125,10 +125,11 @@ services: - REDIS_KEEP_ALIVE=${REDIS_KEEP_ALIVE} - ENABLE_BULLMQ_DASHBOARD=${ENABLE_BULLMQ_DASHBOARD} - # SECURITY + # SECURITY - CUSTOM_MCP_SECURITY_CHECK=${CUSTOM_MCP_SECURITY_CHECK} - CUSTOM_MCP_PROTOCOL=${CUSTOM_MCP_PROTOCOL} - HTTP_DENY_LIST=${HTTP_DENY_LIST} + - TRUST_PROXY=${TRUST_PROXY} ports: - '${PORT}:${PORT}' healthcheck: diff --git a/docker/worker/.env.example b/docker/worker/.env.example index c2854b677..6c2ce8c52 100644 --- a/docker/worker/.env.example +++ b/docker/worker/.env.example @@ -174,3 +174,4 @@ JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200 # HTTP_DENY_LIST= # CUSTOM_MCP_SECURITY_CHECK=true # CUSTOM_MCP_PROTOCOL=sse #(stdio | sse) +# TRUST_PROXY=true #(true | false | 1 | loopback| linklocal | uniquelocal | IP addresses | loopback, IP addresses) diff --git a/docker/worker/docker-compose.yml b/docker/worker/docker-compose.yml index b49e3dcc6..71de912a7 100644 --- a/docker/worker/docker-compose.yml +++ b/docker/worker/docker-compose.yml @@ -129,7 +129,7 @@ services: - CUSTOM_MCP_SECURITY_CHECK=${CUSTOM_MCP_SECURITY_CHECK} - CUSTOM_MCP_PROTOCOL=${CUSTOM_MCP_PROTOCOL} - HTTP_DENY_LIST=${HTTP_DENY_LIST} - + - TRUST_PROXY=${TRUST_PROXY} ports: - '${WORKER_PORT}:${WORKER_PORT}' healthcheck: diff --git a/packages/server/.env.example b/packages/server/.env.example index 95bff4dfa..f8ba1c485 100644 --- a/packages/server/.env.example +++ b/packages/server/.env.example @@ -174,6 +174,7 @@ JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200 # HTTP_DENY_LIST= # CUSTOM_MCP_SECURITY_CHECK=true # CUSTOM_MCP_PROTOCOL=sse #(stdio | sse) +# TRUST_PROXY=true #(true | false | 1 | loopback| linklocal | uniquelocal | IP addresses | loopback, IP addresses) ############################################################################################################ diff --git a/packages/server/src/commands/base.ts b/packages/server/src/commands/base.ts index d3dfbf430..4795ec4b3 100644 --- a/packages/server/src/commands/base.ts +++ b/packages/server/src/commands/base.ts @@ -77,7 +77,8 @@ export abstract class BaseCommand extends Command { ENABLE_BULLMQ_DASHBOARD: Flags.string(), CUSTOM_MCP_SECURITY_CHECK: Flags.string(), CUSTOM_MCP_PROTOCOL: Flags.string(), - HTTP_DENY_LIST: Flags.string() + HTTP_DENY_LIST: Flags.string(), + TRUST_PROXY: Flags.string() } protected async stopProcess() { @@ -210,5 +211,6 @@ export abstract class BaseCommand extends Command { if (flags.CUSTOM_MCP_SECURITY_CHECK) process.env.CUSTOM_MCP_SECURITY_CHECK = flags.CUSTOM_MCP_SECURITY_CHECK if (flags.CUSTOM_MCP_PROTOCOL) process.env.CUSTOM_MCP_PROTOCOL = flags.CUSTOM_MCP_PROTOCOL if (flags.HTTP_DENY_LIST) process.env.HTTP_DENY_LIST = flags.HTTP_DENY_LIST + if (flags.TRUST_PROXY) process.env.TRUST_PROXY = flags.TRUST_PROXY } } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 40666c5c5..1e1d4a3da 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -163,7 +163,19 @@ export class App { this.app.use(express.urlencoded({ limit: flowise_file_size_limit, extended: true })) // Enhanced trust proxy settings for load balancer - this.app.set('trust proxy', true) // Trust all proxies + let trustProxy: string | boolean | number | undefined = process.env.TRUST_PROXY + if (typeof trustProxy === 'undefined' || trustProxy.trim() === '' || trustProxy === 'true') { + // Default to trust all proxies + trustProxy = true + } else if (trustProxy === 'false') { + // Disable trust proxy + trustProxy = false + } else if (!isNaN(Number(trustProxy))) { + // Number: Trust specific number of proxies + trustProxy = Number(trustProxy) + } + + this.app.set('trust proxy', trustProxy) // Allow access from specified domains this.app.use(cors(getCorsOptions()))