mirror of https://github.com/interlegis/sapl.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
7.1 KiB
172 lines
7.1 KiB
upstream sapl_server {
|
|
server unix:/var/interlegis/sapl/run/gunicorn.sock fail_timeout=0;
|
|
}
|
|
|
|
# Reuse X-Request-ID from ingress if present; otherwise generate one.
|
|
map $http_x_request_id $req_id {
|
|
default $http_x_request_id;
|
|
"" $request_id;
|
|
}
|
|
|
|
|
|
server {
|
|
|
|
listen 80;
|
|
server_name sapl.prod;
|
|
|
|
client_max_body_size 4G;
|
|
|
|
# ----------------------------------------------------------------
|
|
# Block known scraper ASNs (datacenter traffic) — zero Python cost.
|
|
# ----------------------------------------------------------------
|
|
if ($bot_asn = 1) {
|
|
return 429 "Too Many Requests";
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# Block known bots by User-Agent — zero Python cost.
|
|
# ----------------------------------------------------------------
|
|
if ($bot_ua_blocked = 1) {
|
|
return 429 "Too Many Requests";
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# robots.txt served directly by nginx.
|
|
# ----------------------------------------------------------------
|
|
location = /robots.txt {
|
|
alias /var/interlegis/sapl/collected_static/robots.txt;
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# Static files — no rate limiting, no proxy.
|
|
# ----------------------------------------------------------------
|
|
location /static/ {
|
|
alias /var/interlegis/sapl/collected_static/;
|
|
expires 90m;
|
|
add_header Cache-Control "public, max-age=5400";
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# Media files — routed through Django for auth, rate counting,
|
|
# and content-type caching; served from disk via X-Accel-Redirect.
|
|
# ----------------------------------------------------------------
|
|
location /media/ {
|
|
limit_req zone=sapl_general burst=${NGINX_BURST_GENERAL} nodelay;
|
|
limit_req_status 429;
|
|
|
|
proxy_set_header X-Request-ID $req_id;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header Host $http_host;
|
|
proxy_redirect off;
|
|
proxy_pass http://sapl_server;
|
|
}
|
|
|
|
# Internal location used exclusively by X-Accel-Redirect responses
|
|
# from serve_media(). Not reachable by external clients.
|
|
location /internal/media/ {
|
|
internal;
|
|
alias /var/interlegis/sapl/media/;
|
|
sendfile on;
|
|
etag on;
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# /relatorios/ — heaviest endpoint (PDF generation).
|
|
# Tighter rate limit; extended timeout for uncached generation.
|
|
# ----------------------------------------------------------------
|
|
location /relatorios/ {
|
|
limit_req zone=sapl_heavy burst=${NGINX_BURST_HEAVY} nodelay;
|
|
limit_req_status 429;
|
|
|
|
proxy_read_timeout 180s;
|
|
proxy_send_timeout 180s;
|
|
|
|
proxy_set_header X-Request-ID $req_id;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header Host $http_host;
|
|
proxy_redirect off;
|
|
proxy_pass http://sapl_server;
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# /api/ — rate limited, CORS maintained from original config.
|
|
# ----------------------------------------------------------------
|
|
location /api/ {
|
|
limit_req zone=sapl_general burst=${NGINX_BURST_API} nodelay;
|
|
limit_req_status 429;
|
|
|
|
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, HEAD, OPTIONS' always;
|
|
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
|
|
add_header 'Access-Control-Expose-Headers' 'Content-Type,X-RateLimit-Reason,Retry-After,X-Quota-Daily-Remaining,X-Quota-Weekly-Remaining' always;
|
|
|
|
if ($request_method = 'OPTIONS') {
|
|
add_header 'Access-Control-Allow-Origin' '*';
|
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, HEAD, OPTIONS';
|
|
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With';
|
|
add_header 'Access-Control-Max-Age' 1728000;
|
|
add_header 'Content-Type' 'text/plain; charset=utf-8';
|
|
add_header 'Content-Length' 0;
|
|
return 204;
|
|
}
|
|
|
|
proxy_set_header X-Request-ID $req_id;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header Host $http_host;
|
|
proxy_redirect off;
|
|
proxy_pass http://sapl_server;
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# /painel/<pk>/dados — high-frequency polling endpoint (will become
|
|
# WebSocket). No rate limiting at either layer (Django middleware
|
|
# also bypasses via RATE_LIMIT_BYPASS_PATHS).
|
|
# ----------------------------------------------------------------
|
|
location ~ ^/painel/\d+/dados$ {
|
|
proxy_set_header X-Request-ID $req_id;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header Host $http_host;
|
|
proxy_redirect off;
|
|
proxy_pass http://sapl_server;
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# Scanner extension probes (.php, .asp, etc.) — SAPL never serves
|
|
# these. Drop the connection before reaching Gunicorn.
|
|
# ----------------------------------------------------------------
|
|
location ~* \.(php|asp|aspx|jsp|cgi|env|htaccess|htpasswd|bak|sql|sh|bash|py|rb|pl)$ {
|
|
return 444;
|
|
}
|
|
|
|
# ----------------------------------------------------------------
|
|
# General traffic — moderate rate limit.
|
|
# ----------------------------------------------------------------
|
|
location / {
|
|
limit_req zone=sapl_general burst=${NGINX_BURST_GENERAL} nodelay;
|
|
limit_req_status 429;
|
|
|
|
proxy_set_header X-Request-ID $req_id;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header Host $http_host;
|
|
proxy_redirect off;
|
|
proxy_pass http://sapl_server;
|
|
}
|
|
|
|
error_page 429 /429.html;
|
|
location = /429.html {
|
|
add_header Retry-After 60 always;
|
|
root /var/interlegis/sapl/sapl/static/;
|
|
internal;
|
|
}
|
|
|
|
error_page 500 502 503 504 /500.html;
|
|
location = /500.html {
|
|
root /var/interlegis/sapl/sapl/static/;
|
|
internal;
|
|
}
|
|
}
|
|
|