From d1d234b4d22af9fe2fb33c13b6fea085163ccae3 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Aug 2026 13:39:02 +0000 Subject: [PATCH] Fix Gatus false-positive red on Authelia-protected and stale-synced sites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-sync condition "[STATUS] < 400" reads as red for any site behind Authelia's forward_auth: Gatus's probe is never logged in, so it correctly gets a 401 back every time — the site is completely healthy, Authelia is just doing its job, but that 401 fails the condition. Confirmed live: every site the user actually logs into showed permanently red. That single condition also had the opposite bug in reserve: on a genuine outage (connection refused, DNS failure, TLS failure), Gatus reports [STATUS] as 0, and 0 < 400 is true — a fully unreachable site would have silently read as "up". Fixed to two conditions together: "[CONNECTED] == true" (catches the actual outage case) and "[STATUS] < 500" (accepts any real response, including 401/403/redirects from an auth gate, only failing on Caddy's own 502/503/504 when the backend itself is unreachable). Also changed the sync loop to refresh conditions on already-synced endpoints, not just add-missing-ones — the old add-if-missing-only logic meant this fix would only apply to newly discovered domains, leaving every already-synced site (which is most of them, on a live box) stuck on the broken condition forever until removed and re-added by hand. Now every sync run (every 15 minutes via the existing systemd timer, or the one that happens immediately on a Gatus reinstall) self-heals all of them. Verified end-to-end against the real mikefarah/yq binary: an existing caddy-sync entry gets its conditions rewritten in place, an unrelated manually-added endpoint is left untouched, and a newly-discovered domain gets the corrected conditions from the start. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn --- services/gatus.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/services/gatus.sh b/services/gatus.sh index 16aab0c..0d10068 100644 --- a/services/gatus.sh +++ b/services/gatus.sh @@ -262,14 +262,34 @@ EXISTING_SYNCED="$(yq e '.endpoints[] | select(.group == "caddy-sync") | .name' ADDED=0 REMOVED=0 +# [CONNECTED] == true catches an actual outage (refused/timed-out +# connection, DNS failure, TLS failure — [STATUS] reads 0 in all of those, +# which is < 400 too, so that condition ALONE would have silently reported +# a fully unreachable site as "up"). [STATUS] < 500 accepts anything the +# server actually answered with, including 401/403/redirects from an +# Authelia-protected site — Gatus's own probe is never logged in, so an +# Authelia-gated site correctly returns 401 to it every time, which is the +# site working exactly as designed, not an outage. Only Caddy's own +# 502/503/504 (backend unreachable) or a connection failure should ever +# read as down here. Confirmed live: with the old "[STATUS] < 400" alone, +# every Authelia-protected site the user actually logs into showed red +# permanently, and a would-be-real outage (STATUS=0) would have shown green. +CONDITIONS='["[CONNECTED] == true", "[STATUS] < 500"]' + while IFS= read -r domain; do [ -z "$domain" ] && continue # Domains only — a defensive filter, not strictly needed since these # come from our own Caddyfile, but cheap insurance against ever # building a yq expression out of anything unexpected. [[ "$domain" =~ ^[a-zA-Z0-9.-]+$ ]] || continue - if ! grep -qxF "$domain" <<< "$EXISTING_SYNCED"; then - yq e -i ".endpoints += [{\"name\": \"${domain}\", \"group\": \"caddy-sync\", \"url\": \"https://${domain}\", \"interval\": \"5m\", \"conditions\": [\"[STATUS] < 400\"]}]" "$GATUS_CONFIG" \ + if grep -qxF "$domain" <<< "$EXISTING_SYNCED"; then + # Already synced — refresh its conditions too, not just add-if- + # missing, so a template fix like this one reaches every + # already-added endpoint on the very next sync instead of leaving + # them stuck on whatever conditions they were first created with. + yq e -i "(.endpoints[] | select(.group == \"caddy-sync\" and .name == \"${domain}\") | .conditions) = ${CONDITIONS}" "$GATUS_CONFIG" + else + yq e -i ".endpoints += [{\"name\": \"${domain}\", \"group\": \"caddy-sync\", \"url\": \"https://${domain}\", \"interval\": \"5m\", \"conditions\": ${CONDITIONS}}]" "$GATUS_CONFIG" \ && ADDED=$((ADDED + 1)) fi done <<< "$CURRENT_DOMAINS"