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

+7 -1
View File
@@ -34,13 +34,19 @@ jobs:
runs-on: ubuntu-latest
# THE TOOLBOX, the image gate.yml already runs, pinned by digest. A job here has no Docker
# daemon, so a job container is the only way to choose an image. The toolbox provides
# python3, git, curl and jq, pulls anonymously like the gate's, and is normally already in
# python3, git, curl and jq, pulls with the gate's credential, and is normally already in
# the DinD store. It has no Node, so actions/checkout cannot run; a public python image has
# neither Node nor git and would add a second image to keep pinned. The cost is a cold pull
# after a runner restart, which the gate pays anyway.
container:
# renovate-digest: image=gitea.home.keithjackson.dev/home-network/toolbox tag=main
image: gitea.home.keithjackson.dev/home-network/toolbox@sha256:83a3501efe34fa0b039e3f0d360738686a0f59109866c49779768f5d5ed0ef81
# Anonymous registry pulls are refused since REQUIRE_SIGNIN_VIEW (catalog/gitea.yaml); this
# job already holds RENOVATE_TOKEN, whose read:package scope renovate.yaml's preflight proves.
# See "PULL AUTH" in gate.yml's header.
credentials:
username: renovate
password: ${{ secrets.RENOVATE_TOKEN }}
# Without a bound, a wedged run would hold the concurrency group and block every later run.
timeout-minutes: 15
steps:
+14 -5
View File
@@ -58,11 +58,16 @@
# Renovate follows the `main` tag and opens a digest-bump PR, whose OWN gate run then
# executes on the new image — the bump tests itself. infra/gitea/registry-retention.sh reads
# the same marker, so the pinned digest is never pruned. Never hand-edit it to a tag.
# * PULL AUTH: none needed. The Home-Network org is public and the registry issues an
# anonymous pull token (measured 2026-11-08: anonymous /v2/token -> manifest 200), so the
# runner's DinD pulls it like any public image. If the org is ever made private, add
# `credentials:` here with a read:package token secret — the automatic Actions token has no
# package scope at /v2/token (see images.yaml's header).
# * PULL AUTH: REQUIRED since 2026-11-14. Gitea now runs REQUIRE_SIGNIN_VIEW=true
# (catalog/gitea.yaml), whose strict mode refuses the anonymous /v2/token exchange that this
# pull used to rely on (measured 2026-11-08: anonymous /v2/token -> manifest 200). The job
# therefore declares `credentials:` with RENOVATE_TOKEN — an EXISTING secret whose
# read:package scope renovate.yaml's preflight already proves on every run, and the token the
# cve-triage job already uses. The automatic Actions token cannot do it: it has no package scope
# at /v2/token (see images.yaml's header). act interpolates both fields before the pull and
# FAILS THE JOB if either is empty, so a missing secret is a red gate, never a silent
# anonymous fallback. A dedicated read:package-only token would be narrower; swapping it in
# changes this one line.
# * COLD PULL: ~467 MB compressed (1.54 GB unpacked). The DinD store is an emptyDir, so the
# first job after a runner-pod restart or a digest bump pays one pull; every other run finds
# it cached (forcePull=false), exactly as it already did for the catthehacker image.
@@ -111,6 +116,10 @@ jobs:
container:
# renovate-digest: image=gitea.home.keithjackson.dev/home-network/toolbox tag=main
image: gitea.home.keithjackson.dev/home-network/toolbox@sha256:83a3501efe34fa0b039e3f0d360738686a0f59109866c49779768f5d5ed0ef81
# The registry refuses anonymous pulls (REQUIRE_SIGNIN_VIEW) — see "PULL AUTH" in the header.
credentials:
username: renovate
password: ${{ secrets.RENOVATE_TOKEN }}
steps:
# Plain git, because actions/checkout is a Node action and the toolbox has no Node. It
# reproduces what actions/checkout@v7 with `fetch-depth: 0` did here (read from its logged