Add zero-click Authelia login to Gitea and Uptime Kuma
Follow-up to Frigate's Authelia integration: both of these can also skip their own login entirely once Authelia is doing the gating, each with a different trust model appropriate to what the app actually supports. - gitea: new _gitea_offer_reverse_proxy_auth(), a second Authelia integration alongside the existing OIDC "Sign in with Authelia" button. Enables Gitea's own ENABLE_REVERSE_PROXY_AUTHENTICATION so it auto-logs in from a trusted Remote-User header — no click, no separate Gitea session to expire on its own. Trust is IP-range based (REVERSE_PROXY_TRUSTED_PROXIES), computed from caddy_net's real subnet the same way ufw_allow_from_caddy_net does; refuses to enable the feature at all if that can't be determined rather than fall back to a permissive default — Gitea's own Docker image has shipped an unscoped default before (GHSA-f75j-4cw6-rmx4, any IP could impersonate any user). Rewires Gitea onto caddy_net and re-points Caddy at gitea:3000, since it previously only reached Caddy via its published host port. Gitea's own login stays available as a fallback, so unlike Frigate there's no "native login off with nothing gating it" state to guard against. - uptimekuma: sets DISABLE_AUTH=true only once Caddy's "import authelia" gate is confirmed in front of it. Uptime Kuma already joined caddy_net unconditionally, so this only needed the env var plus moving the Authelia-gated Caddy call earlier (before docker-compose.yml is written); the existing unconditional call at the end now only runs as a fallback when the Authelia path wasn't used or wasn't completed. Kuma's DISABLE_AUTH has no IP-scoping or secret check left once set — the strictest of the three to get the ordering right on, since a mistake here means wide open, not just spoofable. Verified with a local test harness (fake Authelia/Caddy/docker-network state): both the happy path and the "Caddy declined" safety fallback produce the expected docker-compose.yml/.env/Caddyfile output for each service, and Gitea's subnet-detection refusal + idempotent-rerun guard were exercised directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpKTLpwAgZNooTacWeQLuc
This commit is contained in:
@@ -502,6 +502,56 @@ reinstall (from `.env` via `ENV_MAP`, the same array `_frigate_parse_existing`
|
||||
already builds) rather than rotating it and breaking the existing Caddy
|
||||
pairing.
|
||||
|
||||
**`gitea` and `uptimekuma` — two more "disable/bypass built-in login,
|
||||
Authelia is the only gate" integrations, each with its own trust model.**
|
||||
Both are opt-in extras layered on top of the has-built-in-auth entries
|
||||
those services already had; neither replaces the existing behavior for
|
||||
anyone who doesn't ask for it.
|
||||
|
||||
- `gitea`'s `_gitea_offer_reverse_proxy_auth()` is a *second*, stronger
|
||||
Authelia integration alongside the OIDC "Sign in with Authelia" button
|
||||
(`_gitea_offer_authelia_sso()`, unchanged): Gitea's own
|
||||
`ENABLE_REVERSE_PROXY_AUTHENTICATION` mode auto-logs in as whatever
|
||||
username arrives in a trusted header — no click, no separate Gitea
|
||||
session with its own expiry. Unlike Frigate, Gitea's own login page
|
||||
isn't disabled — it stays as a fallback for anyone not arriving through
|
||||
the trusted path, so there's no "native login off with nothing gating
|
||||
it" failure mode to guard against here. The trust boundary is
|
||||
`REVERSE_PROXY_TRUSTED_PROXIES` (an IP range), not a shared secret —
|
||||
Gitea's own Docker image has shipped this wildcarded before (a real CVE,
|
||||
GHSA-f75j-4cw6-rmx4: any source IP could set `X-WEBAUTH-USER` and log in
|
||||
as anyone), so this always computes the range from caddy_net's actual
|
||||
subnet (`docker network inspect ... --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'`,
|
||||
the same lookup `ufw_allow_from_caddy_net` uses) and refuses to enable
|
||||
the feature at all if that can't be determined — never falls back to a
|
||||
permissive default. `REVERSE_PROXY_AUTHENTICATION_USER`/`_EMAIL` are set
|
||||
to `Remote-User`/`Remote-Email` to match Authelia's `import authelia`
|
||||
snippet's own `copy_headers` output directly, rather than renaming
|
||||
headers in Caddy to match Gitea's own `X-WEBAUTH-USER` default. Gitea
|
||||
currently reaches Caddy over its published host port
|
||||
(`host.docker.internal:PORT`), not caddy_net, because it predates this
|
||||
feature — enabling it rewires Gitea onto caddy_net (like every other
|
||||
locally-Caddy-fronted service) and re-points Caddy's upstream at
|
||||
`gitea:3000`, replacing the old site block via
|
||||
`configure_caddy_for_service`'s own existing "already exists —
|
||||
overwrite?" prompt. Local Caddy only; a remote Caddy machine's source
|
||||
address isn't a stable, narrowly-scopeable range the way caddy_net's
|
||||
bridge subnet is.
|
||||
- `uptimekuma`'s equivalent is much simpler: Uptime Kuma's `DISABLE_AUTH=true`
|
||||
env var turns its own login off *completely*, with no IP-range or secret
|
||||
check left at all — once set, anything that can reach its port is in, no
|
||||
questions asked. That makes it the one of these three where getting the
|
||||
ordering wrong is worst: `services/uptimekuma.sh` only ever sets
|
||||
`DISABLE_AUTH=true` after `configure_caddy_for_service "Uptime Kuma" "uptime-kuma:3001" "uptime" " import authelia"`
|
||||
confirms `CADDY_SERVICE_CONFIGURED` — the same never-disable-native-auth-
|
||||
without-a-confirmed-gate rule Frigate follows. Uptime Kuma already joined
|
||||
caddy_net unconditionally before this (see its own `_CADDY_NET_BLOCK`),
|
||||
so no networking change was needed here, just the env var and the
|
||||
Authelia-gated Caddy call happening earlier (before `docker-compose.yml`
|
||||
is written) instead of the plain unconditional call this file already
|
||||
had at the end — which now only runs as a fallback when the Authelia
|
||||
path wasn't used or wasn't completed.
|
||||
|
||||
For services without built-in auth, prompt the user before calling
|
||||
`configure_caddy_for_service` and pass `import authelia` as the extra block
|
||||
if Authelia is installed and the user wants SSO protection:
|
||||
|
||||
@@ -240,6 +240,92 @@ _gitea_offer_authelia_sso() {
|
||||
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "gitea" "$GITEA_OIDC_DOMAIN"
|
||||
}
|
||||
|
||||
# Offers Gitea's OTHER Authelia integration — not the OIDC button above, but
|
||||
# ENABLE_REVERSE_PROXY_AUTHENTICATION: Gitea auto-logs in as whatever user
|
||||
# name arrives in a trusted header, no click and no separate Gitea session
|
||||
# to expire on its own schedule. This is genuinely stronger than the OIDC
|
||||
# button (which still shows a login page, just with an extra option on it)
|
||||
# and matches the pattern services/frigate.sh uses — except Gitea's own
|
||||
# login form stays available as a fallback for anyone NOT arriving from a
|
||||
# trusted source, so there's no "native login disabled with nothing gating
|
||||
# it" failure mode to guard against here the way Frigate's had.
|
||||
#
|
||||
# The security boundary is REVERSE_PROXY_TRUSTED_PROXIES, not a shared
|
||||
# secret: Gitea only honors the identity header from source IPs inside that
|
||||
# range. Gitea's own Docker image shipped this wildcarded (GHSA-f75j-4cw6-
|
||||
# rmx4 — any IP could set X-WEBAUTH-USER and log in as anyone), so this is
|
||||
# always computed from caddy_net's real subnet (same lookup
|
||||
# ufw_allow_from_caddy_net uses) and refuses to enable the feature at all if
|
||||
# that can't be determined — never falls back to a permissive default.
|
||||
#
|
||||
# Requires Gitea to actually be reachable from an address inside that range,
|
||||
# which means joining caddy_net like every other locally-Caddy-fronted
|
||||
# service in this repo (Gitea currently reaches Caddy via its published
|
||||
# host port instead — host.docker.internal upstream — because it predates
|
||||
# this feature). Local Caddy only: a remote Caddy machine's source address
|
||||
# isn't a stable, narrowly-scopeable range the way caddy_net's bridge subnet
|
||||
# is, so this skips remote mode rather than guess at a trust range worth
|
||||
# getting wrong.
|
||||
_gitea_offer_reverse_proxy_auth() {
|
||||
local DIR="$1"
|
||||
|
||||
[ -d "$DOCKER_DIR/authelia" ] || return 0
|
||||
[ -d "$DOCKER_DIR/caddy" ] || return 0
|
||||
|
||||
if grep -q 'ENABLE_REVERSE_PROXY_AUTHENTICATION=true' "$DIR/docker-compose.yml" 2>/dev/null; then
|
||||
log_info "Gitea's zero-click Authelia login (reverse-proxy auth) is already enabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
local USE_RP=""
|
||||
prompt_yn " Skip Gitea's own login entirely for anyone arriving via Authelia — fully transparent, no click, no separate Gitea session to re-expire? Rewires Gitea onto Caddy's internal network (Caddy must be on this same machine). (y/n):" "n" USE_RP
|
||||
[[ "$USE_RP" =~ ^[Yy]$ ]] || return 0
|
||||
|
||||
local _subnet
|
||||
_subnet="$(docker network inspect "${SITE_CADDY_NET:-caddy_net}" \
|
||||
--format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null)"
|
||||
if [ -z "$_subnet" ]; then
|
||||
log_warning "Couldn't determine ${SITE_CADDY_NET:-caddy_net}'s subnet — refusing to enable"
|
||||
log_warning "reverse-proxy auth without a scoped trust range. An unscoped default lets ANY"
|
||||
log_warning "client impersonate ANY Gitea user via a spoofed header (this was a real Gitea"
|
||||
log_warning "CVE — GHSA-f75j-4cw6-rmx4). Skipping."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Wiring Gitea onto caddy_net and enabling reverse-proxy authentication..."
|
||||
sed -i "/GITEA__security__INSTALL_LOCK=true/a\\ - GITEA__service__ENABLE_REVERSE_PROXY_AUTHENTICATION=true\\n - GITEA__service__ENABLE_REVERSE_PROXY_AUTO_REGISTRATION=true\\n - GITEA__service__ENABLE_REVERSE_PROXY_EMAIL=true\\n - GITEA__security__REVERSE_PROXY_AUTHENTICATION_USER=Remote-User\\n - GITEA__security__REVERSE_PROXY_AUTHENTICATION_EMAIL=Remote-Email\\n - GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES=${_subnet}" \
|
||||
"$DIR/docker-compose.yml"
|
||||
cat >> "$DIR/docker-compose.yml" << EOF
|
||||
networks:
|
||||
- caddy_net
|
||||
|
||||
networks:
|
||||
caddy_net:
|
||||
external: true
|
||||
name: ${SITE_CADDY_NET:-caddy_net}
|
||||
EOF
|
||||
|
||||
_gitea_fix_ownership "$DIR"
|
||||
(cd "$DIR" && docker compose up -d) \
|
||||
&& log_success "Gitea restarted on caddy_net (trusted range: ${_subnet})." \
|
||||
|| { log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"; return 1; }
|
||||
|
||||
# Re-point Caddy at the container (gitea:3000, now reachable over
|
||||
# caddy_net) instead of the host-published port, with the auth gate in
|
||||
# front. This replaces the plain block set up earlier in this install —
|
||||
# configure_caddy_for_service's own "already exists — overwrite?" prompt
|
||||
# covers that; nothing here bypasses it.
|
||||
configure_caddy_for_service "Gitea" "gitea:3000" "git" " import authelia"
|
||||
if [ "${CADDY_SERVICE_CONFIGURED:-false}" = true ]; then
|
||||
log_success "Gitea now signs in transparently via Authelia at https://${CADDY_SERVICE_DOMAIN} — its own login page is still there for anyone reaching it another way."
|
||||
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "gitea" "$CADDY_SERVICE_DOMAIN"
|
||||
else
|
||||
log_warning "Caddy wasn't reconfigured — env vars are set, but nothing is routing Gitea through Authelia yet."
|
||||
log_warning "Point Gitea's Caddy entry at gitea:3000 (not the old host.docker.internal upstream) with 'import authelia' in front, or just re-run this offer."
|
||||
fi
|
||||
}
|
||||
|
||||
# Offers to enable Gitea Actions (Gitea's own CI, largely GitHub-Actions-
|
||||
# workflow-compatible) with a local runner — mainly useful as a fallback so
|
||||
# .gitea/workflows/*.yml can still run something like a GitHub Actions build
|
||||
@@ -445,6 +531,8 @@ install_gitea() {
|
||||
echo "[DRY-RUN] to install a systemd timer for automatic sync, or print manual instructions"
|
||||
echo "[DRY-RUN] Would offer to run a sync now (dry-run preview or for real), off-schedule"
|
||||
echo "[DRY-RUN] Would offer \"Sign in with Authelia\" (OIDC) if Authelia is installed"
|
||||
echo "[DRY-RUN] Would offer zero-click Authelia login (reverse-proxy auth) if Authelia"
|
||||
echo "[DRY-RUN] and local Caddy are both installed — rewires Gitea onto caddy_net"
|
||||
echo "[DRY-RUN] Would offer to enable Gitea Actions (CI) with a local act_runner container"
|
||||
echo "[DRY-RUN] Would write $DIR/README.md"
|
||||
return 0
|
||||
@@ -473,6 +561,7 @@ install_gitea() {
|
||||
|| log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"
|
||||
_gitea_run_sync_direction_step "$DIR"
|
||||
_gitea_offer_authelia_sso "$DIR"
|
||||
_gitea_offer_reverse_proxy_auth "$DIR"
|
||||
_gitea_offer_actions_runner "$DIR"
|
||||
log_success "Existing .env (tokens) and web/SSH ports were left untouched."
|
||||
return 0
|
||||
@@ -643,6 +732,7 @@ ENV
|
||||
configure_caddy_for_service "Gitea" "host.docker.internal:${WEB_PORT}" "git"
|
||||
|
||||
_gitea_offer_authelia_sso "$DIR"
|
||||
_gitea_offer_reverse_proxy_auth "$DIR"
|
||||
_gitea_offer_actions_runner "$DIR"
|
||||
|
||||
write_readme "$DIR" << MD
|
||||
@@ -685,6 +775,21 @@ on Gitea's own login page. Local admin login keeps working exactly as
|
||||
before — this is additive, not a replacement. Managed in Gitea under
|
||||
Site Administration -> Authentication Sources (source name: \`authelia\`).
|
||||
|
||||
## Zero-click Authelia login (optional, stronger)
|
||||
|
||||
A second, separate Authelia integration: instead of an extra button on
|
||||
Gitea's login page, Gitea auto-logs in as whoever Authelia says you are —
|
||||
no click, and no separate Gitea session that can expire on its own and
|
||||
force a re-login later. Re-run \`sudo ./setup.sh gitea\` (Update mode) and
|
||||
answer yes to the "Skip Gitea's own login entirely..." prompt. Requires
|
||||
Authelia and Caddy on this same machine — it moves Gitea onto Caddy's
|
||||
internal Docker network (\`caddy_net\`) and Gitea only trusts the identity
|
||||
header from that network's address range, not from the internet or from
|
||||
its own host-published port. Gitea's own login page keeps working for
|
||||
anyone who reaches it any other way (e.g. directly on its port). New
|
||||
users arriving this way get an ordinary (non-admin) Gitea account created
|
||||
automatically the first time they show up.
|
||||
|
||||
## Gitea Actions (CI) — optional local runner
|
||||
|
||||
Re-run \`sudo ./setup.sh gitea\` (Update mode is fine) and answer yes to
|
||||
|
||||
+51
-3
@@ -206,6 +206,9 @@ install_uptimekuma() {
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Would create $UPTIME_DIR"
|
||||
echo "[DRY-RUN] Would auto-scan for a free host port"
|
||||
echo "[DRY-RUN] If Authelia is installed: would offer to protect Uptime Kuma with it —"
|
||||
echo "[DRY-RUN] sets DISABLE_AUTH=true (Kuma's own login off) only once Caddy's"
|
||||
echo "[DRY-RUN] 'import authelia' gate is actually confirmed in front of it"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -241,6 +244,39 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
# Authelia SSO — decided (and, if accepted, wired into Caddy) before
|
||||
# docker-compose.yml is written, so DISABLE_AUTH only ever gets set once
|
||||
# Caddy's "import authelia" gate is actually confirmed in front of Kuma.
|
||||
# Unlike Frigate/Gitea, Uptime Kuma with DISABLE_AUTH=true has NO
|
||||
# internal check left at all — it's not IP-scoped (Gitea) or secret-
|
||||
# pinned (Frigate), just fully open to whatever reaches its port, so
|
||||
# this is the one place getting the ordering wrong is worst: a login-
|
||||
# disabled Kuma with nothing gating it is wide open to anyone who can
|
||||
# reach the port, not just spoofable.
|
||||
local UPTIME_USE_AUTHELIA="n" UPTIME_ENV_BLOCK="" _uptime_caddy_done=false
|
||||
if [ -d "$DOCKER_DIR/authelia" ]; then
|
||||
echo ""
|
||||
prompt_yn "Protect Uptime Kuma with Authelia SSO (disables Kuma's own login entirely)? (y/n):" "y" UPTIME_USE_AUTHELIA
|
||||
fi
|
||||
|
||||
if [[ "$UPTIME_USE_AUTHELIA" =~ ^[Yy]$ ]]; then
|
||||
configure_caddy_for_service "Uptime Kuma" "uptime-kuma:3001" "uptime" " import authelia"
|
||||
if [ "${CADDY_SERVICE_CONFIGURED:-false}" = true ]; then
|
||||
UPTIME_ENV_BLOCK=" - DISABLE_AUTH=true"
|
||||
_uptime_caddy_done=true
|
||||
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "uptimekuma" "$CADDY_SERVICE_DOMAIN"
|
||||
else
|
||||
log_warning "Caddy wasn't configured — leaving Uptime Kuma's own login enabled (nothing else would be gating access)."
|
||||
fi
|
||||
fi
|
||||
|
||||
local UPTIME_ENV_SECTION=""
|
||||
if [ -n "$UPTIME_ENV_BLOCK" ]; then
|
||||
UPTIME_ENV_SECTION=" environment:
|
||||
${UPTIME_ENV_BLOCK}
|
||||
"
|
||||
fi
|
||||
|
||||
cat > docker-compose.yml << UPTIME_COMPOSE
|
||||
name: uptime-kuma
|
||||
|
||||
@@ -250,7 +286,7 @@ services:
|
||||
container_name: uptime-kuma
|
||||
hostname: uptime-kuma
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
${UPTIME_ENV_SECTION} volumes:
|
||||
- ./data:/app/data
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
@@ -282,6 +318,15 @@ Docker containers.
|
||||
If Caddy is installed, you can expose this via the prompt during install
|
||||
(see configure_caddy_for_service). Default subdomain: uptime.
|
||||
|
||||
## Authelia SSO (optional)
|
||||
If Authelia is installed, the installer offers to protect Uptime Kuma with
|
||||
it instead of Kuma's own login — this sets \`DISABLE_AUTH=true\` (Kuma's own
|
||||
account/login screen goes away entirely) and puts Caddy's \`import authelia\`
|
||||
gate in front instead, so Authelia is the only thing checking who you are.
|
||||
This only gets set once Caddy confirms it's actually fronting the domain —
|
||||
never with nothing else gating access. Re-run \`sudo ./setup.sh uptimekuma\`
|
||||
to add or change this later.
|
||||
|
||||
## Manage
|
||||
\`\`\`
|
||||
cd $UPTIME_DIR
|
||||
@@ -291,8 +336,11 @@ docker compose logs -f # logs
|
||||
\`\`\`
|
||||
MD
|
||||
|
||||
# Configure Caddy reverse proxy before starting
|
||||
configure_caddy_for_service "Uptime Kuma" "uptime-kuma:3001" "uptime"
|
||||
# Configure Caddy reverse proxy before starting (skip if the Authelia
|
||||
# step above already did it)
|
||||
if [ "$_uptime_caddy_done" != true ]; then
|
||||
configure_caddy_for_service "Uptime Kuma" "uptime-kuma:3001" "uptime"
|
||||
fi
|
||||
|
||||
local START_UPTIME=""
|
||||
prompt_yn "Start Uptime Kuma now? (y/n):" "y" START_UPTIME
|
||||
|
||||
Reference in New Issue
Block a user