Runbook: Wenn der Tunnel weg ist
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
parent
d4e5a6c3ef
commit
7b336d49f9
|
|
@ -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.
|
||||
Loading…
Reference in New Issue