This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/scripts/pinix-reachability-probe.sh
pijukka f6ef38b8b4 docs+scripts: Reboot-/Reconnect-Probe fuer pinix vorbereiten
Intent Schritt 6 ist der Torwaechter fuer alles Weitere beim selbst
gehosteten DNS, war aber nie vorbereitet. Jetzt als diffbare Messung
statt als Gedaechtnisleistung:

scripts/pinix-reachability-probe.sh nimmt den Zustand rein lesend auf --
Generationen, Units, stabile SLAAC-ULA, RA-Default-Route, Resolver,
BIND-Antwort, Mango-Adresse und -Regeln, Cloudflare-AAAA gegen die
tatsaechliche Adresse, externe Tests und Zertifikatsrestlaufzeit.
Bewusst ohne set -e, weil ein Fehlschlag das Messergebnis ist, mit
Timeout pro Schritt.

Die externen Tests binden zwingend auf wlan0. tnix haengt gleichzeitig
im Mango-LAN, und die DNAT-Regeln tragen reflection=0 -- ein Test ohne
Interface-Bindung nimmt den LAN-Weg und meldet einen Fehlschlag, der
keiner ist. Genau diese Verwechslung hat heute Zeit gekostet, zusammen
mit einem Test von blunix, das ueberhaupt keine oeffentliche IPv6 hat.

Kern des Dokuments: BIND hat noch keinen Bootvorgang erlebt. booted zeigt
auf Generation 240, current auf 242, beide BIND-Generationen sind nur per
switch aktiviert. Die Hauptvermutung ist der Resolver -- beim switch war
resolv.conf schon korrekt, beim Boot wird resolvconf neu erzeugt, und
gewinnt dort localhost, hat pinix keinen Resolver mehr, weil BIND
autoritativ ist und nicht rekursiert.

Rueckweg vorher festgeschrieben und geprueft: LAN-Zugang von tnix haengt
nicht an Mobilfunk, Nokia oder Tailscale, und system-240-link zeigt auf
die laufende Generation vor BIND. Headless gibt es keine Generationswahl
beim Boot -- extlinux nimmt ohne Konsole den Default -- daher der
SD-Karten-Weg als letzte Stufe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aw4viUJyXGP8ANocHiF15q
2026-09-12 17:46:49 +02:00

92 lines
5 KiB
Bash
Executable file

#!/usr/bin/env bash
# Zustandsaufnahme fuer die Reboot-/Reconnect-Probe (Intent Schritt 6).
# Von tnix aus laufen lassen, einmal vor und einmal nach der Unterbrechung:
#
# scripts/pinix-reachability-probe.sh vorher > /tmp/probe-vorher.txt
# scripts/pinix-reachability-probe.sh nachher > /tmp/probe-nachher.txt
# diff -u /tmp/probe-vorher.txt /tmp/probe-nachher.txt
#
# Absichtlich ohne `set -e`: ein Fehlschlag ist hier das Messergebnis, kein
# Abbruchgrund. Jeder Schritt hat ein Timeout, damit die Aufnahme auch dann
# durchlaeuft, wenn ein Pfad tot ist. Nur lesende Befehle.
#
# Die externen Tests muessen zwingend ueber EXT_IF (Hotspot) laufen: tnix
# haengt gleichzeitig im Mango-LAN, und die DNAT-Regeln haben
# `reflection=0`. Ein Test ohne --interface nimmt sonst den LAN-Weg und
# meldet einen Fehlschlag, der keiner ist -- genau diese Verwechslung hat
# am 12.09.2026 Zeit gekostet.
set -u
LABEL="${1:-unbenannt}"
PINIX_LAN="${PINIX_LAN:-192.168.8.244}"
PINIX_ULA="${PINIX_ULA:-fdee:3c40:44f0:0:dea6:32ff:fe48:c6c8}"
MANGO="${MANGO:-192.168.8.1}"
EXT_NAME="${EXT_NAME:-ipv6-test.0ff.space}"
EXT_IF="${EXT_IF:-wlan0}"
SSH_PINIX="ssh -o LogLevel=ERROR -o ConnectTimeout=8 -o BatchMode=yes -o HostKeyAlias=pinix drone@${PINIX_LAN}"
SSH_MANGO="ssh -o LogLevel=ERROR -o ConnectTimeout=8 -o BatchMode=yes root@${MANGO}"
say() { printf '\n== %s\n' "$1"; }
try() { timeout "${1}" bash -c "${2}" 2>&1 || echo "FEHLSCHLAG (exit $?)"; }
printf '# Probe "%s"\n' "$LABEL"
# Kein date +%s im Vergleich: die Uhrzeit unterscheidet sich immer und
# wuerde jeden diff verrauschen. Nur zur Protokollierung, bewusst am Rand.
printf '# Zeit: %s\n' "$(date '+%Y-%m-%d %H:%M:%S %Z')"
say 'pinix: Generationen (booted muss nach der Probe == current sein)'
try 15 "$SSH_PINIX 'readlink -f /run/booted-system; readlink -f /run/current-system' | sed 's|/nix/store/||'"
say 'pinix: Systemzustand und fehlgeschlagene Units'
try 15 "$SSH_PINIX 'systemctl is-system-running; systemctl --failed --no-legend --no-pager | cat'"
say 'pinix: Dienste'
try 15 "$SSH_PINIX 'for u in bind traefik mango-dns.timer tailscaled; do printf \"%s=%s\n\" \$u \$(systemctl is-active \$u 2>/dev/null); done'"
say 'pinix: stabile SLAAC-ULA (Ziel aller DNAT-Regeln) vorhanden?'
# Die Adresse ist aus der MAC abgeleitet und damit deterministisch. Fehlt
# sie, zeigen alle Mango-Weiterleitungen ins Leere.
try 15 "$SSH_PINIX 'ip -6 addr show dev end0 | grep -q \"${PINIX_ULA}\" && echo \"${PINIX_ULA} vorhanden\" || echo \"FEHLT: ${PINIX_ULA}\"'"
say 'pinix: IPv6-Default-Route per RA (der dhcpcd/noipv6rs-Fallstrick)'
try 15 "$SSH_PINIX 'ip -6 route show default | sed \"s/expires [0-9]*sec/expires N/\" || echo KEINE'"
say 'pinix: Resolver (muss Mango bleiben, nicht localhost)'
try 15 "$SSH_PINIX 'grep nameserver /etc/resolv.conf'"
say 'pinix: BIND antwortet autoritativ ueber ::1'
try 20 "$SSH_PINIX 'H=\$(ls -d /nix/store/*-bind-*-host/bin/host 2>/dev/null | head -1); \$H -t SOA 0ff.space ::1 | grep \"has SOA\"'"
say 'Mango: oeffentliche Adresse auf usb0'
try 15 "$SSH_MANGO 'ip -6 addr show dev usb0 scope global | awk \"/inet6/{print \\\$2}\"'"
say 'Mango: Weiterleitungen persistent vorhanden?'
try 15 "$SSH_MANGO 'for r in pinix_web_80 pinix_web_443 pinix_web_8080 pinix_ssh pinix_dns; do printf \"%s=%s\n\" \$r \"\$(uci -q get firewall.\$r.target || echo FEHLT)\"; done'"
say 'Mango: DNAT-Trefferzaehler (nur Existenz der Regeln, Zahlen driften)'
try 15 "$SSH_MANGO 'nft list chain inet fw4 dstnat_wan6 2>/dev/null | grep -oE \"dport [0-9]+\" | sort -u'"
say "Cloudflare-AAAA von ${EXT_NAME} gegen tatsaechliche Adresse"
# mango-dns fuehrt nur diesen einen Eintrag nach. Weichen die beiden
# nach einem Reconnect dauerhaft ab, hat der Timer sein Ziel verfehlt.
try 15 "host -t AAAA ${EXT_NAME} | awk '/has IPv6/{print \"AAAA:\", \$NF}'"
try 20 "$SSH_PINIX 'curl -6 --fail --silent --max-time 12 https://api6.ipify.org' | sed 's/^/tatsaechlich: /'"
say "extern ueber ${EXT_IF}: Web und SSH"
SRC=$(ip -6 addr show dev "$EXT_IF" scope global 2>/dev/null |
awk '/inet6 2a0/{split($2,a,"/"); print a[1]; exit}')
if [ -z "${SRC:-}" ]; then
echo "FEHLSCHLAG: keine globale IPv6 auf ${EXT_IF} -- externer Test nicht moeglich"
else
echo "Quelladresse: ${SRC}"
try 25 "curl -6 -s --interface ${EXT_IF} -o /dev/null -w 'https=%{http_code}\n' --max-time 18 https://${EXT_NAME}/"
try 20 "curl -6 -s --interface ${EXT_IF} -o /dev/null -w 'http=%{http_code}\n' --max-time 15 http://${EXT_NAME}/"
try 15 "nc -6 -s ${SRC} -z -w 8 ${EXT_NAME} 2222 >/dev/null 2>&1 && echo 'ssh2222=offen' || echo 'ssh2222=zu'"
say "extern ueber ${EXT_IF}: DNS (erst nach configure-mango-dns.sh sinnvoll)"
try 20 "host -W 8 -t SOA 0ff.space ${SRC%%/*} >/dev/null 2>&1; A=\$(host -t AAAA ${EXT_NAME} | awk '/has IPv6/{print \$NF; exit}'); host -W 8 -t SOA 0ff.space \$A | grep -E 'has SOA|not found|timed out' | head -2"
fi
say 'Zertifikat: Restlaufzeit (ACME-Zustand hat den Reboot ueberlebt?)'
try 25 "echo | timeout 15 openssl s_client -6 -connect [${PINIX_ULA}]:443 -servername ${EXT_NAME} 2>/dev/null | openssl x509 -noout -enddate -issuer"
printf '\n# Ende Probe "%s"\n' "$LABEL"