From 33fb5be4df8d760e63bbe325d113398443e1459c Mon Sep 17 00:00:00 2001 From: nexxo Date: Mon, 3 Aug 2026 07:29:20 +0200 Subject: [PATCH] Ein Befehl statt einer Befehlsfolge, wenn der Tunnel weg ist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy/rescue-tunnel.sh stellt der Reihe nach fest, wo es klemmt, sagt es in klaren Worten und behebt, was sich vom Server aus beheben laesst: Container starten, wg0 hochziehen, gemerkte UDP-Stroeme wegraeumen. Und es sagt, wenn der Server noch vor v1.4.4 steht, wo der Tunnel noch am Warteschlangen-Arbeiter hing. Bewusst nichts Zerstoererisches — kein Neubau, kein Neustart des Tunnel-Containers. Genau der waere die Ursache und nicht die Loesung. Der Grund fuer das Skript: im Ernstfall soll niemand eine Befehlsfolge aus einem Runbook abtippen. Ein Befehl, und was danach noch zu tun bleibt, steht am Ende der Ausgabe. Co-Authored-By: Claude Opus 5 --- deploy/rescue-tunnel.sh | 138 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100755 deploy/rescue-tunnel.sh diff --git a/deploy/rescue-tunnel.sh b/deploy/rescue-tunnel.sh new file mode 100755 index 0000000..fbd1dc1 --- /dev/null +++ b/deploy/rescue-tunnel.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# +# CluPilot — den Tunnel wieder hinbekommen. +# +# Ein Befehl statt einer Befehlsfolge, die man sich im Ernstfall merken müsste. +# Das Skript stellt der Reihe nach fest, WO es klemmt, sagt es in klaren Worten, +# und behebt, was sich von hier aus beheben lässt. +# +# Es macht nichts Zerstörerisches: kein Neubau, kein Löschen, kein Neustart des +# Tunnel-Containers. Genau der wäre nämlich die Ursache und nicht die Lösung — +# ein Neubau schreibt die Weiterleitungsregeln neu und reisst alle Sitzungen ab. +# +# sudo -u clupilot bash deploy/rescue-tunnel.sh +# +# Ausführlich: docs/runbooks/tunnel-recovery.md +set -uo pipefail + +cd "$(cd "$(dirname "$0")/.." && pwd)" + +log() { printf '\n\033[1;34m==>\033[0m %s\n' "$*"; } +ok() { printf '\033[1;32m ✓\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33m !\033[0m %s\n' "$*"; } +bad() { printf '\033[1;31m ✗\033[0m %s\n' "$*"; } + +if [[ $EUID -eq 0 ]]; then + echo "Bitte als Dienstbenutzer starten, nicht als root:" >&2 + echo " sudo -u clupilot bash $0" >&2 + exit 1 +fi + +hub() { docker compose exec -T queue-provisioning "$@" 2>/dev/null; } + +WG_PORT="$(grep -m1 '^WG_HUB_PORT=' .env 2>/dev/null | cut -d= -f2- | tr -d '"'"'"' ')" +WG_PORT="${WG_PORT:-51820}" + +# ── 1. Läuft der Container, in dessen Namensraum wg0 lebt? ─────────────────── +log "Der Container mit dem Tunnel" + +if ! docker compose ps --status running queue-provisioning 2>/dev/null | grep -q queue-provisioning; then + bad "queue-provisioning läuft nicht. Ich starte ihn." + docker compose up -d queue-provisioning || true + sleep 8 +fi + +if docker compose ps --status running queue-provisioning 2>/dev/null | grep -q queue-provisioning; then + ok "läuft" +else + bad "startet nicht. Das Protokoll sagt warum:" + docker compose logs --tail=30 queue-provisioning + exit 1 +fi + +# Prozess 1 verrät, ob der Server den Stand ab v1.4.4 hat. Vorher war es der +# Warteschlangen-Arbeiter, und dann nimmt jeder seiner Abstürze den Tunnel mit. +if hub sh -c 'tr "\0" " " < /proc/1/cmdline' | grep -q 'provisioning-worker.sh'; then + ok "Der Tunnel hängt nicht mehr am Warteschlangen-Arbeiter (v1.4.4 oder neuer)" +else + warn "Prozess 1 ist noch der Arbeiter selbst — dieser Server steht vor v1.4.4." + warn "Bis dahin nimmt jeder Absturz des Arbeiters den Tunnel mit. Update einspielen." +fi + +# ── 2. Steht wg0? ──────────────────────────────────────────────────────────── +log "Das Interface wg0" + +if ! hub wg show wg0 >/dev/null 2>&1; then + bad "wg0 fehlt. Ich ziehe es hoch." + hub wg-quick up wg0 || true + sleep 3 +fi + +if hub wg show wg0 >/dev/null 2>&1; then + ok "steht" +else + bad "lässt sich nicht hochziehen. Liegt die Konfiguration überhaupt da?" + hub ls -l /etc/wireguard/ || true + echo + bad "Weiter in docs/runbooks/tunnel-recovery.md, Abschnitt 'Der Tunnel kommt nicht hoch'." + exit 1 +fi + +peers="$(hub wg show wg0 peers | grep -c . || true)" +if [[ "${peers:-0}" -gt 0 ]]; then + ok "$peers Zugänge geladen" +else + warn "Kein einziger Zugang geladen — /etc/wireguard/wg0.conf ansehen." +fi + +# ── 3. Kommt auch etwas an? ────────────────────────────────────────────────── +# +# Das eigentliche Krankheitsbild: der Hub sendet, aber empfängt nichts. Dann +# zeigen die im Kernel gemerkten UDP-Ströme noch auf einen Container, den es +# nicht mehr gibt — und sie verfallen NICHT, weil WireGuards Keepalive sie alle +# 25 Sekunden auffrischt. Das heilt nie von allein. +log "Kommen Antworten an?" + +stumm=0 +while read -r line; do + [[ -z "$line" ]] && continue + empfangen="$(awk '{print $1}' <<<"$line")" + [[ "$empfangen" == "0" ]] && stumm=$((stumm + 1)) +done < <(hub wg show wg0 transfer | awk '{print $2, $3}') + +gesamt="$(hub wg show wg0 transfer | grep -c . || echo 0)" + +if [[ "${stumm:-0}" -gt 0 ]]; then + warn "$stumm von $gesamt Zugängen haben noch NIE etwas geschickt." + log "Gemerkte UDP-Ströme wegräumen" + if sudo -n conntrack -D -p udp --dport "$WG_PORT" >/dev/null 2>&1; then + ok "erledigt — die Zugänge bauen sich in den nächsten 30 Sekunden neu auf" + else + bad "Dafür fehlt mir Root. Bitte einmal von Hand ausführen:" + echo + echo " sudo conntrack -D -p udp --dport $WG_PORT" + echo + warn "Damit ich das künftig selbst darf, siehe docs/runbooks/tunnel-recovery.md," + warn "Abschnitt 'Einmal einrichten'." + fi +else + ok "Alle Zugänge haben schon Daten geschickt" +fi + +# ── 4. Der Stand, zum Mitlesen ─────────────────────────────────────────────── +log "Stand jetzt" +hub wg show wg0 || true + +echo +log "Wenn ein HOST weiter fehlt, während andere Zugänge stehen" +echo " Dann liegt es auf der Maschine, nicht hier. Über die Konsole des" +echo " Anbieters (SSH ist von aussen zu, das ist Absicht):" +echo +echo " systemctl status wg-quick@wg0" +echo " systemctl restart wg-quick@wg0" +echo +echo " Kommt man dort nicht weit genug, macht dieses Skript auf dem Host SSH" +echo " wieder auf — und muss danach zurückgenommen werden:" +echo +echo " /usr/local/sbin/clupilot-emergency-open-firewall.sh" +echo