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
This commit is contained in:
parent
3236ca332f
commit
3ace667b6c
12 changed files with 264 additions and 49 deletions
|
|
@ -15,20 +15,53 @@ server {
|
|||
|
||||
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 %}
|
||||
# Direct-SSO entrypoint: a tiny page that asks the API for the IdP
|
||||
# authorize URL (no email/domain check on this endpoint) and forwards
|
||||
# the browser there. Link users here instead of /login to skip the
|
||||
# email field entirely. Exact-match so it wins over the `/` prefix.
|
||||
location = {{ opnform_oidc_sso_path }} {
|
||||
# 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+". Go to the login page instead.";var a=document.createElement("a");a.href="/login";a.textContent="Open login page";a.style.display="block";a.style.marginTop="1rem";document.body.appendChild(a)});</script></body></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://ui:3000;
|
||||
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;
|
||||
|
|
@ -45,7 +78,7 @@ server {
|
|||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass api:9000;
|
||||
fastcgi_pass $upstream_api:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue