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>
feat/versandtakt
nexxo 2026-08-03 07:15:10 +02:00
parent d4e5a6c3ef
commit 7b336d49f9
1 changed files with 226 additions and 0 deletions

View File

@ -0,0 +1,226 @@
# Runbook: Wenn der Tunnel weg ist
Der WireGuard-Tunnel trägt alles: die Konsole, jeden Host, jede
Provisionierung, das Terminal. Fällt er aus, sieht es aus, als sei das ganze
Produkt kaputt — obwohl meistens nur eine Sitzung nicht wieder aufgebaut wurde.
Dieses Runbook beantwortet zwei Fragen. Erstens: **womit komme ich noch hinein,
wenn der Tunnel nicht steht?** Zweitens: **wie bringe ich ihn zurück?**
---
## Die eine Regel
**Kein Weg zurück darf durch den Tunnel führen, der gerade kaputt ist.**
Alles unten ist danach sortiert. Wer als Rettungsweg etwas einplant, das selbst
WireGuard braucht, hat keinen Rettungsweg eingeplant.
---
## Die vier Wege hinein, in dieser Reihenfolge
### 1. SSH auf den CluPilot-Server
Der Hebel für fast alles. Er hängt an nichts, was hier beschrieben wird — der
Server ist über seine öffentliche Adresse erreichbar, unabhängig vom Tunnel.
```bash
ssh <benutzer>@<clupilot-server>
cd /opt/clupilot
```
Von hier aus laufen alle Befehle weiter unten.
### 2. Die Konsole des Anbieters (Hetzner: KVM oder Rettungssystem)
Für einen **Host**, nicht für den CluPilot-Server. Immer verfügbar, hängt an
keinem Dienst von uns. Der Weg, wenn ein Host weder über den Tunnel noch über
SSH antwortet.
### 3. Das Notfallskript auf dem Host
Liegt auf jedem übernommenen Host:
```
/usr/local/sbin/clupilot-emergency-open-firewall.sh
```
`SecureHostFirewall` lässt Port 22 nur aus dem WireGuard-Netz zu. Steht der
Tunnel nicht, ist SSH aus dem Internet **zu** — das ist Absicht und genau der
Grund, warum es dieses Skript gibt. Aufgerufen wird es über die Konsole des
Anbieters (Weg 2), nicht per SSH; per SSH käme man ja gerade nicht hin.
Danach ist der Host wieder aus dem Internet erreichbar. **Nach der Reparatur den
Schritt `SecureHostFirewall` erneut laufen lassen**, sonst bleibt die Maschine
offen.
### 4. Eine WireGuard-Konfiguration, die offline liegt
In der Zugangsliste der Konsole gibt es zu jedem Mitarbeiter-Zugang einen
Download. **Eine davon gehört außerhalb der Konsole aufbewahrt** — Passwort-Safe,
zweites Gerät, ausgedruckt. Sonst braucht man die Konsole, um an den Zugang zu
kommen, der einen zur Konsole bringt.
---
## Erst feststellen, wo es klemmt
Vom CluPilot-Server aus, in dieser Reihenfolge. Jede Zeile schließt eine
Möglichkeit aus.
```bash
cd /opt/clupilot
# 1. Steht wg0 überhaupt, und wie viele Zugänge kennt es?
sudo -u clupilot docker compose exec -T queue-provisioning wg show
# 2. Läuft der Container, in dessen Namensraum wg0 lebt?
sudo -u clupilot docker compose ps queue-provisioning
# 3. Was sagt er beim Starten?
sudo -u clupilot docker compose logs --tail=40 queue-provisioning
```
**Kein Interface, kein `wg0`** → weiter bei „Der Tunnel kommt nicht hoch".
**Interface da, Zugänge da, aber `Letzter Kontakt: nie` oder uralt, und die
Zahlen stehen bei ↑ etwas / ↓ 0 B** → weiter bei „Der Tunnel steht, aber
niemand kommt an".
**Ein einzelner Zugang fehlt, die anderen stehen** → das Problem liegt auf
dessen Gegenseite, nicht hier. Bei einem Host: Weg 2 und `systemctl status
wg-quick@wg0` auf der Maschine.
---
## Der Tunnel steht, aber niemand kommt an
Das häufigste Bild, und das mit der unauffälligsten Ursache.
Der Kernel merkt sich laufende UDP-Ströme samt Ziel. Wird der Tunnel-Container
**neu gebaut** (nicht neu gestartet — neu gebaut), bekommt er eine neue Adresse
im Compose-Netz, und die Weiterleitung für den WireGuard-Port wird neu
geschrieben. Die gemerkten Einträge zeigen aber weiter auf den alten Container.
Und sie verfallen nicht: WireGuard schickt alle 25 Sekunden ein Lebenszeichen
und hält den kaputten Eintrag damit am Leben. Der Zustand heilt also **nie** von
allein.
Daran erkennt man ihn:
* Ein Telefon kommt nach Aus- und Einschalten sofort zurück — ein neuer
Quellport bekommt einen frischen Eintrag.
* Ein **Host** kommt gar nicht zurück — er schickt stur vom selben Port.
* Auf der Zugangsseite: ↑ wächst weiter, ↓ bleibt bei 0 B.
Der Griff, auf dem **Wirt**, nicht im Container:
```bash
sudo conntrack -D -p udp --dport 51820
```
Fehlt das Werkzeug: `sudo apt-get install -y conntrack`. Danach dreißig Sekunden
warten — jeder Zugang meldet sich von selbst wieder, alle 25 Sekunden läuft sein
Keepalive.
> **Seit v1.4.6 macht das Update das selbst.** Es merkt sich die Container-ID
> vor und nach `docker compose up -d`; hat sie sich geändert, räumt es die
> Ströme weg. Kann es das nicht (es läuft als Dienstbenutzer, der Griff braucht
> Root), steht der Befehl als Warnung im Protokoll. Siehe „Einmal einrichten"
> unten.
---
## Der Tunnel kommt nicht hoch
`wg show` zeigt gar kein Interface.
```bash
cd /opt/clupilot
# Von Hand hochziehen — sagt auch gleich, woran es scheitert.
sudo -u clupilot docker compose exec -T queue-provisioning wg-quick up wg0
# Steht die Konfiguration überhaupt noch im Datenträger?
sudo -u clupilot docker compose exec -T queue-provisioning ls -l /etc/wireguard/
```
Die Zugänge stehen **in `wg0.conf`** und überleben einen Neustart. Fehlt die
Datei, ist der `wireguard`-Datenträger das Problem, nicht WireGuard.
Läuft der Container gar nicht:
```bash
sudo -u clupilot docker compose up -d queue-provisioning
sudo -u clupilot docker compose logs --tail=40 queue-provisioning
```
Prozess 1 ist `docker/provisioning-worker.sh`. Das Skript zieht wg0 hoch und
hält danach den Warteschlangen-Arbeiter in einer Schleife. Steht dort etwas
anderes, läuft der Container auf einem alten Stand — dann fehlt das Update.
---
## Ein Host kommt nicht zurück, alle anderen schon
Dann liegt es auf der Maschine. Über die Konsole des Anbieters (Weg 2):
```bash
systemctl status wg-quick@wg0
wg show
systemctl restart wg-quick@wg0
```
`ConfigureWireguard` schreibt dem Host `Endpoint = <Hub>` und
`PersistentKeepalive = 25` — er meldet sich also von sich aus, alle 25 Sekunden.
Tut er das nicht, läuft wg0 dort nicht.
Kommt man über die Konsole nicht weit genug, weil SSH zu ist: erst Weg 3, dann
weiter.
---
## Einmal einrichten, damit der Plan später trägt
Vier Dinge, die man vorher macht und nicht, wenn es brennt.
1. **Eine WireGuard-Konfiguration offline ablegen.** Siehe Weg 4.
2. **Zugang zur Anbieterkonsole prüfen** — einmal einloggen, bevor man es
braucht. Ein vergessenes Passwort ist im Ernstfall ein zweiter Ausfall.
3. **Dem Dienstbenutzer den conntrack-Griff erlauben**, damit das Update ihn
selbst ausführen kann. In `/etc/sudoers.d/clupilot-conntrack`:
```
clupilot ALL=(root) NOPASSWD: /usr/sbin/conntrack -D -p udp --dport 51820
```
Genau dieser eine Aufruf, nicht `conntrack` allgemein.
4. **`conntrack` installieren**, falls nicht vorhanden:
`sudo apt-get install -y conntrack`.
---
## Was inzwischen NICHT mehr passieren sollte
Drei Ursachen sind abgestellt. Wer sie trotzdem sieht, hat einen Server auf
altem Stand.
* **Bis v1.4.3** hing wg0 am Leben des Warteschlangen-Arbeiters: Prozess 1 des
Containers war `php artisan queue:work`. Ein Zeitüberlauf, ein fataler Fehler
oder ein Speicherlimit beendete den Arbeiter, damit den Container, damit den
Tunnel. Das las sich von außen als „das VPN ist unzuverlässig". Seit v1.4.4
läuft der Arbeiter in einer Schleife neben dem Tunnel.
* **v1.4.2** gab dem Tunnel-Container einen Netz-Alias. Ein Alias ist Teil der
Netzkonfiguration, und Compose baut einen Container neu, sobald die sich
ändert — mit allem, was oben unter „gemerkte Ströme" steht. Seit v1.4.4 ist
der Alias weg; nginx spricht die Terminal-Brücke unter dem Dienstnamen des
Nachbarn an.
* **Bis v1.4.5** startete jedes Update `queue-provisioning` neu und riss damit
jede Sitzung ab, auch wenn sich nur PHP-Code geändert hatte. Seit v1.4.6
bekommt der Arbeiter ein Signal und wird von seiner Schleife neu gestartet,
während der Container steht.
Erzwungen durch `tests/Feature/DeploymentRunsAsTheAppUserTest.php` — der Container
darf nicht in der Neustart-Liste stehen, und der conntrack-Griff muss im Skript
bleiben.