Create the file the import needs, instead of writing that something else does

The comment in Caddyfile.example said install-agent.sh creates
/etc/caddy/clupilot-proxy-hosts.conf before anything imports it. It did not. I
wrote the claim and never built the thing, and the release notes repeated it as
an instruction.

Following that instruction would have taken the site down. An import of a
missing file makes the WHOLE Caddy configuration invalid — app.clupilot.com,
the portal, everything — and the operator would have been holding a step I told
them to take.

So the installer now does both halves itself: it creates the file, and it adds
the import line, then runs `caddy validate` and takes the line straight back out
if Caddy rejects it. Doing it in a script rather than in a comment is the
point — an instruction gets followed in the wrong order, a script does not.

And a second trap alongside it: a hostname written by hand in the Caddyfile is
now skipped when the generated file is rendered. Two site blocks for one name
make Caddy reject the entire configuration, so an operator who managed an
already-hand-written name in the console as well would have taken the public
site off the air with no visible connection between the two actions.

2037 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feature/host-bootstrap
nexxo 2026-07-30 23:35:13 +02:00
parent dac84f024a
commit a21278eb52
2 changed files with 61 additions and 5 deletions

View File

@ -99,13 +99,19 @@ status.example.com {
# sich auf den Server zu melden der Handgriff, der `files.clupilot.com`
# gekostet hat.
#
# Die Datei MUSS existieren, bevor diese Zeile sie importiert: ein Import ins
# Leere macht die ganze Proxy-Konfiguration ungültig, und das nimmt die
# öffentliche Website mit. `install-agent.sh` legt sie deshalb an, bevor es
# hierauf hinweist. Wer die Zeile von Hand einträgt, legt vorher an:
# BEIDES setzt `sudo bash deploy/install-agent.sh` selbst die Datei und die
# Import-Zeile unten. Von Hand eintragen muss man nichts, und man sollte es auch
# nicht: die Datei MUSS existieren, bevor irgendetwas sie importiert, denn ein
# Import ins Leere macht die GANZE Proxy-Konfiguration ungültig und nimmt die
# öffentliche Website mit. Eine Anleitung wird in der falschen Reihenfolge
# befolgt, ein Skript nicht.
#
# sudo touch /etc/caddy/clupilot-proxy-hosts.conf
# Der Installer prüft nach dem Eintragen mit `caddy validate` und nimmt die
# Zeile wieder heraus, wenn Caddy sie ablehnt.
#
# EIN NAME GEHÖRT AN EINE STELLE. Steht ein Hostname schon von Hand in dieser
# Datei, lässt die Erzeugung ihn aus zwei Blöcke für denselben Namen lehnt
# Caddy ab, und zwar die ganze Konfiguration.
import /etc/caddy/clupilot-proxy-hosts.conf
# ── Dateien zum Herunterladen ────────────────────────────────────────────────

View File

@ -107,6 +107,17 @@ case "${1:-}" in
[[ "$NAME" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$ ]] \
|| { echo "refusing hostname: $NAME" >&2; rm -f "$TMP"; exit 5; }
# Steht der Name schon von Hand im Caddyfile, wird er hier
# ausgelassen. Zwei Blöcke für denselben Hostnamen lehnt Caddy ab —
# und zwar die GANZE Konfiguration, nicht nur den doppelten Block.
# Ein Betreiber, der einen bereits eingetragenen Namen zusätzlich in
# der Konsole pflegt, nähme sich damit die öffentliche Website vom
# Netz, ohne einen Zusammenhang zu sehen.
if grep -qE "^[[:space:]]*(https?://)?${NAME//./\\.}([[:space:],{]|\$)" "$CADDYFILE"; then
echo " skipping $NAME — already written by hand in $CADDYFILE" >&2
continue
fi
{
echo ""
echo "$NAME {"
@ -482,3 +493,42 @@ systemctl enable --now clupilot-update-agent.path >/dev/null
systemctl start clupilot-update-agent.service || true
echo "Update agent installed and running (clupilot-update-agent.timer)."
# ── Die von der Konsole verwalteten Hostnamen ────────────────────────────────
# Die Datei MUSS existieren, bevor irgendetwas sie importiert: ein Import ins
# Leere macht die GANZE Proxy-Konfiguration ungültig, und das nimmt die
# öffentliche Website mit. Genau diese Reihenfolge ist der Grund, warum das hier
# steht und nicht in einer Anleitung — eine Anleitung wird in der falschen
# Reihenfolge befolgt, ein Skript nicht.
PROXY_HOSTS_FILE="/etc/caddy/clupilot-proxy-hosts.conf"
if [[ -f "$CADDYFILE" ]] && command -v caddy >/dev/null 2>&1; then
if [[ ! -f "$PROXY_HOSTS_FILE" ]]; then
printf '# Von CluPilot erzeugt. Noch leer — die Konsole füllt sie.\n' > "$PROXY_HOSTS_FILE"
chown root:root "$PROXY_HOSTS_FILE"
chmod 0644 "$PROXY_HOSTS_FILE"
echo " Created $PROXY_HOSTS_FILE"
fi
# Die Import-Zeile wird gesetzt, nicht angesagt. Sie von Hand nachtragen zu
# lassen war der Fehler: die Anleitung dazu stand in einem Kommentar, die
# Datei wurde nie angelegt, und wer der Anleitung folgte, hätte den ganzen
# Proxy ungültig gemacht.
if ! grep -q "clupilot-proxy-hosts.conf" "$CADDYFILE"; then
cp -p "$CADDYFILE" "$CADDYFILE.clupilot-backup"
printf '\n# Von CluPilot verwaltete Hostnamen (Konsole → Betrieb → Hostnamen).\nimport %s\n' \
"$PROXY_HOSTS_FILE" >> "$CADDYFILE"
if caddy validate --config "$CADDYFILE" >/dev/null 2>&1; then
rm -f "$CADDYFILE.clupilot-backup"
systemctl reload caddy >/dev/null 2>&1 || true
echo " Added the import line to $CADDYFILE"
else
# Zurück, sofort. Eine ungültige Konfiguration liegen zu lassen
# heißt, den nächsten Caddy-Start scheitern zu lassen — und der
# kommt beim nächsten Neustart der Maschine, nicht jetzt.
mv -f "$CADDYFILE.clupilot-backup" "$CADDYFILE"
echo " ! caddy rejected the import line — it was taken back out again."
fi
fi
fi