Merge pull request #394 from outis1one/claude/frigate-authelia-openid-0l1htj
Claude/frigate authelia openid 0l1htj
This commit is contained in:
@@ -422,22 +422,49 @@ existing login page. Reuse `_authelia_provision_oidc_client()` (guarded by
|
||||
instead of duplicating Authelia's client-secret-generation/config-patching
|
||||
logic again.
|
||||
|
||||
**Scoping a domain to specific users instead of every Authelia user.**
|
||||
By default, any domain with an `access_control` rule at all is reachable by
|
||||
every Authelia user (the existing catch-all `*.${AUTHELIA_DOMAIN}` rule).
|
||||
`services/authelia.sh`'s `_authelia_scope_access(SERVICE_ID, DOMAIN)` is a
|
||||
generic, reusable opt-in on top of that — call it right after *any* service
|
||||
finishes being protected by Authelia, forward_auth gate or native OIDC
|
||||
alike (it only cares about the domain, not the gating mechanism; see
|
||||
`_gitea_offer_authelia_sso()` for the reference caller). Asks whether
|
||||
access should stay universal or be scoped to specific usernames; if scoped,
|
||||
creates a dedicated `<service_id>-only` group, adds every listed username
|
||||
to it (creating accounts on the fly via
|
||||
**Internal vs. outside access — named, reusable groups, not one group per
|
||||
service.** By default, any domain with an `access_control` rule at all is
|
||||
"internal": reachable by every Authelia user (the existing catch-all
|
||||
`*.${AUTHELIA_DOMAIN}` rule) — admins included automatically, since the
|
||||
admin-bypass rule (below) always outranks it anyway. `services/authelia.sh`'s
|
||||
`_authelia_scope_access(SERVICE_ID, DOMAIN)` is the generic, reusable opt-in
|
||||
on top of that for "outside access" — call it right after *any* service
|
||||
finishes being protected by Authelia, forward_auth gate or native OIDC alike
|
||||
(it only cares about the domain, not the gating mechanism; see
|
||||
`_gitea_offer_authelia_sso()` for the reference caller). Asks "Internal
|
||||
(default) or Outside access", and if outside access, lets the admin pick an
|
||||
*existing* named group (by number, so e.g. "customer1" can be attached to a
|
||||
second, third, unrelated site later) or type a new one — `service_id` is
|
||||
only the suggested default name, never forced. Creates the group if new
|
||||
(adding every listed username to it, creating accounts on the fly via
|
||||
`_authelia_create_user_noninteractive()` for names that don't exist yet,
|
||||
printing their temp password), and inserts two rules *above* the general
|
||||
catch-all — allow that group on this domain, deny that group on every
|
||||
other protected domain. Idempotent: reruns against an already-scoped
|
||||
domain just report the existing group instead of duplicating rules.
|
||||
printing their temp password) and inserts two rules *above* the general
|
||||
catch-all but *below* the admin-bypass rule — allow that group on this
|
||||
domain, deny that group on every other protected domain. The already-scoped
|
||||
check is keyed to the (domain, group) pair, not the group name alone, so
|
||||
reusing a group on a second site correctly adds that site's own rule instead
|
||||
of a false "already scoped" no-op (a real bug in an earlier version of this
|
||||
function, since fixed).
|
||||
|
||||
Three more menu options round this out: **13** backfills the admin-bypass
|
||||
rule (below) onto any apex domain missing it; **14** renames a group
|
||||
everywhere it's referenced (rules + every member); **15** lists every
|
||||
group's sites and members in one place; **16** is the reverse of the
|
||||
scoping prompt's own member-picker — pick a group first, then toggle which
|
||||
users are in it, for adding members without re-touching a site.
|
||||
|
||||
**Admins always match first, on every domain — old sites and new.**
|
||||
`_authelia_ensure_admin_bypass(config_file, domain)` inserts
|
||||
`- domain: "*.${domain}" / subject: "group:admins" / policy: two_factor` as
|
||||
literally the first rule under `rules:`, and every insertion point that
|
||||
adds new rules (`add_authelia_domain`, `_authelia_scope_access`) inserts
|
||||
*after* this block rather than at the literal top of `rules:`, so a later
|
||||
scoping action can never accidentally outrank it. Without this, a group's
|
||||
deny-elsewhere rule (above) would deny an admin who's ever added to that
|
||||
group on every OTHER domain — this rule exists specifically so that can't
|
||||
happen. `install_authelia()`/`add_authelia_domain()` bake it in for
|
||||
anything created from here on; menu option 13 backfills it onto an
|
||||
instance that predates the feature.
|
||||
Guard every cross-file call with `declare -F`, same convention as the OIDC
|
||||
helper above — a service can run standalone with authelia.sh never sourced.
|
||||
|
||||
|
||||
+166
-11
@@ -250,10 +250,13 @@ install_authelia() {
|
||||
echo " 15) Show every group's sites and users (site groups + user groups overview)"
|
||||
echo " 16) Add/remove users from a group (pick the group, then toggle members —"
|
||||
echo " the reverse of option 4's per-user group toggle)"
|
||||
echo " 17) Bulk-assign several users to one group at once (e.g. \"1 4 5\" ->"
|
||||
echo " internal, then \"2 3 7\" -> a named group), with each user's"
|
||||
echo " current access shown alongside their name"
|
||||
echo " 0) Leave as-is / exit"
|
||||
echo ""
|
||||
local EXISTING_CHOICE=""
|
||||
prompt_text " Choice [1-16, 0 to exit]:" "0" EXISTING_CHOICE
|
||||
prompt_text " Choice [1-17, 0 to exit]:" "0" EXISTING_CHOICE
|
||||
case "$EXISTING_CHOICE" in
|
||||
1)
|
||||
add_authelia_domain
|
||||
@@ -318,6 +321,10 @@ install_authelia() {
|
||||
_authelia_manage_group_membership
|
||||
return 0
|
||||
;;
|
||||
17)
|
||||
_authelia_bulk_assign_group
|
||||
return 0
|
||||
;;
|
||||
0|*)
|
||||
echo " Keeping existing Authelia. (Edit config/users.yml then: cd $AUTHELIA_DIR && docker compose restart authelia)"
|
||||
return 0
|
||||
@@ -1689,11 +1696,11 @@ _authelia_scope_access() {
|
||||
|
||||
echo ""
|
||||
echo " Who should be able to reach $domain via Authelia?"
|
||||
echo " 0) Native — your own users, no extra restriction (default — same"
|
||||
echo " 0) Internal — your own users, no extra restriction (default — same"
|
||||
echo " access as everything else)"
|
||||
echo " 1) Outside access — a named group of specific users only"
|
||||
local scope_choice=""
|
||||
prompt_text " Choice [0 for native, 1 for outside access]:" "0" scope_choice
|
||||
prompt_text " Choice [0 for internal, 1 for outside access]:" "0" scope_choice
|
||||
[ "$scope_choice" = "1" ] || return 0
|
||||
|
||||
local -a existing_groups
|
||||
@@ -1936,7 +1943,7 @@ _authelia_report_groups() {
|
||||
local -a groups
|
||||
mapfile -t groups < <(_authelia_list_scoped_groups "$users_file")
|
||||
if [ "${#groups[@]}" -eq 0 ]; then
|
||||
log_info "No outside-access groups exist yet — every site is native (open to any Authelia user, admins always included)."
|
||||
log_info "No outside-access groups exist yet — every site is internal (open to any Authelia user, admins always included)."
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -1995,7 +2002,7 @@ _authelia_manage_group_membership() {
|
||||
if [ "${#groups[@]}" -eq 0 ]; then
|
||||
log_info "No outside-access groups exist yet. A group is created the first time you"
|
||||
log_info "protect a site (option 10, or a service's own \"Add Sign in with Authelia\""
|
||||
log_info "offer) and choose \"Outside access\" instead of \"Native\" — name it there"
|
||||
log_info "offer) and choose \"Outside access\" instead of \"Internal\" — name it there"
|
||||
log_info "(e.g. \"customer1\"), and it'll show up here afterward to manage its members."
|
||||
return 0
|
||||
fi
|
||||
@@ -2075,6 +2082,129 @@ _authelia_manage_group_membership() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Bulk version spanning BOTH axes at once — several users, one target group,
|
||||
# in a single step (e.g. "1 4 5 6 -> internal", then "2 3 7 8 -> external1"),
|
||||
# repeatable for as many user/group batches as needed in one menu visit.
|
||||
# Complements the two single-axis tools above: option 6 (per-user menu) is
|
||||
# one user, many groups to toggle; option 16 is one group, many users to
|
||||
# toggle; this is many users, one group, picked together. "Internal" isn't
|
||||
# a real group — picking it clears every outside-access group membership
|
||||
# for the selected users, since internal access is the absence of a
|
||||
# restricting group, not a group of its own.
|
||||
_authelia_bulk_assign_group() {
|
||||
local users_file="$DOCKER_DIR/authelia/config/users.yml"
|
||||
[ -f "$users_file" ] || { log_warning "No users.yml found — install Authelia first."; return 1; }
|
||||
|
||||
local KEEP_GOING="y"
|
||||
while [[ "$KEEP_GOING" =~ ^[Yy]$ ]]; do
|
||||
local -a all_users
|
||||
mapfile -t all_users < <(_authelia_list_usernames "$users_file")
|
||||
if [ "${#all_users[@]}" -eq 0 ]; then
|
||||
log_info "No users exist yet — add one first (this menu's \"Add a new user\")."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Existing users:"
|
||||
local i start_end start end
|
||||
for i in "${!all_users[@]}"; do
|
||||
start_end="$(_authelia_user_line_range "$users_file" "${all_users[$i]}")"
|
||||
start="${start_end% *}"; end="${start_end#* }"
|
||||
echo " $((i + 1))) ${all_users[$i]} [$(_authelia_describe_user_access "$users_file" "$start" "$end")]"
|
||||
done
|
||||
echo ""
|
||||
echo " Select one or more users by number (space-separated), or 0 to cancel."
|
||||
local USEL=""
|
||||
prompt_text " User number(s) [0]:" "0" USEL
|
||||
if [ -z "$USEL" ] || [ "$USEL" = "0" ]; then
|
||||
log_info "Cancelled."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local -a usel_tokens targets=()
|
||||
read -ra usel_tokens <<< "$USEL"
|
||||
local tok
|
||||
for tok in "${usel_tokens[@]}"; do
|
||||
if [[ "$tok" =~ ^[0-9]+$ ]] && [ "$tok" -ge 1 ] && [ "$tok" -le "${#all_users[@]}" ]; then
|
||||
targets+=("${all_users[$((tok - 1))]}")
|
||||
else
|
||||
log_warning "Skipping invalid selection: $tok"
|
||||
fi
|
||||
done
|
||||
if [ "${#targets[@]}" -eq 0 ]; then
|
||||
log_warning "No valid users selected."
|
||||
prompt_yn " Try again? (y/n):" "n" KEEP_GOING
|
||||
continue
|
||||
fi
|
||||
|
||||
local -a existing_groups
|
||||
mapfile -t existing_groups < <(_authelia_list_scoped_groups "$users_file")
|
||||
echo ""
|
||||
echo " Assign ${#targets[@]} user(s) to:"
|
||||
echo " 0) Internal — remove from every outside-access group"
|
||||
local gi
|
||||
for gi in "${!existing_groups[@]}"; do
|
||||
echo " $((gi + 1))) ${existing_groups[$gi]%-only}"
|
||||
done
|
||||
echo " Or type a new group name to create one."
|
||||
local GSEL=""
|
||||
prompt_text " Group [0 for internal]:" "0" GSEL
|
||||
|
||||
if [ -z "$GSEL" ] || [ "$GSEL" = "0" ]; then
|
||||
local t t_start_end t_start t_end g
|
||||
for t in "${targets[@]}"; do
|
||||
t_start_end="$(_authelia_user_line_range "$users_file" "$t")"
|
||||
t_start="${t_start_end% *}"; t_end="${t_start_end#* }"
|
||||
for g in "${existing_groups[@]}"; do
|
||||
sed -n "${t_start},${t_end}p" "$users_file" | grep -qF " - ${g}" \
|
||||
&& _authelia_toggle_group "$users_file" "$t_start" "$t_end" "$g" "false"
|
||||
done
|
||||
log_success "${t} set to internal (removed from every outside-access group)"
|
||||
done
|
||||
else
|
||||
local group=""
|
||||
if [[ "$GSEL" =~ ^[0-9]+$ ]] && [ "$GSEL" -ge 1 ] && [ "$GSEL" -le "${#existing_groups[@]}" ]; then
|
||||
group="${existing_groups[$((GSEL - 1))]}"
|
||||
else
|
||||
local clean_name
|
||||
clean_name="$(echo "$GSEL" | tr -cs 'a-zA-Z0-9_-' '-' | sed 's/^-*//;s/-*$//')"
|
||||
if [ -z "$clean_name" ]; then
|
||||
log_warning "Invalid group name — nothing changed."
|
||||
prompt_yn " Try again? (y/n):" "n" KEEP_GOING
|
||||
continue
|
||||
fi
|
||||
group="${clean_name}-only"
|
||||
local is_new="true" eg
|
||||
for eg in "${existing_groups[@]}"; do [ "$eg" = "$group" ] && is_new="false"; done
|
||||
if [ "$is_new" = "true" ]; then
|
||||
log_warning "'$clean_name' isn't attached to any site yet — membership alone won't grant"
|
||||
log_warning "access to anything until a site is scoped to it (site protection's"
|
||||
log_warning "\"Outside access\" choice, or re-running a service's own SSO offer)."
|
||||
fi
|
||||
fi
|
||||
local t t_start_end2 t_start2 t_end2
|
||||
for t in "${targets[@]}"; do
|
||||
t_start_end2="$(_authelia_user_line_range "$users_file" "$t")"
|
||||
t_start2="${t_start_end2% *}"; t_end2="${t_start_end2#* }"
|
||||
_authelia_toggle_group "$users_file" "$t_start2" "$t_end2" "$group" "true"
|
||||
log_success "Added ${t} to '${group%-only}'"
|
||||
done
|
||||
fi
|
||||
|
||||
chown 1000:1000 "$users_file" 2>/dev/null || true
|
||||
local RESTART_AUTH=""
|
||||
prompt_yn " Restart Authelia to apply? (y/n):" "y" RESTART_AUTH
|
||||
if [[ "$RESTART_AUTH" =~ ^[Yy]$ ]]; then
|
||||
(cd "$DOCKER_DIR/authelia" && docker compose restart authelia 2>/dev/null) \
|
||||
&& log_success "Authelia restarted" \
|
||||
|| log_warning "Restart failed — check: docker compose logs authelia"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
prompt_yn " Assign another batch (different users and/or a different group)? (y/n):" "n" KEEP_GOING
|
||||
done
|
||||
}
|
||||
|
||||
# Menu-driven, idempotent bulk version of _authelia_ensure_admin_bypass —
|
||||
# backfills the admin-bypass rule for every apex domain currently on this
|
||||
# instance in one pass. install_authelia and add_authelia_domain bake the
|
||||
@@ -2147,7 +2277,7 @@ _authelia_report_access_scope() {
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Native (universal — every protected domain, no outside-access group):"
|
||||
echo " Internal (universal — every protected domain, no outside-access group):"
|
||||
local -a universal=() restricted=()
|
||||
local u start_end start end groups_in_range
|
||||
for u in "${all_users[@]}"; do
|
||||
@@ -2452,6 +2582,23 @@ _authelia_set_2fa_exempt() {
|
||||
chown 1000:1000 "$config_file" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# One-line access summary for a user, e.g. "admin" / "internal" /
|
||||
# "customer1, customer2" / "admin, customer1" — used by both
|
||||
# edit_authelia_user()'s listing and _authelia_bulk_assign_group() so a
|
||||
# user's current privileges are visible right where you're about to change
|
||||
# them, not something you have to cross-check against option 15 first.
|
||||
_authelia_describe_user_access() {
|
||||
local users_file="$1" start="$2" end="$3"
|
||||
local -a tags=()
|
||||
sed -n "${start},${end}p" "$users_file" | grep -q '^ - admins$' && tags+=("admin")
|
||||
local -a groups
|
||||
mapfile -t groups < <(sed -n "${start},${end}p" "$users_file" | grep -oE '^ - [a-zA-Z0-9_-]+-only$' | sed 's/^ - //; s/-only$//')
|
||||
tags+=("${groups[@]}")
|
||||
[ "${#tags[@]}" -eq 0 ] && tags=("internal")
|
||||
local IFS=", "
|
||||
echo "${tags[*]}"
|
||||
}
|
||||
|
||||
# Interactive: pick an existing user from users.yml, then act on them —
|
||||
# edit email/display name, force a password reset, reset their 2FA device,
|
||||
# toggle whether they need 2FA at all, or toggle admin group membership.
|
||||
@@ -2481,9 +2628,11 @@ edit_authelia_user() {
|
||||
|
||||
echo ""
|
||||
echo " Existing users:"
|
||||
local i
|
||||
local i u_start_end u_start u_end
|
||||
for i in "${!USERNAMES[@]}"; do
|
||||
echo " $((i + 1))) ${USERNAMES[$i]}"
|
||||
u_start_end="$(_authelia_user_line_range "$USERS_FILE" "${USERNAMES[$i]}")"
|
||||
u_start="${u_start_end% *}"; u_end="${u_start_end#* }"
|
||||
echo " $((i + 1))) ${USERNAMES[$i]} [$(_authelia_describe_user_access "$USERS_FILE" "$u_start" "$u_end")]"
|
||||
done
|
||||
echo ""
|
||||
echo " Select one or more by number (space-separated, e.g. \"2 4\"),"
|
||||
@@ -3200,14 +3349,20 @@ _authelia_add_oidc_client() {
|
||||
echo ""
|
||||
;;
|
||||
6)
|
||||
echo " Beszel is PocketBase-based — its OAuth2 provider is a PocketBase admin"
|
||||
echo " setting, not an API this script can write (checked against beszel.dev's"
|
||||
echo " own docs). In the hub: Settings -> Auth providers -> OpenID Connect:"
|
||||
echo " Beszel is PocketBase-based — its OAuth2 provider lives in PocketBase's"
|
||||
echo " own admin panel underneath the hub, not the hub's own Settings page and"
|
||||
echo " not an API this script can write (checked against beszel.dev's own docs):"
|
||||
echo " 1) https://<your-beszel-domain>/_/#/settings -> toggle OFF \"Hide"
|
||||
echo " collection create and edit controls\""
|
||||
echo " 2) Collections -> edit the \"users\" collection"
|
||||
echo " 3) Options tab -> enable OAuth2 -> Add provider, fill in:"
|
||||
echo " Client ID: ${CLIENT_ID}"
|
||||
echo " Client Secret: ${CLIENT_SECRET_PLAIN}"
|
||||
echo " Auth URL: https://${AUTHELIA_PORTAL_DOMAIN}/api/oidc/authorization"
|
||||
echo " Token URL: https://${AUTHELIA_PORTAL_DOMAIN}/api/oidc/token"
|
||||
echo " User Info URL: https://${AUTHELIA_PORTAL_DOMAIN}/api/oidc/userinfo"
|
||||
echo " 4) Save, then toggle \"Hide collection create and edit controls\" back"
|
||||
echo " ON — leaving it off is its own exposure once you're done"
|
||||
echo " Register your first Beszel account with a password BEFORE touching"
|
||||
echo " DISABLE_PASSWORD_AUTH/USER_CREATION in its .env — flipping those before"
|
||||
echo " a working login exists risks locking the hub's UI out entirely."
|
||||
|
||||
+56
-23
@@ -288,41 +288,73 @@ _beszel_offer_authelia_oidc() {
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " In the hub: Settings -> Auth providers -> OpenID Connect:"
|
||||
echo " This lives in PocketBase's own admin panel underneath the hub, not the"
|
||||
echo " hub's own Settings page — checked against beszel.dev's OAuth guide"
|
||||
echo " directly, exact steps:"
|
||||
echo " 1) Go to https://<your-beszel-domain>/_/#/settings and toggle OFF"
|
||||
echo " \"Hide collection create and edit controls\""
|
||||
echo " 2) Go to Collections, edit the \"users\" collection"
|
||||
echo " 3) Options tab -> enable OAuth2 -> Add provider, fill in:"
|
||||
echo " Client ID: beszel"
|
||||
echo " Client Secret: $OIDC_CLIENT_SECRET_PLAIN"
|
||||
echo " Auth URL: ${OIDC_AUTHELIA_PORTAL_URL}/api/oidc/authorization"
|
||||
echo " Token URL: ${OIDC_AUTHELIA_PORTAL_URL}/api/oidc/token"
|
||||
echo " User Info URL: ${OIDC_AUTHELIA_PORTAL_URL}/api/oidc/userinfo"
|
||||
echo " 4) Save, then toggle \"Hide collection create and edit controls\" back ON"
|
||||
echo " at /_/#/settings — leaving it off is its own exposure once you're done"
|
||||
echo ""
|
||||
log_warning "The Client Secret above is shown once — save it now."
|
||||
|
||||
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "beszel" "$APP_DOMAIN"
|
||||
|
||||
# DISABLE_PASSWORD_AUTH/USER_CREATION are real, documented env vars
|
||||
# (beszel.dev's own OAuth guide) — but Beszel has no default account:
|
||||
# the FIRST person to register becomes admin (see this file's own
|
||||
# README section), and there's no signup-fallback like Mealie's
|
||||
# OIDC_SIGNUP_ENABLED convenience wording to lean on if that hasn't
|
||||
# happened yet. Off by default and gated behind an explicit warning —
|
||||
# flipping this before a working login exists risks locking the hub's
|
||||
# UI out with no account able to reach it at all.
|
||||
echo ""
|
||||
local _disable_local=""
|
||||
prompt_yn " Also disable Beszel's own password login, so Authelia is the only way in? Only do this AFTER you've registered a working account. (y/n):" "n" _disable_local
|
||||
if [[ "$_disable_local" =~ ^[Yy]$ ]]; then
|
||||
local _auto_register=""
|
||||
prompt_yn " Auto-create Beszel accounts for new Authelia logins? (y/n):" "n" _auto_register
|
||||
sed -i '/^DISABLE_PASSWORD_AUTH=/d; /^USER_CREATION=/d' "$dir/.env"
|
||||
{
|
||||
echo "DISABLE_PASSWORD_AUTH=true"
|
||||
[[ "$_auto_register" =~ ^[Yy]$ ]] && echo "USER_CREATION=true"
|
||||
} >> "$dir/.env"
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$dir/.env" 2>/dev/null || true
|
||||
( cd "$dir" && docker compose up -d beszel ) \
|
||||
&& log_success "Beszel's own password login is now disabled — Authelia is the only way in." \
|
||||
|| log_warning "Restart failed — check: docker compose -f $dir/docker-compose.yml logs beszel"
|
||||
log_info "Paste those values into Beszel's Settings -> Auth providers -> OpenID"
|
||||
log_info "Connect page now, then log out and click through the Authelia login"
|
||||
log_info "button to confirm it actually works — BEFORE going any further here."
|
||||
echo ""
|
||||
log_warning "The next step can disable Beszel's password login entirely. Confirmed"
|
||||
log_warning "live: saying yes here before actually testing the button leaves NEITHER"
|
||||
log_warning "login path working — the password form is gone, and the OAuth provider"
|
||||
log_warning "was never actually finished on Beszel's side, so its button never"
|
||||
log_warning "appears either. Re-run 'sudo ./setup.sh beszel' (choose update) any time"
|
||||
log_warning "later to come back to this once you've verified the button works."
|
||||
}
|
||||
|
||||
# Split out from _beszel_offer_authelia_oidc so it can also be re-reached on
|
||||
# its own via a later "update" rerun, once the admin has actually gone and
|
||||
# tested the Authelia login button — see that function's own warning for
|
||||
# why this can't be offered in the same breath as printing the paste-in
|
||||
# values. DISABLE_PASSWORD_AUTH/USER_CREATION are real, documented env vars
|
||||
# (beszel.dev's own OAuth guide).
|
||||
_beszel_offer_disable_password_auth() {
|
||||
local dir="$1"
|
||||
[ -f "$dir/.env" ] || return 0
|
||||
grep -qF "client_id: 'beszel'" "$DOCKER_DIR/authelia/config/configuration.yml" 2>/dev/null || return 0
|
||||
grep -q '^DISABLE_PASSWORD_AUTH=true' "$dir/.env" 2>/dev/null && return 0
|
||||
|
||||
echo ""
|
||||
local _tested=""
|
||||
prompt_yn " Have you ALREADY logged into Beszel successfully using the Authelia button (not just pasted the values)? (y/n):" "n" _tested
|
||||
if [[ ! "$_tested" =~ ^[Yy]$ ]]; then
|
||||
log_info "Skipped. Test the Authelia login button first, then re-run 'sudo ./setup.sh beszel' (choose update) to come back to this."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local _disable_local=""
|
||||
prompt_yn " Disable Beszel's own password login now, so Authelia is the only way in? (y/n):" "n" _disable_local
|
||||
[[ "$_disable_local" =~ ^[Yy]$ ]] || return 0
|
||||
|
||||
local _auto_register=""
|
||||
prompt_yn " Auto-create Beszel accounts for new Authelia logins? (y/n):" "n" _auto_register
|
||||
sed -i '/^DISABLE_PASSWORD_AUTH=/d; /^USER_CREATION=/d' "$dir/.env"
|
||||
{
|
||||
echo "DISABLE_PASSWORD_AUTH=true"
|
||||
[[ "$_auto_register" =~ ^[Yy]$ ]] && echo "USER_CREATION=true"
|
||||
} >> "$dir/.env"
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$dir/.env" 2>/dev/null || true
|
||||
( cd "$dir" && docker compose up -d beszel ) \
|
||||
&& log_success "Beszel's own password login is now disabled — Authelia is the only way in." \
|
||||
|| log_warning "Restart failed — check: docker compose -f $dir/docker-compose.yml logs beszel"
|
||||
}
|
||||
|
||||
install_beszel() {
|
||||
@@ -361,6 +393,7 @@ install_beszel() {
|
||||
[[ "$FINISH_AGENT" =~ ^[Yy]$ ]] && _beszel_configure_agent "$DIR" "http://localhost:${_WP} (or its Caddy domain, once configured)"
|
||||
fi
|
||||
_beszel_offer_authelia_oidc "$DIR"
|
||||
_beszel_offer_disable_password_auth "$DIR"
|
||||
return 0
|
||||
;;
|
||||
cancel)
|
||||
|
||||
+53
-34
@@ -228,7 +228,10 @@ _mealie_offer_authelia_oidc() {
|
||||
log_info "Authelia SSO is already configured for Mealie (OIDC_* already set in $DIR/.env)."
|
||||
local RECONFIGURE=""
|
||||
prompt_yn " Reconfigure it (registers a fresh Authelia client + secret)? (y/n):" "n" RECONFIGURE
|
||||
[[ "$RECONFIGURE" =~ ^[Yy]$ ]] || return 0
|
||||
if [[ ! "$RECONFIGURE" =~ ^[Yy]$ ]]; then
|
||||
_mealie_offer_disable_password_login "$DIR"
|
||||
return 0
|
||||
fi
|
||||
# ALLOW_PASSWORD_LOGIN isn't OIDC_-prefixed but is written by this
|
||||
# same step (see below) — strip it too so reconfiguring doesn't
|
||||
# leave a stale duplicate line if it's set again.
|
||||
@@ -251,27 +254,6 @@ _mealie_offer_authelia_oidc() {
|
||||
prompt_yn " Require two-factor for Mealie logins via Authelia too? (y/n):" "y" _2fa
|
||||
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
||||
|
||||
# Mealie's own docs document ALLOW_PASSWORD_LOGIN (hides the
|
||||
# username+password fields entirely) and OIDC_AUTO_REDIRECT (skips the
|
||||
# login page and jumps straight to the identity provider) as the real,
|
||||
# supported way to make Authelia the only way in — confirmed against
|
||||
# docs.mealie.io's OIDC and backend-config pages directly, not assumed.
|
||||
# Off by default: this is a real access-control change (anyone who
|
||||
# only has a local Mealie account, not an Authelia one, loses their
|
||||
# login path), not just an additive convenience like the SSO button
|
||||
# above. Recoverable any time by flipping these back and restarting —
|
||||
# worth saying so, since a locked-out admin's first instinct otherwise
|
||||
# is to worry the account itself is gone.
|
||||
echo ""
|
||||
local _disable_local=""
|
||||
prompt_yn " Also disable Mealie's own username/password login, so Authelia is the only way in? (y/n):" "n" _disable_local
|
||||
local _auto_redirect=""
|
||||
if [[ "$_disable_local" =~ ^[Yy]$ ]]; then
|
||||
log_warning "Anyone without an Authelia account (only a local Mealie one) will no longer be able to log in."
|
||||
log_info "Reversible any time: set ALLOW_PASSWORD_LOGIN back to true in $DIR/.env and 'docker compose up -d'."
|
||||
prompt_yn " Skip Mealie's login page entirely and jump straight to Authelia? (y/n):" "y" _auto_redirect
|
||||
fi
|
||||
|
||||
if ! _authelia_provision_oidc_client "Mealie" "mealie" "$AUTH_POLICY" "y" "n" "${BASE_URL}/login"; then
|
||||
log_warning "Couldn't register Mealie as an OIDC client in Authelia — skipping SSO setup."
|
||||
return 0
|
||||
@@ -289,15 +271,6 @@ OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET_PLAIN
|
||||
OIDC_CONFIGURATION_URL=$_discovery_url
|
||||
OIDC_PROVIDER_NAME=Authelia
|
||||
ENV
|
||||
if [[ "$_disable_local" =~ ^[Yy]$ ]]; then
|
||||
echo "ALLOW_PASSWORD_LOGIN=false" >> "$DIR/.env"
|
||||
fi
|
||||
if [[ "$_auto_redirect" =~ ^[Yy]$ ]]; then
|
||||
{
|
||||
echo "OIDC_AUTO_REDIRECT=true"
|
||||
echo "OIDC_REMEMBER_ME=true"
|
||||
} >> "$DIR/.env"
|
||||
fi
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$DIR/.env" 2>/dev/null || true
|
||||
|
||||
# Mealie's OIDC redirect URI generation trusts X-Forwarded-* only from
|
||||
@@ -312,13 +285,59 @@ ENV
|
||||
sed -i "/container_name: ${CONTAINER}\$/a\\ entrypoint: [\"uvicorn\", \"mealie.app:app\", \"--host\", \"0.0.0.0\", \"--port\", \"9000\", \"--forwarded-allow-ips=*\"]" "$DIR/docker-compose.yml"
|
||||
fi
|
||||
|
||||
local _success_msg="\"Sign in with Authelia\" added to Mealie — local login still works too."
|
||||
[[ "$_disable_local" =~ ^[Yy]$ ]] && _success_msg="\"Sign in with Authelia\" added to Mealie — local username/password login is now disabled."
|
||||
(cd "$DIR" && docker compose up -d) \
|
||||
&& log_success "$_success_msg" \
|
||||
&& log_success "\"Sign in with Authelia\" added to Mealie — local login still works too." \
|
||||
|| log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"
|
||||
|
||||
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "mealie" "${BASE_URL#*://}"
|
||||
|
||||
echo ""
|
||||
log_info "Test the \"Login with Authelia\" button on Mealie's own login page before"
|
||||
log_info "disabling local login — re-run 'sudo ./setup.sh mealie' (choose update,"
|
||||
log_info "then \"Reconfigure? n\") once you've confirmed it works, and you'll be"
|
||||
log_info "offered that as a separate step."
|
||||
}
|
||||
|
||||
# Split out from _mealie_offer_authelia_oidc so disabling local login is
|
||||
# never offered in the same breath as first setting SSO up — confirmed
|
||||
# live (on Beszel, same risk class) that saying yes before actually testing
|
||||
# the Authelia button leaves both login paths broken at once. Only reached
|
||||
# from a later "update" rerun once OIDC is already configured and the admin
|
||||
# declines to reconfigure — i.e. after they've had a real chance to test it.
|
||||
_mealie_offer_disable_password_login() {
|
||||
local DIR="$1"
|
||||
grep -q '^ALLOW_PASSWORD_LOGIN=false' "$DIR/.env" 2>/dev/null && return 0
|
||||
|
||||
echo ""
|
||||
local _tested=""
|
||||
prompt_yn " Have you ALREADY logged into Mealie successfully using the Authelia button (not just enabled it)? (y/n):" "n" _tested
|
||||
if [[ ! "$_tested" =~ ^[Yy]$ ]]; then
|
||||
log_info "Skipped. Test the Authelia login button first, then re-run 'sudo ./setup.sh mealie' (choose update) to come back to this."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local _disable_local=""
|
||||
prompt_yn " Also disable Mealie's own username/password login, so Authelia is the only way in? (y/n):" "n" _disable_local
|
||||
[[ "$_disable_local" =~ ^[Yy]$ ]] || return 0
|
||||
|
||||
log_warning "Anyone without an Authelia account (only a local Mealie one) will no longer be able to log in."
|
||||
log_info "Reversible any time: set ALLOW_PASSWORD_LOGIN back to true in $DIR/.env and 'docker compose up -d'."
|
||||
local _auto_redirect=""
|
||||
prompt_yn " Skip Mealie's login page entirely and jump straight to Authelia? (y/n):" "y" _auto_redirect
|
||||
|
||||
sed -i '/^ALLOW_PASSWORD_LOGIN=/d; /^OIDC_AUTO_REDIRECT=/d; /^OIDC_REMEMBER_ME=/d' "$DIR/.env"
|
||||
{
|
||||
echo "ALLOW_PASSWORD_LOGIN=false"
|
||||
if [[ "$_auto_redirect" =~ ^[Yy]$ ]]; then
|
||||
echo "OIDC_AUTO_REDIRECT=true"
|
||||
echo "OIDC_REMEMBER_ME=true"
|
||||
fi
|
||||
} >> "$DIR/.env"
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$DIR/.env" 2>/dev/null || true
|
||||
|
||||
(cd "$DIR" && docker compose up -d) \
|
||||
&& log_success "Local username/password login is now disabled — Authelia is the only way in." \
|
||||
|| log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"
|
||||
}
|
||||
|
||||
install_mealie() {
|
||||
|
||||
Reference in New Issue
Block a user