feat: add nginx reverse proxy configuration (#8516) (#10780)

Co-authored-by: Boon <boon@security8.work>
This commit is contained in:
Boon
2026-04-20 23:30:21 +08:00
committed by GitHub
parent 3406c5ec64
commit 98b9449e14
3 changed files with 176 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
# Prowler Reverse Proxy Configuration
Ready-to-use nginx configuration for running Prowler behind a reverse proxy.
## Problem
Prowler's default Docker setup exposes two separate services:
- **UI** on port 3000
- **API** on port 8080
This causes CORS issues and authentication failures (especially SAML SSO) when accessed through an external reverse proxy, since the proxy typically exposes a single domain.
## Solution
This adds an nginx container that unifies both services behind a single port, correctly forwarding headers so that Django generates proper URLs for SAML ACS callbacks and API responses.
## Quick Start
From the prowler root directory:
docker compose -f docker-compose.yml \
-f contrib/reverse-proxy/docker-compose.reverse-proxy.yml \
up -d
Access Prowler at http://localhost (port 80).
## With an External Reverse Proxy
Point your external reverse proxy to the prowler-nginx container on port 80.
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| PROWLER_PROXY_PORT | 80 | Port exposed by the nginx proxy |
### Example: Traefik
services:
nginx:
labels:
- "traefik.enable=true"
- "traefik.http.routers.prowler.rule=Host(`prowler.example.com`)"
- "traefik.http.routers.prowler.tls.certresolver=letsencrypt"
- "traefik.http.services.prowler.loadbalancer.server.port=80"
### Example: Caddy
prowler.example.com {
reverse_proxy prowler-nginx:80
}
## SAML SSO
If using SAML SSO behind a reverse proxy, also set the SAML_ACS_BASE_URL environment variable:
SAML_ACS_BASE_URL=https://prowler.example.com
## Architecture
Internet -> External Reverse Proxy -> prowler-nginx:80
|-- /api/* -> prowler-api:8080
|-- /accounts/saml/ -> prowler-api:8080
+-- /* -> prowler-ui:3000
@@ -0,0 +1,42 @@
# Prowler Reverse Proxy - Docker Compose Override
#
# Use this alongside the main docker-compose.yml to add an nginx
# reverse proxy that unifies UI and API behind a single port.
#
# Usage:
# docker compose -f docker-compose.yml -f contrib/reverse-proxy/docker-compose.reverse-proxy.yml up -d
#
# Then access Prowler at http://localhost (port 80) or configure
# your external reverse proxy (Traefik, Caddy, Cloudflare Tunnel,
# Pangolin, etc.) to point to this container on port 80.
#
# For HTTPS with your own certs, see the README in this directory.
#
# Fixes: https://github.com/prowler-cloud/prowler/issues/8516
services:
nginx:
image: nginx:alpine
container_name: prowler-nginx
restart: unless-stopped
ports:
- "${PROWLER_PROXY_PORT:-80}:80"
volumes:
- ./contrib/reverse-proxy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- prowler-ui
- prowler-api
networks:
- prowler-network
# Override UI to not expose port externally (nginx handles it)
prowler-ui:
ports: !reset []
# Override API to not expose port externally (nginx handles it)
prowler-api:
ports: !reset []
networks:
prowler-network:
driver: bridge
+70
View File
@@ -0,0 +1,70 @@
# Prowler Reverse Proxy Configuration
# Routes both UI and API through a single endpoint
#
# Usage: See docker-compose.reverse-proxy.yml
# Fixes: https://github.com/prowler-cloud/prowler/issues/8516
upstream prowler-ui {
server prowler-ui:3000;
}
upstream prowler-api {
server prowler-api:8080;
}
server {
listen 80;
server_name _;
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# API requests — proxy to prowler-api
location /api/ {
proxy_pass http://prowler-api/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 300s;
proxy_connect_timeout 10s;
# Handle large scan payloads
client_max_body_size 50m;
}
# SAML endpoints — proxy to prowler-api
location /accounts/saml/ {
proxy_pass http://prowler-api/accounts/saml/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
# Everything else — proxy to prowler-ui
location / {
proxy_pass http://prowler-ui/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
# WebSocket support for Next.js HMR (dev) and live updates
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Health check endpoint
location /health {
access_log off;
return 200 "ok\n";
add_header Content-Type text/plain;
}
}