5 Commits (a569d2215b4473d584592210f1d44870df10607f)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
6ebbaa82aa |
Ein Host, der die Sperrmengen nicht kennt, laesst sich jetzt nachziehen
Fix-Welle nach dem Gesamt-Review, zweite Haelfte von Punkt 3. Die beiden nftables-Mengen clupilot_blocked und clupilot_blocked6 kamen mit dem Fruehwarnsystem ins Regelwerk. Jeder Host, der VORHER uebernommen wurde, traegt noch die alte Datei — dort scheitert `nft add element` bei jedem Versuch, und die Sperre steht in Datenbank, Portal, Konsole und in der Mail an den Kunden als aktiv, in der Firewall aber nie. Gemeldet wird dieser Fall seit dem Commit davor; das hier ist der Griff, mit dem man ihn abstellt. Der Uebernahme-Lauf hilft nicht, und genau das stand im Runbook falsch: SecureHostFirewall kuerzt sich ueber den `host_firewall`-Brotkrumen ab, und der haengt am LAUF, nicht am Host. Auf einem bereits uebernommenen Host taete der Schritt gar nichts und meldete trotzdem "erledigt". Das Repo hat fuer genau das ein Muster, und es passt: clupilot:apply-quotas faehrt ueber eine EIN-SCHRITT-Pipeline (`quota`) einen einzelnen Schritt gegen ein bestehendes Subjekt — gedrosselt, wiederholt, protokolliert und in der Konsole sichtbar wie jede andere Fernarbeit, statt dass ein Konsolenbefehl selbst auf die Maschine greift. Ein zweiter Weg, dieselbe Datei zu schreiben, wuerde driften. Also dasselbe hier: - Pipeline `host-firewall` mit Host\SecureHostFirewall als einzigem Schritt. Ein frischer Lauf hat den Brotkrumen nicht, fuehrt den Schritt also wirklich aus — und weil der Schritt die Datei ohnehin vollstaendig neu schreibt und vorher den Tunnel von der HOSTSEITE aus nachprueft, ist das dieselbe Arbeit wie beim ersten Mal, nicht eine zweite Umsetzung davon. - EIGENE Pipeline und nicht `host`, und das ist kein Ordnungssinn: RunRunner::failRun() loest den Subjekt-Haken nur aus, wenn der gescheiterte Lauf DER Lauf des Subjekts ist. Unter `host` wuerde ein gescheitertes Nachziehen einen laufenden, bezahlten Host auf 'error' stellen. Dafuer gibt es einen eigenen Test. - php artisan clupilot:refresh-host-firewall, mit --dry-run und --host=, und mit derselben "der Grund ist der Bericht"-Ausgabe wie beim Vorbild: "12 uebersprungen" und sonst nichts ist keine Auskunft, mit der jemand etwas anfangen kann. Kein Zeitplan, aus den drei Gruenden, die schon ueber clupilot:apply-quotas stehen: das Loch ist endlich und schliesst sich endgueltig, ein naechtlicher Lauf waere eine zweite Instanz, die dieselbe Datei auf laufende Maschinen schreibt und am Tag eines still kaputten Pipeline-Schritts fuer ihn einspraenge, und eine Reparatur, die der Betreiber anstoesst, ist eine, deren Ausgabe er liest. docs/runbooks/tunnel-recovery.md ist richtiggestellt. Dort stand, man solle nach dem Notfallskript "den Schritt SecureHostFirewall erneut laufen lassen" — jetzt steht dort der Befehl, mit der Warnung darunter, warum der alte Rat nicht trug. Committet mit ausdruecklicher Dateiangabe am Zeilenende, weil eine parallele Sitzung an derselben Ablage arbeitet und der Index fremde Arbeit enthalten kann. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
5f3d983658 |
Der Tunnel bekommt eine feste Adresse — damit ist die conntrack-Falle weg statt behandelt
Die Wurzel des ganzen Uebels, endlich an der Wurzel: Docker traegt den veroeffentlichten UDP-Port als Weiterleitung auf die Container-Adresse ein, und der Kernel merkt sich jeden laufenden Strom samt dieser Adresse. Bekam der Container beim Neubau eine andere — und er bekam die naechste freie —, zeigten alle gemerkten Stroeme ins Leere. Und sie verfallen nicht, weil WireGuards Lebenszeichen sie alle 25 Sekunden auffrischt. Deshalb kam ein Telefon nach Aus/Ein sofort zurueck und ein Host mit festem Quellport ueberhaupt nicht. Jetzt hat vpn-hub eine feste Adresse (172.18.0.240, ueber CLUPILOT_VPN_HUB_IP aenderbar) in einem Netz mit erklaertem Subnetz. Nach einem Neubau entsteht exakt dieselbe Weiterleitung, die gemerkten Stroeme bleiben gueltig, und es gibt nichts mehr aufzuraeumen. Nachgemessen: 172.18.0.240 vor und nach `up -d --force-recreate vpn-hub`. UND DIE UMSTELLUNG SELBST, die mich beim Bauen fast den Stapel gekostet haette: Docker kann ein bestehendes Netz nicht umdefinieren. Es muss neu angelegt werden, und das scheitert, solange auch nur EIN Container daranhaengt — `up -d` bricht dann mit "network … has active endpoints" ab und laesst den Stapel halb unten stehen. Genau so hier passiert, mit einem Container, der gar nicht zum Stapel gehoerte. Das Deployment vergleicht deshalb vorher das erklaerte Subnetz mit dem tatsaechlichen und faehrt bei Abweichung EINMAL geordnet herunter, statt darueber zu stolpern. Danach stimmen die Werte ueberein und der Block tut nie wieder etwas. Das conntrack-Aufraeumen bleibt trotzdem drin: als Rueckfahrkarte fuer Server auf aelterem Stand und fuer den Fall, dass jemand das Subnetz aendert. 2510 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
e3c781f067 |
Der Tunnel bekommt einen eigenen Container
Bisher wohnte wg0 im Container des Provisionierungs-Arbeiters, also im Abbild `clupilot-app`. Das wird bei fast jeder Freigabe neu gebaut — und ein neu gebauter Container bekommt eine neue Adresse im Compose-Netz, womit die Weiterleitung fuer UDP 51820 neu geschrieben wird und JEDE bestehende WireGuard-Sitzung abreisst. Der Tunnel hing damit am Veroeffentlichungstakt der Anwendung, und zusaetzlich daran, dass ein PHP-Prozess nicht abstuerzt. Jetzt gehoert der Netz-Namensraum einem eigenen Container `vpn-hub` mit eigenem, winzigem Abbild (Alpine plus wireguard-tools), das sich fast nie aendert. Provisionierungs-Arbeiter, Terminal-Bruecke, interner DNS und internes Gateway steigen dort ein, statt einer von ihnen den Namensraum zu besitzen. NACHGEMESSEN, nicht angenommen: App-Abbild neu gebaut, Arbeiter, Bruecke und Gateway per --force-recreate neu erzeugt — der Hub blieb Container 92e928cf53b0, wg0 und beide Zugaenge unangetastet, und nginx erreichte die Bruecke weiter (HTTP 426). Genau der Vorgang, der bisher jedes Mal alles abgerissen hat. Nachgezogen: - nginx spricht die Bruecke unter `vpn-hub:8082` an — dem Namen des Namensraum-Eigentuemers; ein Mitbewohner hat keinen eigenen DNS-Eintrag. - update.sh baut vpn-hub mit und haengt Nachbar-Neustarts und den conntrack-Griff an die Frage, ob der Hub WIRKLICH neu gebaut wurde. - update-agent.sh startet den Arbeiter nicht mehr neu, sondern signalisiert ihm. Diese Stelle laeuft unbeaufsichtigt hinter dem Knopf „Dienste neu starten" — wer den drueckt, rechnet nicht damit, sich selbst auszusperren. - Vier Meldungen in der Konsole rieten dem Betreiber, genau den Befehl von Hand auszufuehren, der ihm den Tunnel abreisst. Auch die sind korrigiert. - rescue-tunnel.sh und das Runbook zeigen auf den neuen Besitzer. Eine Kleinigkeit unterwegs, die ich falsch angekuendigt hatte: `[[ … ]] && x=true` bricht unter `set -e` NICHT ab — bash nimmt die linke Seite einer &&-Liste ausdruecklich aus. Nachgeprueft; die if-Form bleibt trotzdem, aus Lesbarkeit, und der Kommentar sagt jetzt den wahren Grund. 2509 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
7b336d49f9 |
Runbook: Wenn der Tunnel weg ist
tests / pest (push) Failing after 8m44s
Details
tests / assets (push) Successful in 25s
Details
tests / release (push) Has been skipped
Details
Der Rettungsweg war bisher muendliche Ueberlieferung, verteilt ueber einen Chatverlauf. Jetzt aufgeschrieben, mit der einen Regel darueber: kein Weg zurueck darf durch den Tunnel fuehren, der gerade kaputt ist. Vier Wege hinein (SSH auf den Server, Anbieterkonsole, das Notfallskript auf dem Host, eine offline abgelegte WireGuard-Konfiguration), ein Entscheidungsbaum zum Eingrenzen, und je Fall die Befehle. Dazu vier Dinge, die man EINMAL einrichtet, solange nichts brennt — darunter der sudoers-Eintrag, ohne den der conntrack- Griff aus v1.4.6 nur als Warnung im Protokoll landet statt zu laufen. Und ein Abschnitt darueber, was seit v1.4.4/v1.4.6 nicht mehr passieren sollte: wer es trotzdem sieht, hat einen Server auf altem Stand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
668ed67de4 |
Write down what to do when the bootstrap stops
One entry per section: how you notice, where to look, what it usually is. Above all of them the rule the plan already states — a half installed machine is reinstalled, not repaired — with the reason, which is not convenience. The script is built so a second run on a freshly imaged machine always comes out the same; a run on a machine somebody straightened out by hand is no longer that. Repairing trades a repeatable result for a one-off, and the difference only shows up on the next host. rescue_checked is the stated exception, because nothing has been written yet. It is the only section where fixing in place is right, and the runbook says so rather than leaving the reader to infer it. Two things that are easy to get wrong are answered up front. Whether the reboot has happened is one `findmnt -no FSTYPE /` — tmpfs or overlay means still in the rescue system, zfs means the first-boot hook already resumed. And when a host is unreachable, the first question is whether the firewall had even run: it is applied last, so if `registered` is not done, the cause is the network and not the rules. The installer can be watched while it runs. If QEMU is still up, forwarding 5900 over SSH shows what the ISO is doing, and an input mask there means the answer file was not accepted and the installer has dropped into interactive mode where it will sit until the hour expires. That failure otherwise arrives as a timeout saying nothing. The last heading is the honest one: everything here is written from the script and the findings behind it, not from incidents. What actually stops on the first real run belongs in this file afterwards, quoted as it appeared in the console. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |