feat(gitea): require sign-in to read anything; Flux and CI authenticate first
gate / gate (pull_request) Failing after 59s
gate / conformance (pull_request) Skipped

Gitea stays public on the Cloudflare Tunnel, but anonymous crawlers have kept
the pod at 2-6 cores since PR #156. Operator decision 2026-11-14: reading
anything requires a (Keycloak) login.

- catalog/gitea.yaml (+ re-rendered apps/wave-0/gitea.yaml):
  GITEA__service__REQUIRE_SIGNIN_VIEW=true (strict: web, API, git pull,
  registry /v2/token), and a postStart-written robots.txt (Disallow: /).
- clusters/prod: flux-system GitRepository gets secretRef flux-gitea-read,
  an OUT-OF-BAND read:repository token the operator must create BEFORE merge.
- gate.yml / cve-triage.yaml: the toolbox job image was pulled anonymously
  from the Gitea registry; it now uses container.credentials with the
  existing RENOVATE_TOKEN (read:package, proven by renovate.yaml preflight).

Anonymous-reader inventory and operator steps:
decisions/2026-11-14-gitea-require-signin.md

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
jakman85andClaude Opus 5.5 committed 2026-11-14 14:16:50 -06:00
1 parent 93c4994bf1
commit b855b6b294
6 files changed
+238 -12

No files matched your search

+29 -1
View File
@@ -60,7 +60,22 @@ env:
# ALLOW_ONLY_INTERNAL_REGISTRATION and ENABLE_AUTO_REGISTRATION, not on DISABLE_REGISTRATION.
- name: GITEA__service__DISABLE_REGISTRATION
value: "true"
- name: GITEA_ADMIN_USERNAME # break-glass local admin creds (consumed by the postStart hook)
# NOTHING IS READABLE ANONYMOUSLY (operator decision 2026-11-14). Since PR #156 put gitea.<domain>
# on the public Cloudflare Tunnel, anonymous crawlers kept this pod at 2-6 cores (~24k requests in
# 90 min, mostly /<owner>/<repo>/compare/... at ~13 s each). Gitea stays public, but every page,
# API call, git clone/fetch and container-registry pull now needs a signed-in user or a token.
# `true` is the STRICT mode (setting.Service.RequireSignInViewStrict): it also disables the
# anonymous git pull path (routers/web/repo/githttp.go) and the anonymous /v2/token exchange
# (routers/api/packages/container/container.go). `expensive` would only hide the costly pages and
# leave code readable, which is not what was asked for.
# Still anonymous, by upstream design (routers/web/web.go): /robots.txt, /api/healthz, /user/login
# and /user/oauth2/keycloak — so humans can still reach the Keycloak button.
# EVERY anonymous reader had to gain a credential before this could be turned on; the inventory,
# and the operator prerequisite for Flux's own source, are in
# decisions/2026-11-14-gitea-require-signin.md. Flipping this back to false is safe at any time.
- name: GITEA__service__REQUIRE_SIGNIN_VIEW
value: "true"
- name: GITEA_ADMIN_USERNAME # break-glass local admin creds (consumed by the postStart hook)
valueFrom:
secretKeyRef:
name: gitea-bootstrap
@@ -120,6 +135,19 @@ lifecycle:
done
echo "[gitea-bootstrap] created break-glass admin '$GITEA_ADMIN_USERNAME'"
fi
# --- robots.txt: ask polite crawlers to stay away entirely (defence in depth) ---
# REQUIRE_SIGNIN_VIEW already refuses them, but every refused hit is still a redirect to
# /user/login that Gitea has to serve. Gitea serves $GITEA_CUSTOM/public/robots.txt
# (/data/gitea in this image), checked on EVERY request (routers/web/misc/misc.go), and
# the route is outside the sign-in requirement. Written after the DB wait above, so the
# entrypoint has already created and chowned /data/gitea. Rewritten each boot, so this
# declaration is the only source of its content.
if su-exec git mkdir -p /data/gitea/public \
&& printf 'User-agent: *\nDisallow: /\n' | su-exec git tee /data/gitea/public/robots.txt >/dev/null; then
echo "[gitea-bootstrap] robots.txt written (Disallow: /)"
else
echo "[gitea-bootstrap] could not write /data/gitea/public/robots.txt" >&2
fi
# --- Keycloak OIDC login source (idempotent add-or-update; R-IDP-02/06) ---
# Registers the confidential `gitea` client as the OAuth2 login source "keycloak" against the
# homelab realm's discovery doc. The `groups` claim maps platform-admins -> Gitea admin