Was in bmeise nachzuziehen war
Erledigt am 14.08.2026, angewendet und nachgeprüft. Diese Datei bleibt als Protokoll stehen — die Punkte unten beschreiben den Zustand vor dem Deploy und werden nicht nachgeführt. Was heute gilt, steht im Abschnitt gleich darunter.
Diese Datei beschreibt Änderungen an einem anderen Repo (~/137/bmeise) und
ändert dort nichts. Alles betrifft inventory/host_vars/paketzentrum.yml, Dienst
diercki.
Stand nach dem Deploy
Punkte 1 bis 5 sind umgesetzt (bmeise-Commit d632fd4, „diercki zieht das Image
aus der CI und meldet sich selbst an“) und angewendet. Die Reihenfolge unten
ist so durchlaufen worden: PR gemergt (07a96b1), der CI-Job „Image“ hat
ghcr.io/yuszuv/diercki:latest veröffentlicht, danach make webhost.
Gegen die laufende Seite nachgeprüft am 14.08.2026:
| Adresse | Antwort |
|---|---|
/uploads/neumaier-frueher.png |
302 → /anmelden |
/Gruss-an-Stefan-Waldmann.dc.html |
302 → /anmelden |
/werkstatt/recherche/sternprodukt-notizen.md |
302 → /anmelden |
/Rumaenien-Physisch.html |
200 |
/health |
200 |
Kein WWW-Authenticate in den Antworten: es ist der Wächter der Anwendung, der
schützt, nicht mehr der basicauth-Block des Caddy. Die doppelte Abfrage ist
damit weg, ohne dass zwischendurch etwas offen stand.
Reihenfolge — so ist sie gelaufen
Sie war ursprünglich: erst deployen, dann den Caddy entschärfen. Da der Block im
bmeise-Repo schon entfernt war, drehte sich die Gefahr um auf zu früh
deployen — ein make webhost vor dem veröffentlichten Image hätte einen
Caddyfile ohne basicauth geschrieben und wäre danach am Pull gescheitert: alter
Container, alter Inhalt, kein Schutz. Deshalb in dieser Reihenfolge:
- PR mergen
- den CI-Job „Image“ abwarten, bis
ghcr.io/yuszuv/diercki:latestexistiert - erst dann
make webhost LIMIT=paketzentrum - die zwei
curl-Nachkontrollen auspaketzentrum.yml(302 auf das Foto, 200 auf ein offenes Blatt)
1 · Der Kommentar beschreibt einen Zustand, den es nicht mehr gibt
Aktuell steht dort:
„Statisch, aber in einem Container: nginx schreibt die CDN-Verweise der Blätter um und liefert das JSON-Verzeichnislisting, an dem die Drift-Erkennung der Seite hängt. Gebaut wird auf dem Host aus dem gepinnten Commit — das Image ist Inhalt, kein kompiliertes Artefakt, eine Registry-Rundreise pro Kartenänderung wäre die falsche Granularität.“
Vier Aussagen, drei überholt:
| steht dort | ist jetzt |
|---|---|
| nginx schreibt um | eine Rack-Middleware; nginx ist raus |
| JSON-Verzeichnislisting trägt die Drift-Erkennung | /_index/ gibt es nicht mehr, der Server liest das Verzeichnis direkt |
| „statisch“ | eine Roda-Anwendung mit Anmeldung |
| Image ist Inhalt, nicht kompiliert | kompiliert nio4r, zieht plattformspezifische sqlite3-Binaries |
Vorschlag:
# Sternprodukt-Atlas. Eine Roda-Anwendung, die den Baum ausliefert und
# Startseite, Schaukasten, Blattschau, Namensregister und Werkstatt daraus
# baut; eine Rack-Middleware schreibt die CDN-Verweise der Blätter auf lokal
# eingebackene Bibliotheken um. Das Image wird in der CI des Repos gebaut und
# nach ghcr.io geschoben — seit dem Ruby-Teil ist es kein reiner Inhalt mehr,
# sondern kompiliert (nio4r, sqlite3), und ein Bauversagen gehört nicht auf
# den Server.
2 · Nicht mehr bauen, sondern ziehen
build: true entfällt. Die Rolle nimmt dann von selbst pull: always und
build: policy — an roles/dockerapp ändert sich nichts, und ihr eigener
Vertrag verlangt es ohnehin: „Prefer pre-built images“. diercki war der
einzige build: true-Eintrag über alle Hosts.
Das Compose-Image ist ${ATLAS_IMAGE:-ghcr.io/yuszuv/diercki:latest}. Ohne
weitere Angabe folgt der Dienst latest, also main. Wer einen benannten Stand
will, setzt in dockerapp_env:
ATLAS_IMAGE: ghcr.io/yuszuv/diercki:sha-<commit>
Ist das Paket privat, braucht es dockerapp_registry auf ghcr.io plus
dockerapp_registry_username/password (ein PAT mit read:packages). Bei einem
öffentlichen Paket nicht.
3 · Drei Umgebungswerte über dockerapp_env
dockerapp_env:
ATLAS_KONTO: jan
ATLAS_PASSWORT_HASH: "<base64>" # vaulten
ATLAS_SESSION_SECRET: "<hex 64>" # vaulten
Ohne sie startet die Anwendung nicht — Absicht, ein Vorgabe-Geheimnis wäre schlimmer als keines.
Der Hash muss base64 sein. Nicht Geschmack, sondern gemessen: dockerapp_env
schreibt eine .env, und docker compose löst ${…} in jedem Wert auf, den es
liest — auch dort. Ein bcrypt-Hash besteht aus $-Feldern:
roh: $2a$12$KDvI6RuYisJKz0YWxL3ntewqW5gpbtOdlRMa2Typjy2VoI2/qxuoa
im Container: a2/qxuoa
Weder einfache noch doppelte Anführungszeichen noch $$-Verdopplung helfen,
env_file: verhält sich wie environment:. Die Anwendung würde starten und das
richtige Passwort ablehnen, ohne dass irgendwo etwas dazu steht.
Erzeugen:
ruby -rbcrypt -e 'print [BCrypt::Password.create("…")].pack("m0")'
ruby -rsecurerandom -e 'print SecureRandom.hex(64)'
web/boot.rb verweigert den Start, wenn dekodiert kein bcrypt-Hash herauskommt —
seit dem settings-Umbau in einem Konstruktor des settings-Providers, und zusammen
mit allem anderen, was gleichzeitig fehlt. An Namen und Form der drei Variablen
ändert das nichts, dockerapp_env bleibt also, wie es hier steht.
4 · Erst nach bewiesenem Deploy: basicauth entfernen
Der ganze Block kann weg. Die vier Pfade stehen unverändert in
web/geschuetzt.csv, und der Wächter der Anwendung sitzt vor der
Statik-Auslieferung — er greift damit auch für die zwei statischen Dateien
(Gruss-an-Stefan-Waldmann.dc.html, uploads/neumaier-frueher.png), die der
Caddy heute schützt.
Prüfen vor dem Entfernen, gegen die laufende Seite:
curl -sI https://diercki.sternprodukt.de/uploads/neumaier-frueher.png # 302 → /anmelden
curl -sI https://diercki.sternprodukt.de/Rumaenien-Physisch.html # 200
Solange der Caddy-Block noch steht, fragt er zusätzlich — doppelte Abfrage, aber nichts ist offen. Andersherum wäre es ein Fenster.
5 · upstream: diercki:80 bleibt
Der Container lauscht auf ATLAS_PORT, Vorgabe 80. Lokal ist alles 9292, aber
innen bleibt es 80, damit hier nichts nachzuziehen ist. Wer das ändern will,
ändert beides in einem Zug.
Nachtrag 15.08.2026 · der zweite Deploy griff nicht
Kein Nachführen der Punkte oben — sie beschreiben, was am 14.08. galt. Hier steht, was danach passierte.
Nach dem Merge der drei PRs (55e1128, 9baf841, 4ab4f79) lief make webhost
LIMIT=paketzentrum erneut, meldete Erfolg — und lieferte weiter 07a96b1 aus.
Der Container war unverändert, das Image nie geholt.
Ursache in bmeise, nicht hier: playbooks/webhost.yml reicht dockerapp_pull als
default(omit) durch, und roles/dockerapp/tasks/deploy.yml liest es mit
default('always'). Ansibles omit ist aber die Zeichenkette
__omit_place_holder__… und damit definiert, also greift der Rückfall nie; beim
Modulaufruf wird der Platzhalter entfernt, und docker_compose_v2 nimmt seine eigene
Vorgabe pull: policy — bei der es --pull gar nicht anhängt. Die Rolle warnt in
tasks/main.yml wörtlich vor genau dieser Falle und benutzt dort eine Längenprüfung;
bei pull war sie vergessen.
Betroffen war jede App mit beweglichem Tag: diercki:latest und orbit:latest
hier, brvz-staging auf nebenstelle. Bei festen Tags (beszel:0.18.7,
matomo:5.2.1) ist „nur wenn fehlt“ zufällig richtig.
Was daraus folgte:
- bmeise korrigiert die Durchreichung und prüft den errechneten Wert mit einem
assertgegen die vier erlaubten Literale, damit ein Platzhalter künftig laut scheitert statt still auf die Modulvorgabe zurückzufallen. orbitundbrvz-stagingbekommen ausdrücklichpull: missing— eingefroren als Entscheidung, nicht als Nebeneffekt.- Der Deploy prüft nach dem Hochfahren
https://diercki.sternprodukt.de/versiongegen den ausgecheckten Commit und scheitert bei Abweichung.
Die vier curl-Kontrollen aus Punkt 4 hätten das nicht gefangen: sie fragen, ob
der Wächter hält, und er hielt. Sie bleiben trotzdem — sie prüfen etwas anderes.