From 88466b9b2185cad9f5d499c57b0d753c5335614e Mon Sep 17 00:00:00 2001 From: Ivan Razin Date: Mon, 6 Jul 2026 09:34:40 +0300 Subject: [PATCH] Fix diagnostics 500: harden auth bridge + repair missing panel cert Root cause: nginx auth_request returns a raw 500 to the browser whenever the /__diag_auth subrequest to the panel returns anything other than 2xx/401/403. Two ways that happens: - the panel's TLS cert is recorded in the DB (webCertFile) but the file or symlink is missing on disk (e.g. dangling /root/cert symlink), so the panel can't serve HTTPS and proxy_pass https:// yields a 502; or - the panel returns a login 302 for the AJAX auth probe. Either way the browser got a 500 on the diagnostics page. - Both scripts: add proxy_intercept_errors on + an error_page in /__diag_auth that coerces every non-allow status (3xx/4xx/5xx, incl. 401/403 so the server-level "=404" trap can't rewrite a genuine deny) into a 401 -> @diag_denied, so the flow degrades to a panel-login redirect instead of a 500. Verified with nginx 1.30.3 across 200/401/302/502 upstream backends: all return a clean 302. - x-ui-patch.sh: the panel-HTTPS check now also treats a configured-but- missing cert (-e follows symlinks) as needing repair, re-symlinking the Let's Encrypt cert and re-running `x-ui cert`. Co-Authored-By: Claude Opus 4.8 --- x-ui-latest.sh | 10 +++++++++- x-ui-patch.sh | 27 ++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/x-ui-latest.sh b/x-ui-latest.sh index 2f48377..8a81707 100644 --- a/x-ui-latest.sh +++ b/x-ui-latest.sh @@ -530,8 +530,16 @@ server { proxy_set_header X-Requested-With XMLHttpRequest; proxy_pass_request_body off; proxy_set_header Content-Length ""; - proxy_intercept_errors off; + # auth_request emits a raw 500 to the browser if the subrequest returns + # anything other than 2xx / 401 / 403 (a login 302, or a 502 when the + # panel's HTTPS cert is missing). Coerce every such status to a 401 deny + # so the main location redirects to the panel login instead of 500ing. + # 401/403 must be listed too, else the server-level "error_page 401 =404" + # hijacks a genuine deny into a 404 (which auth_request then 500s on). + proxy_intercept_errors on; + error_page 300 301 302 303 304 305 307 308 400 401 402 403 404 405 500 501 502 503 504 =401 @diag_denied; } + location @diag_denied { return 401; } # ── Network diagnostics page ───────────────────────────────────────────── # No diag cookie yet → bounce through the SSO bridge, which checks the panel diff --git a/x-ui-patch.sh b/x-ui-patch.sh index ca64140..34e2ce1 100644 --- a/x-ui-patch.sh +++ b/x-ui-patch.sh @@ -78,8 +78,19 @@ printf " reality_domain = %s\n" "$reality_domain" # Mirror the main installer: symlink the Let's Encrypt cert into /root/cert and # register it with `x-ui cert`. web_cert=$(db "SELECT value FROM settings WHERE key='webCertFile';") -if [[ -z "$web_cert" ]]; then - blue "Panel has no TLS cert configured — enabling HTTPS..." +web_key=$( db "SELECT value FROM settings WHERE key='webKeyFile';") +# The DB may reference a cert that no longer exists on disk (e.g. a dangling +# /root/cert symlink after a restore) — the panel then fails to serve HTTPS and +# every proxy_pass https:// (panel + diag bridge) breaks. "-e" follows symlinks, +# so a broken link reads as missing. +if [[ -z "$web_cert" || -z "$web_key" ]]; then + need_cert=1; blue "Panel has no TLS cert configured — enabling HTTPS..." +elif [[ ! -e "$web_cert" || ! -e "$web_key" ]]; then + need_cert=1; blue "Panel cert configured but missing on disk ($web_cert) — repairing..." +else + need_cert=0; blue "Panel TLS cert present: $web_cert" +fi +if [[ $need_cert -eq 1 ]]; then if [[ -d "/etc/letsencrypt/live/${domain}" ]]; then mkdir -p "/root/cert/${domain}" chmod 755 /root/cert/* 2>/dev/null || true @@ -97,8 +108,6 @@ if [[ -z "$web_cert" ]]; then else red "No Let's Encrypt cert for ${domain} at /etc/letsencrypt/live/${domain} — cannot enable panel HTTPS." fi -else - blue "Panel TLS cert already configured." fi # ── detect xhttp_path ───────────────────────────────────────────────────────── @@ -428,8 +437,16 @@ server { proxy_set_header X-Requested-With XMLHttpRequest; proxy_pass_request_body off; proxy_set_header Content-Length ""; - proxy_intercept_errors off; + # auth_request emits a raw 500 to the browser if the subrequest returns + # anything other than 2xx / 401 / 403 (a login 302, or a 502 when the + # panel's HTTPS cert is missing). Coerce every such status to a 401 deny + # so the main location redirects to the panel login instead of 500ing. + # 401/403 must be listed too, else the server-level "error_page 401 =404" + # hijacks a genuine deny into a 404 (which auth_request then 500s on). + proxy_intercept_errors on; + error_page 300 301 302 303 304 305 307 308 400 401 402 403 404 405 500 501 502 503 504 =401 @diag_denied; } + location @diag_denied { return 401; } # No diag cookie yet → bounce through the SSO bridge (checks panel session, # mints the cookie) so a bookmarked diag link works once logged into the panel.