Merge pull request #358 from outis1one/claude/gitea-standalone-setup-oxoi2e

Claude/gitea standalone setup oxoi2e
This commit is contained in:
Outis
2026-08-17 18:21:02 -04:00
committed by GitHub
3 changed files with 306 additions and 61 deletions
+20 -1
View File
@@ -369,7 +369,26 @@ collision.)
**Has built-in auth — no Authelia needed:**
`emby`, `jellyfin`, `audiobookshelf`, `immich`, `mealie`, `actualbudget`,
`homeassistant`, `portainer`, `meshcentral`, `traccar`, `uptimekuma`,
`filebrowser`, `wg-easy`, `ntfy` (configurable)
`filebrowser`, `wg-easy`, `ntfy` (configurable), `gitea`
**Native OIDC login as an addition, not a Caddy gate — `gitea`'s pattern.**
Some apps with their own built-in login *also* have their own "add an
OAuth2/OIDC provider" setting — a genuinely different integration from
both the forward_auth gate above and the "Enable OpenID" client-registration
flow below. `services/gitea.sh`'s `_gitea_offer_authelia_sso()` is the
reference: if Authelia is installed, offers to register Gitea as an OIDC
client (via `services/authelia.sh`'s `_authelia_provision_oidc_client()`
the same non-interactive, out-param-returning core that
`_authelia_add_oidc_client()`'s menu flow uses) and then runs `gitea admin
auth add-oauth` itself to add Authelia as an authentication source — no
manual web-UI copy-paste on either side, matching this repo's "no manual
wizard" philosophy elsewhere in gitea.sh (admin account/token creation).
Local login keeps working unchanged; this only adds an extra button on the
existing login page. Reuse `_authelia_provision_oidc_client()` (guarded by
`declare -F`, same convention as chaining into another service's
`install_<name>()`) for any future service with its own native OIDC field,
instead of duplicating Authelia's client-secret-generation/config-patching
logic again.
**No built-in auth — should be protected:**
`magicmirror`, `wolf-pair`, `js99er`, `drum-rhythm-game`, `iopaint`,
+120 -57
View File
@@ -1190,12 +1190,32 @@ _authelia_ensure_oidc_provider() {
log_success "OIDC provider enabled (signing key + HMAC secret generated)"
}
# Registers an OIDC client for another app to log in via Authelia — the
# "Other" provider option in an app's own "Enable OpenID"/SSO dialog. Presets
# below hand back the app's own known redirect URI path and the exact fields
# to paste where; "Other/custom" covers anything not listed (the app's own
# OIDC/SSO docs will say what redirect URI it expects).
_authelia_add_oidc_client() {
# Non-interactive core of _authelia_add_oidc_client() below — generates a
# client secret, patches it into identity_providers.oidc.clients, and
# (optionally) restarts Authelia. Fully self-contained (re-validates
# everything itself rather than trusting a caller's state) so other
# services can call it directly to register themselves as an OIDC client
# without walking a human through this file's own menu — see
# services/gitea.sh's "Sign in with Authelia" step for the reference caller.
# Guard every cross-file call with `declare -F` per this repo's chaining
# convention (services/gitea.sh does).
#
# Args: APP_NAME CLIENT_ID AUTH_POLICY RESTART_AUTH(y/n) <redirect_uri> [<redirect_uri> ...]
# Out-params (not `local` — read them after the call returns):
# OIDC_CLIENT_SECRET_PLAIN the plaintext secret. Shown once — Authelia's
# config only ever stores the hash — so the
# caller must capture and use/display it now.
# OIDC_AUTHELIA_DOMAIN this Authelia instance's apex domain, for
# building discovery/authorization/token URLs.
# Returns 1 on failure (Authelia not installed, client ID already taken,
# secret generation failed) with the reason already logged.
_authelia_provision_oidc_client() {
local APP_NAME="$1" CLIENT_ID="$2" AUTH_POLICY="$3" RESTART_AUTH="$4"; shift 4
local -a REDIRECT_URIS=("$@")
OIDC_CLIENT_SECRET_PLAIN=""
OIDC_AUTHELIA_DOMAIN=""
local AUTHELIA_DIR="$DOCKER_DIR/authelia"
local CONFIG_FILE="$AUTHELIA_DIR/config/configuration.yml"
@@ -1206,12 +1226,101 @@ _authelia_add_oidc_client() {
_authelia_ensure_oidc_provider "$AUTHELIA_DIR" || return 1
# The apex domain this Authelia instance already serves — read back from
# its own session.cookies (same structure install_authelia()/
# add_authelia_domain() write), rather than asking again or assuming a
# variable set earlier in this run is still in scope (this flow can be
# reached standalone from the "already exists" menu, or from another
# service entirely, with none of install_authelia()'s own locals ever
# having run this session).
OIDC_AUTHELIA_DOMAIN="$(awk '/^ cookies:$/{f=1; next} f && /domain:/{print $3; exit}' "$CONFIG_FILE")"
if [ -z "$OIDC_AUTHELIA_DOMAIN" ]; then
log_warning "Couldn't determine this Authelia instance's domain from $CONFIG_FILE — aborting."
return 1
fi
if grep -qF "client_id: '${CLIENT_ID}'" "$CONFIG_FILE" 2>/dev/null; then
log_warning "A client with ID '$CLIENT_ID' is already registered in $CONFIG_FILE."
return 1
fi
log_info "Generating client secret..."
local _hash_out CLIENT_SECRET_HASH
_hash_out="$(docker run --rm authelia/authelia:4.39.20 \
authelia crypto hash generate pbkdf2 --variant sha512 --random \
--random.length 72 --random.charset rfc3986 2>/dev/null)"
OIDC_CLIENT_SECRET_PLAIN="$(echo "$_hash_out" | sed -n 's/^Random Password: //p')"
CLIENT_SECRET_HASH="$(echo "$_hash_out" | sed -n 's/^Digest: //p')"
if [ -z "$OIDC_CLIENT_SECRET_PLAIN" ] || [ -z "$CLIENT_SECRET_HASH" ]; then
log_warning "Couldn't generate the client secret automatically. Run manually, then add the"
log_warning "client to $CONFIG_FILE's identity_providers.oidc.clients by hand:"
echo " docker run --rm authelia/authelia:4.39.20 authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986"
OIDC_CLIENT_SECRET_PLAIN=""
return 1
fi
grep -q '^ clients: \[\]$' "$CONFIG_FILE" && sed -i 's/^ clients: \[\]$/ clients:/' "$CONFIG_FILE"
local REDIRECT_URIS_YAML
REDIRECT_URIS_YAML="$(printf " - '%s'\n" "${REDIRECT_URIS[@]}")"
REDIRECT_URIS_YAML="${REDIRECT_URIS_YAML%$'\n'}"
local CLIENT_BLOCK=" - client_id: '${CLIENT_ID}'
client_name: '${APP_NAME}'
client_secret: '${CLIENT_SECRET_HASH}'
public: false
authorization_policy: '${AUTH_POLICY}'
redirect_uris:
${REDIRECT_URIS_YAML}
scopes:
- 'openid'
- 'profile'
- 'email'
grant_types:
- 'authorization_code'
response_types:
- 'code'
response_modes:
- 'query'
userinfo_signed_response_alg: 'none'"
awk -v block="$CLIENT_BLOCK" '
{ print }
/^ clients:$/ && !done { print block; done=1 }
' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
chown 1000:1000 "$CONFIG_FILE" 2>/dev/null || true
if [[ "$RESTART_AUTH" =~ ^[Yy]$ ]]; then
(cd "$AUTHELIA_DIR" && docker compose restart authelia 2>/dev/null) \
&& log_success "Authelia restarted" \
|| log_warning "Authelia restart failed — check: docker compose logs authelia"
fi
return 0
}
# Registers an OIDC client for another app to log in via Authelia — the
# "Other" provider option in an app's own "Enable OpenID"/SSO dialog. Presets
# below hand back the app's own known redirect URI path and the exact fields
# to paste where; "Other/custom" covers anything not listed (the app's own
# OIDC/SSO docs will say what redirect URI it expects). Interactive wrapper
# around _authelia_provision_oidc_client() above, which does the actual work.
_authelia_add_oidc_client() {
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
# The apex domain this Authelia instance already serves — read back from
# its own session.cookies (same structure install_authelia()/
# add_authelia_domain() write), rather than asking again or assuming a
# variable set earlier in this run is still in scope (this flow can be
# reached standalone from the "already exists" menu with none of
# install_authelia()'s own locals ever having run this session).
# install_authelia()'s own locals ever having run this session). Used
# below only to suggest a domain default — _authelia_provision_oidc_client
# re-derives its own copy independently.
local AUTHELIA_DOMAIN
AUTHELIA_DOMAIN="$(awk '/^ cookies:$/{f=1; next} f && /domain:/{print $3; exit}' "$CONFIG_FILE")"
if [ -z "$AUTHELIA_DOMAIN" ]; then
@@ -1288,58 +1397,12 @@ _authelia_add_oidc_client() {
prompt_yn " Require two-factor for ${APP_NAME} logins too? (y/n):" "y" _2fa
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
log_info "Generating client secret..."
local _hash_out CLIENT_SECRET_PLAIN CLIENT_SECRET_HASH
_hash_out="$(docker run --rm authelia/authelia:4.39.20 \
authelia crypto hash generate pbkdf2 --variant sha512 --random \
--random.length 72 --random.charset rfc3986 2>/dev/null)"
CLIENT_SECRET_PLAIN="$(echo "$_hash_out" | sed -n 's/^Random Password: //p')"
CLIENT_SECRET_HASH="$(echo "$_hash_out" | sed -n 's/^Digest: //p')"
if [ -z "$CLIENT_SECRET_PLAIN" ] || [ -z "$CLIENT_SECRET_HASH" ]; then
log_warning "Couldn't generate the client secret automatically. Run manually, then add the"
log_warning "client to $CONFIG_FILE's identity_providers.oidc.clients by hand:"
echo " docker run --rm authelia/authelia:4.39.20 authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986"
return 1
fi
grep -q '^ clients: \[\]$' "$CONFIG_FILE" && sed -i 's/^ clients: \[\]$/ clients:/' "$CONFIG_FILE"
local REDIRECT_URIS_YAML
REDIRECT_URIS_YAML="$(printf " - '%s'\n" "${REDIRECT_URIS[@]}")"
REDIRECT_URIS_YAML="${REDIRECT_URIS_YAML%$'\n'}"
local CLIENT_BLOCK=" - client_id: '${CLIENT_ID}'
client_name: '${APP_NAME}'
client_secret: '${CLIENT_SECRET_HASH}'
public: false
authorization_policy: '${AUTH_POLICY}'
redirect_uris:
${REDIRECT_URIS_YAML}
scopes:
- 'openid'
- 'profile'
- 'email'
grant_types:
- 'authorization_code'
response_types:
- 'code'
response_modes:
- 'query'
userinfo_signed_response_alg: 'none'"
awk -v block="$CLIENT_BLOCK" '
{ print }
/^ clients:$/ && !done { print block; done=1 }
' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
chown 1000:1000 "$CONFIG_FILE" 2>/dev/null || true
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
_authelia_provision_oidc_client "$APP_NAME" "$CLIENT_ID" "$AUTH_POLICY" "$RESTART_AUTH" "${REDIRECT_URIS[@]}" \
|| return 1
local CLIENT_SECRET_PLAIN="$OIDC_CLIENT_SECRET_PLAIN"
echo ""
echo " ${APP_NAME} is registered. Paste these into its OpenID/SSO settings"
+166 -3
View File
@@ -159,7 +159,8 @@ _gitea_prompt_token() {
# SQLite DB then fails with "attempt to write a readonly database" — the
# directory holding the DB file is owned by root, not the UID 1000 process
# trying to write it. Confirmed live. Chown everything else as normal in
# $DIR; leave data/ for the container to manage.
# $DIR; leave data/ (and the Actions runner's own runner-data/, same reason)
# for their respective containers to manage.
_gitea_fix_ownership() {
local _dir="$1"
[ "$DRY_RUN" = true ] && return 0
@@ -167,11 +168,141 @@ _gitea_fix_ownership() {
local _entry
for _entry in "$_dir"/*; do
[ -e "$_entry" ] || continue
[ "$(basename "$_entry")" = "data" ] && continue
case "$(basename "$_entry")" in
data|runner-data) continue ;;
esac
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$_entry" 2>/dev/null || true
done
}
# Offers to add "Sign in with Authelia" (OpenID Connect) to Gitea's own
# login page. This is a different thing from Caddy forward_auth, which this
# service deliberately skips (see the Caddy call below) since Gitea already
# has a solid built-in login — this adds Authelia as an *additional* OAuth2
# login option on the same page, on top of that built-in login, not a
# replacement for it. Nothing about local admin login changes.
#
# Fully automated on both sides, matching how the rest of this installer
# avoids manual web wizards: registers Gitea as an OIDC client in Authelia
# (services/authelia.sh's _authelia_provision_oidc_client), then adds the
# resulting client as an authentication source in Gitea via its own CLI. If
# either side isn't available (Authelia not installed, or the Gitea CLI
# call fails for some reason — e.g. an older image without `admin auth
# add-oauth`), falls back to printing the values for a two-minute manual
# add in Gitea's Site Administration UI instead of losing the setup.
_gitea_offer_authelia_sso() {
local DIR="$1"
[ -d "$DOCKER_DIR/authelia" ] || return 0
declare -F _authelia_provision_oidc_client >/dev/null 2>&1 || return 0
echo ""
local USE_SSO=""
prompt_yn " Add \"Sign in with Authelia\" (OpenID Connect) to Gitea's login page? (y/n):" "n" USE_SSO
[[ "$USE_SSO" =~ ^[Yy]$ ]] || return 0
local _default_domain=""
[ -n "${SITE_DOMAIN:-}" ] && [ "$SITE_DOMAIN" != "example.com" ] && _default_domain="git.${SITE_DOMAIN}"
local GITEA_OIDC_DOMAIN=""
prompt_text " Domain Gitea is reachable at [${_default_domain:-required}]:" "$_default_domain" GITEA_OIDC_DOMAIN
if [ -z "$GITEA_OIDC_DOMAIN" ]; then
log_warning "No domain entered — skipping Authelia SSO for Gitea."
return 0
fi
local _2fa="" AUTH_POLICY="two_factor"
prompt_yn " Require two-factor for Gitea logins via Authelia too? (y/n):" "y" _2fa
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
if ! _authelia_provision_oidc_client "Gitea" "gitea" "$AUTH_POLICY" "y" \
"https://${GITEA_OIDC_DOMAIN}/user/oauth2/authelia/callback"; then
log_warning "Couldn't register Gitea as an OIDC client in Authelia — skipping SSO setup."
return 0
fi
local _discovery_url="https://auth.${OIDC_AUTHELIA_DOMAIN}/.well-known/openid-configuration"
log_info "Adding Authelia as an authentication source in Gitea..."
if docker exec -u git gitea gitea admin auth add-oauth \
--name authelia --provider openidConnect \
--key gitea --secret "$OIDC_CLIENT_SECRET_PLAIN" \
--auto-discover-url "$_discovery_url" &>/dev/null; then
log_success "\"Sign in with Authelia\" added to Gitea's login page — local admin login still works too."
else
log_warning "Couldn't add the auth source automatically. Add it by hand:"
log_warning " Gitea -> Site Administration -> Authentication Sources -> Add Authentication Source"
log_warning " Type: OAuth2, Provider: OpenID Connect, Name: authelia"
log_warning " Client ID: gitea"
log_warning " Client Secret: $OIDC_CLIENT_SECRET_PLAIN"
log_warning " Discovery URL: $_discovery_url"
log_warning " (The Client Secret above is shown once — it isn't stored in plaintext anywhere.)"
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
# if GitHub itself is ever unreachable, since this Gitea is otherwise just a
# passive pull mirror. Off by default; opt-in on fresh installs and Update
# reruns alike (idempotent — a rerun after it's already set up just no-ops).
#
# The runner (gitea/act_runner) polls Gitea for jobs and needs the host's
# Docker socket to launch a fresh container per job — same pattern this repo
# already uses for portainer/watchtower/uptimekuma/beszel/traccar's autoheal,
# not something new to this file. Worth knowing: that's root-equivalent
# access to this host, standard for any CI runner, not unique to Gitea's.
_gitea_offer_actions_runner() {
local DIR="$1"
grep -q '^ act_runner:$' "$DIR/docker-compose.yml" 2>/dev/null && return 0
echo ""
local USE_ACTIONS=""
prompt_yn " Enable Gitea Actions (CI) with a local runner — runs .gitea/workflows/*.yml the same way GitHub Actions runs .github/workflows/*.yml, useful as a fallback if GitHub is ever unreachable? (y/n):" "n" USE_ACTIONS
[[ "$USE_ACTIONS" =~ ^[Yy]$ ]] || return 0
if ! grep -q 'GITEA__actions__ENABLED' "$DIR/docker-compose.yml"; then
log_info "Enabling Gitea Actions..."
sed -i '/GITEA__security__INSTALL_LOCK=true/a\ - GITEA__actions__ENABLED=true\n - GITEA__actions__DEFAULT_ACTIONS_URL=github' "$DIR/docker-compose.yml"
(cd "$DIR" && docker compose up -d) \
&& log_success "Actions enabled — Gitea restarted to apply." \
|| { log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"; return 1; }
fi
log_info "Generating a runner registration token..."
local RUNNER_TOKEN=""
RUNNER_TOKEN="$(docker exec -u git gitea gitea actions generate-runner-token 2>/dev/null | tail -1)"
if [[ -z "$RUNNER_TOKEN" ]]; then
log_warning "Couldn't generate a runner token automatically (older Gitea image?). Generate one by hand:"
log_warning " Gitea -> Site Administration -> Actions -> Runners -> Create new Runner"
log_warning " then add an act_runner container yourself using that token — see"
log_warning " https://docs.gitea.com/usage/actions/quickstart for the compose snippet."
return 1
fi
mkdir -p "$DIR/runner-data"
cat >> "$DIR/docker-compose.yml" << EOF
act_runner:
image: gitea/act_runner:latest
container_name: gitea-runner
restart: unless-stopped
environment:
- GITEA_INSTANCE_URL=http://gitea:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_TOKEN}
- GITEA_RUNNER_NAME=gitea-runner
volumes:
- ./runner-data:/data
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- gitea
EOF
_gitea_fix_ownership "$DIR"
(cd "$DIR" && docker compose up -d act_runner) \
&& log_success "Actions runner started — .gitea/workflows/*.yml will now run automatically on push." \
|| log_warning "Runner failed to start — check: docker compose -f $DIR/docker-compose.yml logs act_runner"
}
# ── Own systemd timer, not gitea-github-sync.sh's built-in --install-timer ──
# The vendor script's own timer installer always runs the script bare (no
# --pull-only/--push-only), i.e. always both directions — there's no way to
@@ -307,6 +438,8 @@ install_gitea() {
echo "[DRY-RUN] Would ask sync direction (GitHub->Gitea / Gitea->GitHub / both) and whether"
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 to enable Gitea Actions (CI) with a local act_runner container"
echo "[DRY-RUN] Would write $DIR/README.md"
return 0
fi
@@ -333,6 +466,8 @@ install_gitea() {
&& log_success "Gitea refreshed and restarted." \
|| 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_actions_runner "$DIR"
log_success "Existing .env (tokens) and web/SSH ports were left untouched."
return 0
;;
@@ -494,9 +629,16 @@ ENV
_gitea_run_sync_direction_step "$DIR"
# ── Caddy (Gitea has its own built-in login — no Authelia needed) ──────
# ── Caddy — no forward_auth gate here. Gitea has its own built-in login,
# unlike the no-auth-at-all apps elsewhere in this repo that need Caddy
# to gate them via Authelia. Optional "Sign in with Authelia" (OIDC) is
# offered separately below, as an addition to Gitea's own login, not a
# replacement requiring Caddy involvement. ─────────────────────────────
configure_caddy_for_service "Gitea" "host.docker.internal:${WEB_PORT}" "git"
_gitea_offer_authelia_sso "$DIR"
_gitea_offer_actions_runner "$DIR"
write_readme "$DIR" << MD
# Gitea
@@ -528,6 +670,27 @@ Config (which repos, private/forks handling) lives at
\`~/.config/gitea-github-sync/config\` — edit directly, or re-run
\`bash gitea-github-sync.sh --init\` to redo it interactively.
## Sign in with Authelia (optional)
If Authelia is installed, re-run \`sudo ./setup.sh gitea\` (Update mode is
fine — this doesn't touch tokens or anything else) and answer yes to
"Add \"Sign in with Authelia\"?" to add it as an extra OAuth2 login option
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\`).
## Gitea Actions (CI) — optional local runner
Re-run \`sudo ./setup.sh gitea\` (Update mode is fine) and answer yes to
"Enable Gitea Actions?" to run \`.gitea/workflows/*.yml\` here the same way
GitHub Actions runs \`.github/workflows/*.yml\` — mainly useful as a fallback
so builds still work if GitHub is ever unreachable. Adds an \`act_runner\`
container (\`docker compose ps\` will show \`gitea-runner\`) that polls this
Gitea instance for jobs and launches a fresh container per job using this
host's own Docker socket — same pattern already used by this repo's
portainer/watchtower/uptimekuma services, not something new. Manage runners
under Site Administration -> Actions -> Runners.
## Manage
\`\`\`bash