Files
CIAgent/infrastructure/nginx/nginx.conf
T
sakshamandClaude Sonnet 5 3d6fe56991 Add Settings -> Database viewer (Adminer) for local devs and any admin
Local dev gets an unauthenticated Adminer instance bound to loopback
only. In production, any account with is_admin=true can open it -
the app mints a short-lived token from a live admin session, which
Nginx's new db.ciagent.org block exchanges for a session cookie that
re-checks admin status on every request, instead of a shared static
password that wouldn't scale to multiple admins or revoke live.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-05 22:18:16 -04:00

128 lines
4.5 KiB
Nginx Configuration File

# 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;
}
}
}