531 lines
21 KiB
Nginx Configuration File
Executable File
531 lines
21 KiB
Nginx Configuration File
Executable File
# Main process configuration
|
||
worker_processes 1;
|
||
|
||
events {
|
||
worker_connections 1024;
|
||
}
|
||
|
||
http {
|
||
# ## Track by client IP; 20MB ≈ ~1200 active IPs
|
||
# limit_req_zone $binary_remote_addr zone=rl_zone:20m rate=50r/s;
|
||
# # Return 429 instead of 503 when throttled
|
||
# limit_req_status 429; # 429 is the HTTP status code for Too Many Requests
|
||
# # Log 429s at warn (not error)
|
||
# limit_req_log_level warn;
|
||
|
||
geo $frontend_whitelist {
|
||
default 1;
|
||
127.0.0.1 1;
|
||
172.20.0.0/16 1; # Frontend Docker subnet
|
||
5.75.153.161 1; # Grafana or monitoring
|
||
167.235.254.4 1; # Ansible server IP
|
||
}
|
||
|
||
geo $backend_whitelist {
|
||
default 1;
|
||
127.0.0.1 1;
|
||
172.19.0.0/16 1; # Backend Docker subnet
|
||
5.75.153.161 1; # Grafana or monitoring
|
||
167.235.254.4 1; # Ansible server IP
|
||
}
|
||
|
||
# These settings ensure that $remote_addr reflects the real client IP forwarded by https-portal, which is needed for your allow rules to work correctly
|
||
# Recommended for resolving client IP behind proxy
|
||
# Docker networks where both frontend and backend containers communicate through NGINX.
|
||
# To avoid potential misclassification of real client IPs from backend routes.
|
||
# The set_real_ip_from directive doesn’t allow access — it just instructs NGINX to trust the X-Forwarded-For header from those IPs.
|
||
set_real_ip_from 172.20.0.0/16; # Replace with your Docker network subnet (matches your `frontend` network)
|
||
set_real_ip_from 172.19.0.0/16; # Replace with your Docker network subnet (matches your `backend` network)
|
||
real_ip_header X-Forwarded-For;
|
||
real_ip_recursive on;
|
||
|
||
# DNS resolver configuration for better reliability
|
||
resolver 127.0.0.11 valid=60s ipv6=off;
|
||
resolver_timeout 60s;
|
||
|
||
upstream phoenix_system_cluster {
|
||
zone phoenix_system_cluster 64k;
|
||
least_conn;
|
||
server phoenix-system:3000 resolve fail_timeout=60s max_fails=10;
|
||
server 127.0.0.1:81 backup; # Backup server for unavailable service
|
||
# ADD_SYSTEM_SERVERS_HERE
|
||
}
|
||
|
||
upstream phoenix_worker_cluster {
|
||
zone phoenix_worker_cluster 64k;
|
||
least_conn;
|
||
server phoenix-worker:3001 resolve fail_timeout=60s max_fails=10;
|
||
server 127.0.0.1:81 backup; # Backup server for unavailable service
|
||
# ADD_WORKER_SERVERS_HERE
|
||
}
|
||
|
||
upstream pgadmin4-ui {
|
||
zone pgadmin4-ui 64k;
|
||
least_conn;
|
||
server pgadmin4-ui:80 resolve fail_timeout=120s max_fails=20;
|
||
# server 127.0.0.1:81 backup; # Backup server for unavailable service
|
||
# ADD_PGADMIN4_SERVERS_HERE
|
||
}
|
||
|
||
server_tokens off; # Disable NGINX version tokens to avoid leaking NGINX version.
|
||
|
||
# File handling & upload limits
|
||
sendfile on;
|
||
client_max_body_size 64m;
|
||
|
||
# Global proxy timeout settings (can be overridden per location)
|
||
proxy_connect_timeout 30s;
|
||
proxy_send_timeout 30s;
|
||
proxy_read_timeout 30s;
|
||
proxy_next_upstream_timeout 30s;
|
||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
|
||
|
||
# Prevent warning when setting many proxy headers, like we do
|
||
proxy_headers_hash_max_size 1024;
|
||
proxy_headers_hash_bucket_size 128;
|
||
|
||
# Gzip compression (for better bandwidth efficiency)
|
||
gzip on;
|
||
gzip_min_length 1000;
|
||
gzip_proxied expired no-cache no-store private auth;
|
||
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||
|
||
# Trust the protocol from upstream proxy/load balancer
|
||
map $http_x_forwarded_proto $forwarded_proto {
|
||
default $scheme;
|
||
https https;
|
||
http http;
|
||
}
|
||
|
||
# File types and default mime type
|
||
include /etc/nginx/mime.types;
|
||
default_type application/octet-stream;
|
||
|
||
# 🧩 Logs
|
||
map $request_uri $loggable {
|
||
default 1;
|
||
~^/stub_status 0;
|
||
~^/health/system 0;
|
||
~^/health/worker 0;
|
||
}
|
||
|
||
# log_format main_with_realip '$remote_addr - $realip_remote_addr [$time_local] '
|
||
# '"$request" $status $body_bytes_sent '
|
||
# '"$http_referer" "$http_user_agent"';
|
||
|
||
log_format json_compatible escape=json '{'
|
||
'"time":"$time_iso8601",'
|
||
'"remote_addr":"$remote_addr",'
|
||
'"proxy_addr":"$proxy_protocol_addr",'
|
||
'"x_forwarded_for":"$http_x_forwarded_for",'
|
||
'"request_method":"$request_method",'
|
||
'"request_uri":"$request_uri",'
|
||
'"status":$status,'
|
||
'"body_bytes_sent":$body_bytes_sent,'
|
||
'"request_time":$request_time,'
|
||
'"upstream_response_time":"$upstream_response_time",'
|
||
'"http_referer":"$http_referer",'
|
||
'"http_user_agent":"$http_user_agent",'
|
||
'"host":"$host",'
|
||
'"realip":"$realip_remote_addr"'
|
||
'}';
|
||
|
||
access_log /var/log/nginx/access_json.log json_compatible if=$loggable; # JSON format for Loki/Grafana/Prometheus/Fail2ban
|
||
# access_log /var/log/nginx/access.log main_with_realip if=$loggable;
|
||
# End of logs
|
||
|
||
##################################################################
|
||
# 🔧 Backup Server for Unavailable Services
|
||
##################################################################
|
||
server {
|
||
listen 127.0.0.1:81;
|
||
server_name _;
|
||
|
||
# limit_req zone=rl_zone burst=30 nodelay;
|
||
|
||
# Return service unavailable for health checks
|
||
location /health {
|
||
add_header Content-Type application/json always;
|
||
return 503 '{"status":"unavailable","message":"Service is currently down"}';
|
||
}
|
||
|
||
# Return service unavailable for all other requests
|
||
location / {
|
||
add_header Content-Type text/html always;
|
||
return 503 '<html><body><h1>Service Temporarily Unavailable</h1><p>The requested service is currently down for maintenance.</p></body></html>';
|
||
}
|
||
|
||
# Return service unavailable for pgAdmin4
|
||
location /pgadmin4 {
|
||
add_header Content-Type text/html always;
|
||
return 503 '<html><body><h1>Service Temporarily Unavailable</h1><p>The requested service is currently down for maintenance.</p></body></html>';
|
||
}
|
||
|
||
location /health/worker {
|
||
add_header Content-Type application/json always;
|
||
return 503 '{"status":"unavailable","message":"Service is currently down"}';
|
||
}
|
||
}
|
||
|
||
##################################################################
|
||
# 🧩 HTTP Server Block
|
||
##################################################################
|
||
server {
|
||
listen 80;
|
||
server_name _;
|
||
|
||
# limit_req zone=rl_zone burst=30 nodelay;
|
||
|
||
# Security headers
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
add_header X-XSS-Protection "1; mode=block" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||
|
||
root /usr/share/nginx/html;
|
||
index index.html index.htm;
|
||
|
||
# --- Return real 404 for common bot targets (must be before SPA fallback) ---
|
||
# Block direct file probes like .php, .env, .git, backups, etc.
|
||
location ~* \.(php|env|git|sql|bak|ini|config|swp|old|backup)$ {
|
||
return 404;
|
||
}
|
||
|
||
# Block well-known bad paths used by scanners
|
||
location ~* ^/(wp-admin|wp-login\.php|xmlrpc\.php|phpinfo\.php|vendor/phpunit|setup\.php|manager/html|id\.php|shell\.php|\.DS_Store) {
|
||
return 404;
|
||
}
|
||
|
||
# Test for Fail2ban
|
||
# location = /__f2b_test_404__ { return 404; }
|
||
|
||
# Frontend SPA fallback (keep this AFTER the blocks above)
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
# Backend API routes
|
||
location /backend-api/ {
|
||
proxy_pass http://phoenix_system_cluster/;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
client_max_body_size 500m; # Increase the body size limit for admin API requests to 500mb
|
||
# Increase timeout settings for file uploads
|
||
proxy_connect_timeout 1800; # 30 minutes for regular API with file uploads
|
||
proxy_send_timeout 1800; # 30 minutes to send request to backend
|
||
proxy_read_timeout 1800; # 30 minutes to read response from backend
|
||
send_timeout 1800; # 30 minutes to send response to client
|
||
}
|
||
|
||
location /admin-api {
|
||
client_max_body_size 1024m; # Increase the body size limit for admin API requests to 1gb
|
||
proxy_connect_timeout 3600; # 60 minutes to establish connection to backend
|
||
proxy_send_timeout 3600; # 60 minutes to send request to backend
|
||
proxy_read_timeout 3600; # 60 minutes to read response from backend
|
||
send_timeout 3600; # 60 minutes to send response to client
|
||
proxy_pass http://phoenix_system_cluster/admin-api;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
location /remote-assets {
|
||
proxy_pass http://phoenix_system_cluster/remote-assets;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
location /sti {
|
||
proxy_pass http://phoenix_system_cluster/sti;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
# WebSocket support
|
||
location /ws {
|
||
proxy_pass http://phoenix_system_cluster/graphql;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
# Reverse proxy for pgAdmin (subpath support)
|
||
# include /etc/nginx/includes/*.conf;
|
||
location /pgadmin4 {
|
||
error_log /var/log/nginx/pgadmin4_error.log notice;
|
||
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Connection "";
|
||
|
||
proxy_pass http://pgadmin4-ui/;
|
||
proxy_set_header X-Script-Name /pgadmin4;
|
||
proxy_set_header X-Scheme $scheme;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
|
||
# Improved timeout settings
|
||
proxy_connect_timeout 120s;
|
||
proxy_send_timeout 120s;
|
||
proxy_read_timeout 120s;
|
||
|
||
# Retry on errors
|
||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
|
||
proxy_next_upstream_tries 5;
|
||
proxy_next_upstream_timeout 120s;
|
||
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
# End of headers
|
||
proxy_redirect off;
|
||
|
||
# ⚠️ Rewrite required to remove /pgadmin4 from the path
|
||
rewrite ^/pgadmin4(/.*)$ $1 break;
|
||
}
|
||
|
||
# Health check endpoints -> used by the health check exporter
|
||
location /health/system {
|
||
proxy_pass http://phoenix_system_cluster/health;
|
||
# Secure the health check endpoint
|
||
if ($backend_whitelist = 0) {
|
||
return 403;
|
||
}
|
||
# End of security
|
||
# Include headers for proxying
|
||
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 $forwarded_proto;
|
||
# End of headers
|
||
# Timeout settings
|
||
proxy_connect_timeout 30s;
|
||
proxy_send_timeout 30s;
|
||
proxy_read_timeout 30s;
|
||
}
|
||
|
||
# location /health/system/metrics {
|
||
# proxy_pass http://phoenix_system_cluster/health/metrics;
|
||
# # Secure the health check endpoint
|
||
# # if ($backend_whitelist = 0) {
|
||
# # return 403;
|
||
# # }
|
||
# # End of security
|
||
# # Include headers for proxying
|
||
# 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 $forwarded_proto;
|
||
# # End of headers
|
||
# }
|
||
|
||
location /health/worker {
|
||
proxy_pass http://phoenix_worker_cluster/health;
|
||
# Secure the health check endpoint
|
||
if ($backend_whitelist = 0) {
|
||
return 403;
|
||
}
|
||
# End of security
|
||
# Include headers for proxying
|
||
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 $forwarded_proto;
|
||
# End of headers
|
||
# Timeout settings
|
||
proxy_connect_timeout 30s;
|
||
proxy_send_timeout 30s;
|
||
proxy_read_timeout 30s;
|
||
}
|
||
|
||
# location /health/worker/metrics {
|
||
# proxy_pass http://phoenix_worker_cluster/health/metrics;
|
||
# # Secure the health check endpoint
|
||
# # if ($backend_whitelist = 0) {
|
||
# # return 403;
|
||
# # }
|
||
# # End of security
|
||
# # Include headers for proxying
|
||
# 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 $forwarded_proto;
|
||
# # End of headers
|
||
# }
|
||
|
||
location /stub_status {
|
||
stub_status;
|
||
# Secure the stub status endpoint
|
||
if ($frontend_whitelist = 0) {
|
||
return 403;
|
||
}
|
||
# End of security
|
||
}
|
||
}
|
||
|
||
##################################################################
|
||
# 🔐 HTTPS Server Block
|
||
##################################################################
|
||
server {
|
||
listen 443 ssl;
|
||
http2 on;
|
||
server_name _;
|
||
|
||
# Apply globally inside this server
|
||
# limit_req zone=rl_zone burst=30 nodelay;
|
||
|
||
# Security headers
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
add_header X-XSS-Protection "1; mode=block" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||
|
||
ssl_certificate /etc/nginx/external-certificate/certificate.crt;
|
||
ssl_certificate_key /etc/nginx/external-certificate/certificate.key;
|
||
|
||
root /usr/share/nginx/html;
|
||
index index.html index.htm;
|
||
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
# Secure API routes
|
||
location /backend-api/ {
|
||
proxy_pass http://phoenix_system_cluster/;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
client_max_body_size 500m; # Increase the body size limit for admin API requests to 500mb
|
||
# Increase timeout settings for file uploads
|
||
proxy_connect_timeout 1800; # 30 minutes for regular API with file uploads
|
||
proxy_send_timeout 1800; # 30 minutes to send request to backend
|
||
proxy_read_timeout 1800; # 30 minutes to read response from backend
|
||
send_timeout 1800; # 30 minutes to send response to client
|
||
}
|
||
|
||
location /admin-api {
|
||
client_max_body_size 1024m; # Increase the body size limit for admin API requests to 1gb
|
||
proxy_connect_timeout 3600; # 60 minutes to establish connection to backend
|
||
proxy_send_timeout 3600; # 60 minutes to send request to backend
|
||
proxy_read_timeout 3600; # 60 minutes to read response from backend
|
||
send_timeout 3600; # 60 minutes to send response to client
|
||
proxy_pass http://phoenix_system_cluster/admin-api;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
location /remote-assets {
|
||
proxy_pass http://phoenix_system_cluster/remote-assets;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
location /sti {
|
||
proxy_pass http://phoenix_system_cluster/sti;
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
location /ws {
|
||
proxy_pass http://phoenix_system_cluster/graphql;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
# Include headers for proxying
|
||
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-Host $host;
|
||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
# Reverse proxy for pgAdmin (subpath support)
|
||
include /etc/nginx/includes/*.conf;
|
||
|
||
location /health/system {
|
||
proxy_pass http://phoenix_system_cluster/health;
|
||
# Secure the health check endpoint
|
||
if ($backend_whitelist = 0) {
|
||
return 403;
|
||
}
|
||
# End of security
|
||
# Include headers for proxying
|
||
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 $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
location /health/worker {
|
||
proxy_pass http://phoenix_worker_cluster/health;
|
||
# Secure the health check endpoint
|
||
if ($backend_whitelist = 0) {
|
||
return 403;
|
||
}
|
||
# End of security
|
||
# Include headers for proxying
|
||
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 $forwarded_proto;
|
||
# End of headers
|
||
}
|
||
|
||
location /stub_status {
|
||
stub_status;
|
||
# Secure the stub status endpoint
|
||
if ($frontend_whitelist = 0) {
|
||
return 403;
|
||
}
|
||
# End of security
|
||
}
|
||
}
|
||
} |