# Webmail front door: static SPA + reverse proxy to the API container. # # This file is dropped into /etc/nginx/conf.d/, i.e. it is already inside the # `http` block — hence the bare `map` below. # # TWO LISTENERS, one app: # # :80 public — the mail client. Neither the admin bundle nor /api/admin/* # is served here; both 404. # :81 admin — the admin panel (its own bundle, its own sign-in), and the # /api/admin/* endpoints it needs. # # The compose file publishes :80 as WEB_PORT and :81 as ADMIN_PORT, and the # firewall is what makes the split mean anything: allow ADMIN_PORT from the LAN # only, leave WEB_PORT as exposed as you like (docs/nas-install.md §5.1). # # Note what this boundary is and is not. It is nginx refusing paths on one # listener — good enough to keep the admin panel and its API off an # internet-facing port, and it fails closed if the app forgets a permission # check. It is NOT process isolation: both listeners proxy to the same API # container, so anything that can reach :81 can reach every admin route. The # panel still demands an administrator sign-in, and the API's own `CurrentAdmin` # dependency still guards each route; this is the outer layer, not the only one. map $http_x_forwarded_proto $fwd_proto { default $scheme; https https; http http; } # Connection header for the WebSocket upgrade dance on /api/ws. map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream webmail_api { server api:8000; keepalive 16; } # ---- :80 — public ----------------------------------------------------------- server { listen 80; server_name _; # Regex locations beat prefix ones, so this wins over `location /api/` # regardless of order. `(/|$)` keeps it to /api/admin and /api/admin/..., # never a future /api/administrators. 404 rather than 403: a port open to # the internet shouldn't confirm which endpoints exist. location ~ ^/api/admin(/|$) { access_log off; return 404; } # The admin bundle ships in the same dist; don't hand it out here. location = /admin.html { access_log off; return 404; } include /etc/nginx/webmail-app.conf; # SPA fallback -> the mail client. location / { try_files $uri $uri/ /index.html; } } # ---- :81 — admin ------------------------------------------------------------ server { listen 81; server_name _; include /etc/nginx/webmail-app.conf; location = /admin.html { add_header Cache-Control "no-store, must-revalidate"; } # SPA fallback -> the admin panel. `/` on this port IS the panel; the mail # client is not reachable here at all. location / { try_files $uri /admin.html; } }