# Reverse proxy for the three ciagent.org subdomains, sitting between # Cloudflare (which terminates public-facing TLS and hides this origin's # real IP) and the app's own containers. TLS here is a Cloudflare Origin CA # certificate (Cloudflare dashboard -> SSL/TLS -> Origin Server -> Create # Certificate; covers ciagent.org + *.ciagent.org, up to 15yr validity, only # trusted by Cloudflare - no ACME/renewal machinery needed). Cloudflare SSL # mode must be "Full (strict)" for this to be meaningful. See DEPLOYMENT.md. # # CF-Connecting-IP (the header app.core.security.get_client_ip reads once # TRUSTED_PROXY_IP_HEADER=CF-Connecting-IP is set) needs no special handling # here - Nginx forwards any header it doesn't explicitly touch straight # through to the upstream unmodified. user nginx; worker_processes auto; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; server_tokens off; # Bare :80 hits get redirected to :443 - Cloudflare already enforces # HTTPS at the edge, but the origin shouldn't 400 a direct :80 probe. server { listen 80; server_name ciagent.org api.ciagent.org git.ciagent.org db.ciagent.org; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name ciagent.org; ssl_certificate /etc/nginx/certs/cloudflare-origin.pem; ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key; location / { proxy_pass http://web:3000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } server { listen 443 ssl; server_name api.ciagent.org; ssl_certificate /etc/nginx/certs/cloudflare-origin.pem; ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key; location / { proxy_pass http://api:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } server { listen 443 ssl; server_name git.ciagent.org; ssl_certificate /etc/nginx/certs/cloudflare-origin.pem; ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key; # Large git pushes (Gitea's HTTPS push path). client_max_body_size 512m; location / { proxy_pass http://gitea:3000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } # Settings -> Database viewer (Adminer). Access isn't gated by a shared # password - the app itself (app/api/v1/db_viewer.py) mints a short-lived # token from a live admin session, which /_auth here exchanges for a # session cookie that /_verify re-checks (including a fresh `is_admin` # lookup) on every request via auth_request. See DEPLOYMENT.md. server { listen 443 ssl; server_name db.ciagent.org; ssl_certificate /etc/nginx/certs/cloudflare-origin.pem; ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key; # Adminer renders a raw SQL/data editor - not meant to be framed or # MIME-sniffed as another content type. add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; # Optional extra hardening for admins with a static IP: uncomment # and set your own address to additionally require it alongside a # valid session (default `satisfy all` - both must pass, not either). # allow 203.0.113.9; # deny all; location = /_verify { internal; proxy_pass http://api:8000/api/v1/db-viewer/verify; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header Cookie $http_cookie; } location = /_auth { proxy_pass http://api:8000/api/v1/db-viewer/bootstrap; proxy_set_header Host $host; } location / { auth_request /_verify; proxy_pass http://adminer:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }