TURN-Rotation automatisch kanonisieren + Begruendung der Gitea-Kopie
Drei Dinge, die zusammengehoeren.
1. Falscher Satz raus. 'there is no direct-to-Gitea exception left' stand seit
eff643e (2026-08-02, von mir) achtzehn Zeilen ueber einem Absatz, der eine
laufende Ausnahme beschreibt - der Wiki-Umzug hatte die letzte Ausnahme auf
REPO-Ebene beseitigt, ich hatte das zu 'gar keine mehr' verallgemeinert.
2. Das Warum der Gitea-Kopie ergaenzt. Bisher stand nur der Mechanismus da
('the cluster pulls from Gitea'), nicht der Grund: git.lab haelt die
Bauplaene, Gitea eine Kopie, die der Cluster OHNE verfuegbares Lab erreicht.
Ohne diese Begruendung sieht der Aufbau nach Altbestand aus - eine spaetere
Session koennte die Flux-Quelle auf git.lab 'geradeziehen' und genau die
Lab-Unabhaengigkeit zerstoeren, fuer die sie da ist. Steht jetzt als
ausdrueckliche Warnung in beiden CLAUDE.md.
3. Den monatlichen Handgriff abgeschafft. Der Rotations-CronJob laeuft im
Cluster, erreicht git.lab nicht und pusht nach Gitea; von dort musste die
Rotation bisher per Hand ueber git.lab zurueck. Wird das vergessen,
ueberschreibt der naechste Mirror-Push sie und Flux spielt still das ALTE
Shared Secret wieder ein - ein Fehler ohne Symptom.
Der Schedule-Job canonize_rotation holt jetzt taeglich jeden
turn-secret-rotation-*-Branch von Gitea, der nicht in main steckt, merged
und pusht ueber git.lab. Taeglich statt monatlich zum Rotationstermin, weil
ein monatlicher Lauf genau einen Versuch haette.
Faellt etwas aus dem Rahmen - Merge-Konflikt oder ein Secret ohne ENC[ -,
bricht der Job ab und pusht NICHTS. Die rote Pipeline ist der Alarm; ein
zusaetzlicher Termin waere wieder ein Todo, das man vergessen kann.
Verifiziert: YAML parst, alle elf Script-Bloecke sind gueltige sh-Syntax, und
die Kernlogik gegen den echten Repo-Stand durchgespielt - beide vorhandenen
Rotations-Branches werden korrekt als 'schon in main' uebersprungen.
Noch offen (braucht Rechte, siehe Dateikopf): Project Access Token als
CANONIZE_TOKEN hinterlegen und den taeglichen Schedule anlegen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
This commit is contained in:
co-authored by
Claude Fable 5
parent
42da25d0a6
commit
316178bd51
@@ -24,3 +24,85 @@ verify:
|
|||||||
if grep -q "ENC\[" "$f"; then echo "OK: $f ist verschluesselt"; else echo "WARNUNG: $f ist moeglicherweise NICHT verschluesselt!"; fi
|
if grep -q "ENC\[" "$f"; then echo "OK: $f ist verschluesselt"; else echo "WARNUNG: $f ist moeglicherweise NICHT verschluesselt!"; fi
|
||||||
done
|
done
|
||||||
- echo "Flux reconciled die Aenderungen innerhalb ~1 Minute (Quelle Gitea-Mirror)."
|
- echo "Flux reconciled die Aenderungen innerhalb ~1 Minute (Quelle Gitea-Mirror)."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TURN-Rotation kanonisieren (laeuft NUR als Pipeline-Schedule)
|
||||||
|
#
|
||||||
|
# Der Rotations-CronJob laeuft im Cluster und erreicht git.lab nicht - er pusht
|
||||||
|
# seinen Branch deshalb nach Gitea. Von dort muss die Rotation ueber git.lab
|
||||||
|
# zurueck, sonst ueberschreibt der naechste Mirror-Push sie und Flux spielt still
|
||||||
|
# das ALTE Shared Secret wieder ein. Frueher war das ein monatlicher Handgriff;
|
||||||
|
# dieser Job erledigt ihn.
|
||||||
|
#
|
||||||
|
# Einrichtung (einmalig, braucht Rechte - siehe CLAUDE.md):
|
||||||
|
# 1. Project Access Token, Rolle Maintainer, Scope write_repository
|
||||||
|
# -> CI/CD-Variable CANONIZE_TOKEN (masked + protected)
|
||||||
|
# 2. Pipeline-Schedule anlegen, taeglich, z. B. "17 5 * * *"
|
||||||
|
#
|
||||||
|
# Warum taeglich statt monatlich zum Rotationstermin: ein monatlicher Schedule
|
||||||
|
# hat genau einen Versuch. Faellt der Runner an dem Tag aus, faellt die
|
||||||
|
# Kanonisierung ein Monat lang aus. Taeglich holt der Job jede offene Rotation
|
||||||
|
# spaetestens am Folgetag nach und ist ansonsten in Sekunden fertig.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
canonize_rotation:
|
||||||
|
image: alpine:3.20
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||||
|
variables:
|
||||||
|
GIT_DEPTH: "0" # volle Historie: wir brauchen merge-base
|
||||||
|
script:
|
||||||
|
- apk add --no-cache git ca-certificates >/dev/null
|
||||||
|
- |
|
||||||
|
# Lab-CA: der Runner legt sie je nach Version an unterschiedlichen Orten ab.
|
||||||
|
for c in /etc/gitlab-runner/certs/git.lab.crt /etc/gitlab-runner/certs/ca.crt; do
|
||||||
|
[ -f "$c" ] && export GIT_SSL_CAINFO="$c" && break
|
||||||
|
done
|
||||||
|
echo "CA: ${GIT_SSL_CAINFO:-Systemspeicher}"
|
||||||
|
- |
|
||||||
|
if [ -z "$CANONIZE_TOKEN" ]; then
|
||||||
|
echo "CANONIZE_TOKEN fehlt - siehe Kopf dieser Datei, Schritt 1."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- git config --global user.email "ci@axion1337.chat"
|
||||||
|
- git config --global user.name "TURN-Rotation (automatische Kanonisierung)"
|
||||||
|
- git remote add gitea https://rohana.axion1337.de/sorb/axion1337.chat-gitops.git
|
||||||
|
- git fetch --quiet gitea
|
||||||
|
- git fetch --quiet origin main
|
||||||
|
- git checkout -B main origin/main
|
||||||
|
- |
|
||||||
|
MERGED=0
|
||||||
|
for ref in $(git for-each-ref --format='%(refname:short)' 'refs/remotes/gitea/turn-secret-rotation-*'); do
|
||||||
|
SHA=$(git rev-parse "$ref")
|
||||||
|
if git merge-base --is-ancestor "$SHA" HEAD; then
|
||||||
|
echo "bereits kanonisiert: $ref"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo "kanonisiere: $ref ($SHA)"
|
||||||
|
SUBJ="chore(coturn): TURN-Rotation aus ${ref#gitea/} uebernommen"
|
||||||
|
BODY="Automatisch kanonisiert: der Rotations-CronJob im Cluster erreicht git.lab nicht und pusht nach Gitea; dieser Commit bringt die Rotation auf den kanonischen Weg zurueck, bevor der Mirror sie ueberschreiben kann."
|
||||||
|
if ! git merge --no-ff -m "$SUBJ" -m "$BODY" "$SHA"; then
|
||||||
|
echo "MERGE-KONFLIKT in $ref - es wird nichts gepusht."
|
||||||
|
echo "Von Hand aufloesen, siehe CLAUDE.md (Abschnitt Repo Topology)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
MERGED=1
|
||||||
|
done
|
||||||
|
echo "MERGED=$MERGED" > .canonize_state
|
||||||
|
- |
|
||||||
|
. ./.canonize_state
|
||||||
|
if [ "$MERGED" = "0" ]; then
|
||||||
|
echo "Keine offene Rotation - nichts zu tun."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Sicherheitsnetz: die Rotation fasst genau diese Secrets an. Kaeme hier
|
||||||
|
# etwas Unverschluesseltes durch, waere das Shared Secret im Klartext in Git.
|
||||||
|
for f in apps/production/coturn-secret.yaml apps/production/synapse-turn-secret.yaml; do
|
||||||
|
if ! grep -q "ENC\[" "$f"; then
|
||||||
|
echo "ABBRUCH: $f ist nicht SOPS-verschluesselt - es wird nichts gepusht."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "OK: $f ist verschluesselt"
|
||||||
|
done
|
||||||
|
git push "https://oauth2:${CANONIZE_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:main
|
||||||
|
echo "Gepusht. Der Mirror traegt es nach Gitea zurueck, der PR schliesst sich dort selbst;"
|
||||||
|
echo "der Rotations-Branch existiert dann nur noch auf Gitea und wird vom Mirror entfernt."
|
||||||
|
|||||||
@@ -39,8 +39,17 @@ Gitea; the mirror delivers). **Never push directly to Gitea** for this repo —
|
|||||||
force-overwrites divergent state. The same rule applies to ThreadNet-Web, threadnet-call,
|
force-overwrites divergent state. The same rule applies to ThreadNet-Web, threadnet-call,
|
||||||
thread-net-git, threadnet-operating and (since 2026-08-01) `management` (the former
|
thread-net-git, threadnet-operating and (since 2026-08-01) `management` (the former
|
||||||
`Backlogs` repo, renamed in the PM-framework restructuring — ADRs/vision/roadmap live
|
`Backlogs` repo, renamed in the PM-framework restructuring — ADRs/vision/roadmap live
|
||||||
there). **Since 2026-08-02 the wiki lives on git.lab too** — there is no direct-to-Gitea
|
there). Since 2026-08-02 the wiki lives on git.lab too, so **no repo is authored on
|
||||||
exception left.
|
Gitea any more**; the one process that still *writes* there is the TURN rotation
|
||||||
|
(see below).
|
||||||
|
|
||||||
|
**Why Gitea is the Flux source, and why that is not a leftover.** git.lab holds the
|
||||||
|
blueprints; Gitea holds a copy the cluster can reach without the lab being up. That
|
||||||
|
separation is deliberate: the Hetzner cluster must be buildable and re-deployable when
|
||||||
|
the homelab is offline, on holiday, or mid-rebuild — it therefore must not depend on a
|
||||||
|
host that only answers inside the lab. **Do not "fix" the Flux source to point at
|
||||||
|
git.lab**: it would look tidier and would couple production availability to the lab,
|
||||||
|
which is exactly what this split avoids.
|
||||||
|
|
||||||
**Issues live on git.lab** (migrated 2026-08-01, gitops#48): the old Gitea issues are
|
**Issues live on git.lab** (migrated 2026-08-01, gitops#48): the old Gitea issues are
|
||||||
closed with a pointer to their GitLab counterpart. ⚠️ gitops issue numbers **shifted**
|
closed with a pointer to their GitLab counterpart. ⚠️ gitops issue numbers **shifted**
|
||||||
@@ -58,11 +67,22 @@ it is a stale May snapshot of `docs/`; don't edit or trust it. All doc sources
|
|||||||
Docusaurus site at **wiki.lab**, configured in `git.lab/homelab/wiki` — content is
|
Docusaurus site at **wiki.lab**, configured in `git.lab/homelab/wiki` — content is
|
||||||
pulled at build time, so edits always belong in the source repo.
|
pulled at build time, so edits always belong in the source repo.
|
||||||
|
|
||||||
**Exception that needs manual handling**: the monthly TURN-rotation CronJob runs in the
|
**The one write that still lands on Gitea**: the monthly TURN-rotation CronJob runs in
|
||||||
cluster (no route to git.lab) and still opens its PR on Gitea. Never merge that PR on
|
the cluster (no route to git.lab) and pushes its rotation branch to Gitea. Never merge
|
||||||
Gitea — instead fetch the rotation branch, merge it into main locally, push to git.lab;
|
that PR on Gitea — the rotation has to travel back through git.lab, otherwise the next
|
||||||
the mirror carries it back, Gitea auto-closes the PR (worked example: 2026-08-01,
|
mirror push overwrites it and Flux silently re-applies the *old* shared secret.
|
||||||
commit 640c934). If the mirror lags, force it: GitLab API
|
|
||||||
|
**This is automated — do not do it by hand.** The scheduled job `canonize_rotation` in
|
||||||
|
`.gitlab-ci.yml` runs daily on git.lab, picks up any `turn-secret-rotation-*` branch
|
||||||
|
from Gitea that is not yet in `main`, merges it, and pushes to git.lab; the mirror
|
||||||
|
carries it back and Gitea auto-closes the PR. Once merged, the branch exists only on
|
||||||
|
Gitea, so the next mirror run deletes it — no cleanup needed.
|
||||||
|
|
||||||
|
If the job fails (merge conflict, or the SOPS check finds an unencrypted secret file),
|
||||||
|
it fails **loudly and changes nothing** — the pipeline stays red until someone looks.
|
||||||
|
That red pipeline is the alarm; there is no separate reminder. Manual fallback, should
|
||||||
|
it ever be needed: fetch the branch, merge into main locally, push to git.lab (worked
|
||||||
|
example: 2026-08-01, commit `640c934`). If the mirror lags, force it via the GitLab API
|
||||||
`POST /projects/<id>/remote_mirrors/<mirror_id>/sync`.
|
`POST /projects/<id>/remote_mirrors/<mirror_id>/sync`.
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|||||||
Reference in New Issue
Block a user