Join the tunnel, then tell the console everything so far
The three interface states come over from ConfigureWireguard.php exactly as that file works them out, because the one line they replace got two of them wrong. `systemctl enable --now wg-quick@wg0 || wg-quick up wg0` brought the interface up via its fallback WITHOUT the systemd enablement, so the tunnel did not come back after a reboot; and on the next attempt both halves failed with "wg0 already exists", so it retried forever against a tunnel that was working. Enable before start, because enable is the half that survives a reboot, and it is repaired even when the interface is already up by other means. The configuration is compared before it is written. The file used to be rewritten unconditionally with nothing reloading it, so correcting a wrong hub key showed a new file and identical behaviour — the running interface still held the old peer. Knowing whether it CHANGED is what lets an unchanged, working tunnel be left alone and a corrected one actually be applied. A change means a restart rather than `wg syncconf`, because syncconf applies peers only and a corrected Address or AllowedIPs would silently do nothing. AllowedIPs is computed, not copied. Writing the host's own address there would let the tunnel handshake and leave every other participant unreachable — a fault that looks like a routing problem somewhere else entirely. The handshake target is derived from --api rather than taking its own argument. CluPilot's tunnel address is already in there, and a second value meaning the same thing is a second value that eventually disagrees with the first. The tunnel counts only when it has been PROVEN, never when the file is on disk. A stored tunnel address without proof made every later connection attempt useless and the recovery was hand-editing the database. Once it is proven, flush_reports runs and the whole history from the rescue system onwards reaches the console carrying its original timestamps. Verified without hardware: dash-clean; the subnet arithmetic is right on octet boundaries and off them (192.168.5.130/25 gives 192.168.5.128/25, 172.16.4.9/12 gives 172.16.0.0/12); the API host parser handles a bare address, a scheme, a port and a path; and wg0.conf renders with AllowedIPs on the network rather than the host. Step 2 unticked — it wants all five earlier sections showing up in the console with real timestamps, and that needs a hub. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>feature/host-bootstrap
parent
9e485efe87
commit
c345f21e7d
|
|
@ -689,6 +689,52 @@ section_network_bridged() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Abschnitt 6: wireguard_joined — ab hier ist CluPilot erreichbar
|
||||
# ---------------------------------------------------------------------------
|
||||
#
|
||||
# Der erste Abschnitt, nach dem es einen Rückweg gibt. Deshalb endet er mit
|
||||
# `flush_reports`: alles, was seit dem Rettungssystem liegengeblieben ist, geht
|
||||
# jetzt raus — mit den Zeitstempeln von damals.
|
||||
section_wireguard_joined() {
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
if ! apt-get install -y wireguard >> "${CLUPILOT_WORK_DIR}/apt.log" 2>&1; then
|
||||
CLUPILOT_SECTION_NOTE='wireguard ließ sich nicht installieren'
|
||||
return 1
|
||||
fi
|
||||
|
||||
mkdir -p /etc/wireguard
|
||||
chmod 700 /etc/wireguard
|
||||
|
||||
_desired="$(render_wireguard_config)"
|
||||
_current="$(cat /etc/wireguard/wg0.conf 2>/dev/null || true)"
|
||||
|
||||
# Vor dem Schreiben vergleichen. Die Datei wurde früher bedingungslos neu
|
||||
# geschrieben, ohne dass irgendetwas sie neu einlas — wer einen falschen
|
||||
# Hub-Schlüssel korrigierte, sah die neue Datei und kein geändertes
|
||||
# Verhalten, weil die laufende Schnittstelle den alten Peer behielt.
|
||||
_changed=0
|
||||
if [ "$_desired" != "$_current" ]; then
|
||||
_changed=1
|
||||
umask 077
|
||||
printf '%s\n' "$_desired" > /etc/wireguard/wg0.conf
|
||||
chmod 600 /etc/wireguard/wg0.conf
|
||||
fi
|
||||
|
||||
if ! bring_up_wireguard "$_changed"; then
|
||||
CLUPILOT_SECTION_NOTE='wg0 kam nicht in den Zustand, den die Übernahme braucht'
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! wireguard_handshake_proven 10; then
|
||||
CLUPILOT_SECTION_NOTE="kein Handshake mit $(api_host) über den Tunnel. Die Datei steht, der Tunnel nicht — und eine geschriebene Datei ist kein Tunnel."
|
||||
return 1
|
||||
fi
|
||||
|
||||
CLUPILOT_SECTION_NOTE="Handshake mit $(api_host) bewiesen, wg0 systemd-freigegeben"
|
||||
return 0
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_arguments "$@"
|
||||
|
||||
|
|
@ -734,7 +780,13 @@ main() {
|
|||
die 'Abbruch bei der Netzumstellung.'
|
||||
fi
|
||||
|
||||
# run_section wireguard_joined section_wireguard_joined # Task 6
|
||||
if ! run_section wireguard_joined section_wireguard_joined; then
|
||||
die 'Abbruch: ohne Tunnel gibt es keinen Rückweg zu CluPilot.'
|
||||
fi
|
||||
|
||||
# Ab hier existiert ein Weg. Die ganze Vorgeschichte geht jetzt raus.
|
||||
flush_reports
|
||||
|
||||
# run_section traefik_running section_traefik_running # Task 7
|
||||
# run_section template_built section_template_built # Task 8
|
||||
# run_section registered section_registered # Task 9
|
||||
|
|
|
|||
|
|
@ -253,6 +253,125 @@ EOF
|
|||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# WireGuard
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Netzadresse zu einer Adresse mit Präfix: 10.66.0.11/24 -> 10.66.0.0/24
|
||||
#
|
||||
# Gerechnet statt geraten. `AllowedIPs` mit der Wirtsadresse statt der
|
||||
# Netzadresse ließe den Tunnel handshaken und danach jeden anderen Teilnehmer
|
||||
# unerreichbar — ein Fehler, der aussieht wie ein Routingproblem irgendwo sonst.
|
||||
ipv4_network() {
|
||||
_ip="${1%%/*}"
|
||||
_prefix="${1##*/}"
|
||||
|
||||
IFS=. read -r _a _b _c _d <<EOF
|
||||
$_ip
|
||||
EOF
|
||||
|
||||
_num=$(( (_a << 24) + (_b << 16) + (_c << 8) + _d ))
|
||||
_mask=$(( (0xFFFFFFFF << (32 - _prefix)) & 0xFFFFFFFF ))
|
||||
_net=$(( _num & _mask ))
|
||||
|
||||
printf '%d.%d.%d.%d/%d' \
|
||||
$(( (_net >> 24) & 255 )) $(( (_net >> 16) & 255 )) \
|
||||
$(( (_net >> 8) & 255 )) $(( _net & 255 )) "$_prefix"
|
||||
}
|
||||
|
||||
# Die Tunneladresse von CluPilot — dieselbe, die `--api` trägt.
|
||||
#
|
||||
# Sie ist auch das Ziel für den Handshake-Beweis: wenn CluPilot über den Tunnel
|
||||
# antwortet, steht der Tunnel. Ein eigener Parameter dafür wäre ein zweiter Wert,
|
||||
# der dasselbe meint und irgendwann auseinanderläuft.
|
||||
api_host() {
|
||||
printf '%s' "$CLUPILOT_API" | sed -e 's#^[a-z]*://##' -e 's#[:/].*##'
|
||||
}
|
||||
|
||||
render_wireguard_config() {
|
||||
_subnet="$(ipv4_network "$CLUPILOT_WG_IP")"
|
||||
|
||||
cat <<EOF
|
||||
[Interface]
|
||||
Address = ${CLUPILOT_WG_IP}
|
||||
PrivateKey = ${CLUPILOT_WG_PRIVATE}
|
||||
|
||||
[Peer]
|
||||
PublicKey = ${CLUPILOT_HUB_PUBKEY}
|
||||
Endpoint = ${CLUPILOT_HUB_ENDPOINT}
|
||||
AllowedIPs = ${_subnet}
|
||||
PersistentKeepalive = 25
|
||||
EOF
|
||||
}
|
||||
|
||||
# Bringt wg0 in den Zustand, den die Übernahme braucht: jetzt oben UND nach
|
||||
# einem Neustart oben, mit der Konfiguration, die gerade geschrieben wurde.
|
||||
#
|
||||
# Drei Zustände, getrennt behandelt, weil die eine Zeile, die das hier ersetzt —
|
||||
# `systemctl enable --now wg-quick@wg0 || wg-quick up wg0` — zwei davon falsch
|
||||
# machte: der Rückfall brachte die Schnittstelle hoch OHNE die systemd-Freigabe,
|
||||
# also kam der Tunnel nach dem Neustart nicht wieder; und beim nächsten Versuch
|
||||
# scheiterten beide Hälften an „wg0 already exists", also lief der Schritt ewig
|
||||
# gegen einen Tunnel, der in Wahrheit funktionierte.
|
||||
#
|
||||
# **enable vor start**: `enable` ist das, was den Neustart überlebt, und es wird
|
||||
# auch dann geprüft und nachgeholt, wenn die Schnittstelle schon anderweitig
|
||||
# oben ist.
|
||||
bring_up_wireguard() {
|
||||
_config_changed="$1"
|
||||
_unit='wg-quick@wg0'
|
||||
|
||||
_present=0
|
||||
ip link show wg0 >/dev/null 2>&1 && _present=1
|
||||
|
||||
# `is-enabled` wird nach seinem Rückgabewert gefragt, nicht nach seinem
|
||||
# Wortlaut: eine nie freigegebene Template-Unit („disabled", 1) und eine, die
|
||||
# es noch nicht gibt, werden damit gleich behandelt.
|
||||
if ! systemctl is-enabled --quiet "$_unit" 2>/dev/null; then
|
||||
if ! systemctl enable "$_unit" >/dev/null 2>&1; then
|
||||
log "konnte ${_unit} nicht freigeben — wg0 überlebte keinen Neustart"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$_present" -eq 0 ]; then
|
||||
systemctl start "$_unit" >/dev/null 2>&1 || { log 'wg0 ließ sich nicht starten'; return 1; }
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Vorhanden und unverändert: die laufende Schnittstelle passt schon zur
|
||||
# Datei. Hier neu zu starten ist das, was einen funktionierenden Tunnel
|
||||
# kaputt aussehen ließ.
|
||||
[ "$_config_changed" -eq 0 ] && return 0
|
||||
|
||||
# Vorhanden mit geänderter Konfiguration: Neustart, NICHT `wg syncconf`.
|
||||
# syncconf wendet nur Peers an; eine korrigierte `Address` oder `AllowedIPs`
|
||||
# bliebe still wirkungslos — genau die Sorte „die Datei hat sich geändert und
|
||||
# nichts ist passiert".
|
||||
systemctl restart "$_unit" >/dev/null 2>&1 || { log 'Neustart von wg0 mit der korrigierten Konfiguration fehlgeschlagen'; return 1; }
|
||||
return 0
|
||||
}
|
||||
|
||||
# Der Tunnel gilt erst mit BEWIESENEM Handshake, nicht mit geschriebener Datei.
|
||||
#
|
||||
# Eine gespeicherte Tunneladresse ohne Beweis machte jeden weiteren
|
||||
# Verbindungsversuch unbrauchbar, und die Rettung war Datenbank-Handarbeit.
|
||||
wireguard_handshake_proven() {
|
||||
_target="$(api_host)"
|
||||
[ -n "$_target" ] || return 1
|
||||
|
||||
_tries="${1:-10}"
|
||||
while [ "$_tries" -gt 0 ]; do
|
||||
if ping -c1 -W2 "$_target" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
_tries=$((_tries - 1))
|
||||
sleep 3
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Die Datacenter-Firewall
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ der Kunden-VMs wirkungslos, und `ConfigureProxmox` hielt das als eigenen Fund fe
|
|||
|
||||
### Task 6: `wireguard_joined` — ab hier ist CluPilot erreichbar
|
||||
|
||||
- [ ] **Step 1: Schreiben**
|
||||
- [x] **Step 1: Schreiben**
|
||||
|
||||
`wg0` aus den mitgegebenen Werten, **systemd-aktiviert** — lies
|
||||
`ConfigureWireguard.php` dazu: ein `||`-Rückfall verdeckte dort die fehlende
|
||||
|
|
@ -363,7 +363,7 @@ Sobald der Handshake steht: `flush_reports` — alles bis hierher wird nachgerei
|
|||
|
||||
- [ ] **Step 2: Auf echter Hardware prüfen.** In der Konsole müssen jetzt **alle
|
||||
fünf** bisherigen Abschnitte auftauchen, mit ihren echten Zeitstempeln.
|
||||
- [ ] **Step 3: Committen.** `Join the tunnel, then tell the console everything so far`
|
||||
- [x] **Step 3: Committen.** `Join the tunnel, then tell the console everything so far`
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue