digitalboard.core/roles/opnform/templates/nginx.conf.j2
Simon Bärlocher 3ace667b6c
feat(services): refine split-horizon OIDC routing and harden nextcloud patch
- authentik: address the rewrite service by compose service name instead
  of a network alias on the public FQDN, which shadowed extra_hosts pins
  and broke OIDC discovery for c-ares-based (Node) resolvers
- homarr: add homarr_extra_hosts to pin the IdP FQDN to a LAN IP so OIDC
  discovery stays in-network while the issuer matches the browser-facing URL
- opnform: add opnform_oidc_sso_redirect_root to 302 the root URL to the
  SSO path (deep-links untouched, /login?bypass=1 break-glass); restart
  ingress via container restart so envsubst re-renders nginx.conf
- nextcloud: make the UserConfig sed workaround fail loud on upstream
  drift instead of silently skipping (nextcloud/server#59629)
- gitignore: exclude the local .ansible/ collection cache
2026-06-02 13:44:08 +02:00

87 lines
3.8 KiB
Django/Jinja

map $original_uri $api_uri {
~^/api(/.*$) $1;
default $original_uri;
}
server {
listen 80;
server_name {{ opnform_domain }};
root /app/public;
client_max_body_size {% raw %}${NGINX_MAX_BODY_SIZE}{% endraw %};
access_log /dev/stdout;
error_log /dev/stderr error;
index index.html index.htm index.php;
# Re-resolve upstream container hostnames via Docker's embedded DNS
# at request time. Without this, nginx caches the first resolution
# forever; if `api` or `ui` get recreated and pick up a new IP, every
# request 502s until the ingress itself is restarted.
resolver 127.0.0.11 valid=10s ipv6=off;
set $upstream_api api;
set $upstream_ui ui;
{% if opnform_oidc_enabled and opnform_oidc_sso_entrypoint %}
# Root → /login. Public forms live under /forms/<slug>, so the bare
# hostname only serves the authenticated dashboard — sending it
# straight to /login (which then jumps to the IdP) saves an extra
# UI-side redirect for anyone who lands there.
location = / {
return 302 /login;
}
# /login intercept: serve a tiny HTML page that calls OpnForm's
# /api/auth/{slug}/redirect endpoint and forwards the browser to the
# IdP authorize URL — skipping the email-based login form entirely.
# Break-glass: /login?bypass=1 falls through to the UI's own login
# form so the email/password path stays reachable when the IdP is
# down. Bypass branches to a named location (`@login_bypass`) because
# `proxy_pass` inside an `if` block is invalid nginx config.
location = /login {
if ($arg_bypass = "1") {
error_page 418 = @login_bypass;
return 418;
}
default_type text/html;
return 200 '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Redirecting to sign-in…</title></head><body style="font-family:sans-serif;text-align:center;padding:3rem;color:#374151"><p id="m">Redirecting to sign-in…</p><script>fetch("/api/auth/{{ opnform_oidc_slug }}/redirect",{method:"POST",headers:{Accept:"application/json"}}).then(function(r){if(!r.ok)throw new Error("HTTP "+r.status);return r.json()}).then(function(d){if(d&&d.redirect_url){window.location.replace(d.redirect_url)}else{throw new Error("no redirect_url")}}).catch(function(e){document.getElementById("m").textContent="Sign-in redirect failed: "+e.message+". Open /login?bypass=1 to use the email form.";});</script></body></html>';
}
location @login_bypass {
proxy_http_version 1.1;
proxy_pass http://$upstream_ui:3000/login;
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 $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
{% endif %}
location / {
proxy_http_version 1.1;
proxy_pass http://$upstream_ui:3000;
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 $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ~/(api|open|local\/temp|forms\/assets)/ {
set $original_uri $uri;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass $upstream_api:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php;
fastcgi_param REQUEST_URI $api_uri;
}
}