From 3e9f9dbfedf42f69b152752cc5769ef49aee202b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 04:45:45 +0000 Subject: [PATCH] Add "remove a protected domain" to Authelia's existing-install menu add_authelia_domain() (menu option 1) had no reverse operation -- once a domain was added there was no way to undo it short of hand-editing configuration.yml and the Caddyfile. remove_authelia_domain() (new option 2) does the reverse cleanly: removes the access_control.rules entry, the session.cookies entry, and the auth. Caddy portal block for one domain, verified against a synthetic multi-domain configuration.yml before shipping. Warns loudly that any service still pointed at the removed domain will stop authenticating, and requires confirmation before touching anything. The menu's own text now also flags the likely real mistake this surfaces: adding a domain that's actually just a SUBDOMAIN of an apex already on the instance creates a *.subdomain.apex wildcard rule that doesn't match the bare subdomain itself, plus a redundant separate auth.subdomain.apex portal -- when the subdomain was already covered by the existing apex's own wildcard rule and portal all along. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SpKTLpwAgZNooTacWeQLuc --- services/authelia.sh | 148 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 13 deletions(-) diff --git a/services/authelia.sh b/services/authelia.sh index f2f3511..df302b3 100644 --- a/services/authelia.sh +++ b/services/authelia.sh @@ -225,44 +225,50 @@ install_authelia() { echo "" echo " 1) Add another protected domain to this instance (non-destructive —" echo " one Authelia+Redis, multiple independent apex domains/logins)" - echo " 2) Add a new user (creates a users.yml entry + password hash)" - echo " 3) Manage an existing user (email, password reset, 2FA reset/exempt," + echo " 2) Remove a protected domain added this way (undoes option 1 for one" + echo " domain — other services still pointed at it will stop authenticating)" + echo " 3) Add a new user (creates a users.yml entry + password hash)" + echo " 4) Manage an existing user (email, password reset, 2FA reset/exempt," echo " promote/demote admin, per-service access, delete)" - echo " 4) Register an app to log in VIA Authelia (OIDC/SSO — e.g. ActualBudget," + echo " 5) Register an app to log in VIA Authelia (OIDC/SSO — e.g. ActualBudget," echo " Vaultwarden, or any other app with its own \"Enable OpenID\" setting)" - echo " 5) Reconfigure from scratch (regenerates secrets/users — breaks" + echo " 6) Reconfigure from scratch (regenerates secrets/users — breaks" echo " existing sessions for every domain already on this instance)" - echo " 6) Show who has universal vs. service-scoped access" - echo " 7) Change \"Remember me\" session duration (stay logged in longer)" - echo " 8) Leave as-is" + echo " 7) Show who has universal vs. service-scoped access" + echo " 8) Change \"Remember me\" session duration (stay logged in longer)" + echo " 9) Leave as-is" echo "" local EXISTING_CHOICE="" - prompt_text " Choice [1/2/3/4/5/6/7/8]:" "8" EXISTING_CHOICE + prompt_text " Choice [1/2/3/4/5/6/7/8/9]:" "9" EXISTING_CHOICE case "$EXISTING_CHOICE" in 1) add_authelia_domain return 0 ;; 2) - add_authelia_user + remove_authelia_domain return 0 ;; 3) - edit_authelia_user + add_authelia_user return 0 ;; 4) - _authelia_add_oidc_client + edit_authelia_user return 0 ;; 5) - : # fall through to the full reinstall flow below + _authelia_add_oidc_client + return 0 ;; 6) + : # fall through to the full reinstall flow below + ;; + 7) _authelia_report_access_scope return 0 ;; - 7) + 8) _authelia_set_remember_me return 0 ;; @@ -732,6 +738,122 @@ CADDY_AUTH_BLOCK2 echo "" } +# Removes the auth. Caddy portal block add_authelia_domain() writes — +# same bounded-block technique used elsewhere in this repo for Caddy site +# blocks (find the opening " {" line, walk forward to the matching +# unindented "}"), just keyed on "auth. {" instead of a service +# domain or a reverse_proxy marker. +_authelia_remove_caddy_portal_block() { + local domain="$1" + local caddy_file="$DOCKER_DIR/caddy/Caddyfile" + [ -f "$caddy_file" ] || return 0 + + local domain_line end_line start_line + domain_line="$(grep -nx "auth.${domain} {" "$caddy_file" | head -1 | cut -d: -f1)" + [ -z "$domain_line" ] && return 0 + + start_line="$domain_line" + if [ "$domain_line" -gt 1 ] && sed -n "$((domain_line - 1))p" "$caddy_file" | grep -qE '^# '; then + start_line=$((domain_line - 1)) + fi + + end_line="$(tail -n "+$domain_line" "$caddy_file" | grep -nx '}' | head -1 | cut -d: -f1)" + if [ -z "$end_line" ]; then + log_warning "Could not find the end of auth.${domain}'s Caddy block — leaving it as-is." + return 1 + fi + end_line=$((domain_line + end_line - 1)) + + sed -i "${start_line},${end_line}d" "$caddy_file" + log_info "Removed the auth.${domain} Caddy portal block." + docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^caddy$" && \ + { docker exec -w /etc/caddy caddy caddy reload 2>/dev/null && log_success "Caddy reloaded" \ + || log_warning "Reload manually: docker exec caddy caddy reload --config /etc/caddy/Caddyfile"; } +} + +# Reverse of add_authelia_domain() — removes one apex domain's +# access_control.rules entry, session.cookies entry, and its auth. +# Caddy portal block from this instance. Undoes a domain added by mistake +# (wrong value entered, or a domain that turned out to already be covered by +# an existing apex's wildcard rule — see the menu's own warning text). Does +# NOT touch any other domain already on this instance, and does NOT find or +# fix whatever individual services still point "import authelia"/forward_auth +# at this instance for the removed domain — those start failing to +# authenticate (no session-cookie scope left to complete a login against) +# the moment this runs, so this is for cleaning up a domain that's not +# actually in use this way, not a way to quietly de-protect a live service. +remove_authelia_domain() { + local AUTHELIA_DIR="$DOCKER_DIR/authelia" + local CONFIG_FILE="$AUTHELIA_DIR/config/configuration.yml" + + if [ ! -f "$CONFIG_FILE" ]; then + log_warning "No configuration.yml found at $CONFIG_FILE — install Authelia first." + return 1 + fi + + echo "" + echo " Domains currently on this Authelia instance:" + grep -oE '^ - domain: "\*\.[^"]+"' "$CONFIG_FILE" | sed -E 's/^ - domain: "\*\.(.+)"$/ - \1/' + echo "" + echo " Note: this removes a whole apex domain entry added via 'Add another" + echo " protected domain' — if you meant to protect a SUBDOMAIN of an apex" + echo " already listed above, you don't need this at all: it's already covered" + echo " by that apex's wildcard rule and session-cookie scope. Just point that" + echo " subdomain's Caddy block at this instance's existing auth. portal" + echo " instead of adding it here as its own entry." + echo "" + local RM_DOMAIN="" + prompt_text " Domain to remove (as shown above, e.g. example.com):" "" RM_DOMAIN + if [ -z "$RM_DOMAIN" ]; then + log_warning "No domain entered — nothing to do." + return 0 + fi + + if ! grep -qF "\"*.${RM_DOMAIN}\"" "$CONFIG_FILE" 2>/dev/null; then + log_warning "$RM_DOMAIN isn't configured on this instance — nothing to do." + return 0 + fi + + echo "" + log_warning "This removes ${RM_DOMAIN}'s access rule, session-cookie scope, and its" + log_warning "auth.${RM_DOMAIN} login portal from THIS Authelia instance." + log_warning "Any service still using 'import authelia' or forward_auth pointed at" + log_warning "${RM_DOMAIN} will start failing to authenticate — reconfigure or remove" + log_warning "those first if they're still live." + local CONFIRM_RM="" + prompt_yn " Continue? (y/n):" "n" CONFIRM_RM + [[ "$CONFIRM_RM" =~ ^[Yy]$ ]] || { log_info "Cancelled — nothing changed."; return 0; } + + # ── access_control.rules: remove the "- domain: "*.X"" + "policy: ..." pair ── + awk -v domain="$RM_DOMAIN" ' + BEGIN { skip=0 } + skip == 1 { skip=0; next } + $0 == " - domain: \"*." domain "\"" { skip=1; next } + { print } + ' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" + + # ── session.cookies: remove the "- domain: X" + 2 following lines ───────── + awk -v domain="$RM_DOMAIN" ' + BEGIN { skip=0 } + skip > 0 { skip--; next } + $0 == " - domain: " domain { skip=2; next } + { print } + ' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" + + chown 1000:1000 "$CONFIG_FILE" 2>/dev/null || true + log_success "Removed ${RM_DOMAIN} from $CONFIG_FILE" + + _authelia_remove_caddy_portal_block "$RM_DOMAIN" + + local RESTART_AUTH="" + prompt_yn " Restart Authelia to apply? (y/n):" "y" RESTART_AUTH + if [ "$RESTART_AUTH" = "y" ] || [ "$RESTART_AUTH" = "Y" ]; then + (cd "$AUTHELIA_DIR" && docker compose restart authelia 2>/dev/null) \ + && log_success "Authelia restarted" \ + || log_warning "Restart failed — check: docker compose logs authelia" + fi +} + # Picks "count" random characters from "charset" using an unbiased-enough # per-byte modulo draw from /dev/urandom. Not part of lib/common.sh's shared # generate_password (that one is deliberately alphanumeric-only — see its