Dos cosas que el ensayo del guion destapó, las dos medidas: 1. `repo-perfil.sh` NO acotaba el build. `pack --build` es instantáneo con cache-hit y una compilación entera si el artefacto no está sellado, así que «publicá el repo» se convertía en silencio en «ponete a compilar el perfil»: la corrida se puso a moler `libnftnl` en una caja de 4 cores que ya estaba a load 46, y después venía `rust`. `build-repo.sh` llevaba `timeout` desde siempre; el que se corre en producción, no. Ahora BUILD_TIMEOUT (120 s) y SKIP_UNSEALED=1, con el vencimiento tratado como «no estaba sellado» y no como error. Resultado del perfil entero: 173 anclados, 0 sin ancla, 2 saltados (os-release, rust), sin compilar nada. 2. `--install-vhost` pone el bloque de Caddy sin que haya que editar a mano, pero el orden es lo único que lo vuelve seguro: copia → añade → `caddy validate` → SÓLO ENTONCES `reload`. Si validate o reload fallan, restaura la copia y recarga con ella. Un reload con la config rota no tira el sitio nuevo: tira los 19 de la caja. Probado en los dos caminos contra un Caddy de juguete (el de la caja no tiene el admin abierto): con reload bueno, el admin muestra el server nuevo sirviendo el repo y el sitio previo intacto; con reload fallido, el Caddyfile vuelve byte a byte y no queda rastro del bloque. Y `repo-perfil.sh` pasa a respetar TAKANA del entorno: la guarda de publicar-repo.sh comprobaba un binario y se publicaba con otro, que es peor que no tener guarda.
166 lines
7.9 KiB
Bash
Executable File
166 lines
7.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# publicar-repo.sh — deja el repo FIRMADO servido en `repo.gioser.net`. Se corre EN LA CAJA, como
|
|
# root (la clave privada de release vive en `/root/.config/takana/keys/`, SDD 28 §2116).
|
|
#
|
|
# ── QUÉ HACE ────────────────────────────────────────────────────────────────────────────────────
|
|
# 1. publica la clausura del perfil en `/srv/repo` con `repo-perfil.sh` (que ABORTA si no está la
|
|
# clave: un índice firmado con una efímera que nadie conoce no lo verifica nadie),
|
|
# 2. comprueba que el índice verifica contra `trust/`,
|
|
# 3. dice si Caddy ya sirve el nombre, y **si no, imprime el bloque y para**.
|
|
#
|
|
# ── EL CADDYFILE: SÓLO CON `--install-vhost`, Y CON VUELTA ATRÁS ───────────────────────────────
|
|
# Por defecto NO lo toca: imprime el bloque y para. En esta caja Caddy sirve 19 dominios y su
|
|
# Caddyfile ya acumula 20 `.bak` (SDD 28 §1); un guion que lo reescribe a ciegas es cómo se tiran
|
|
# 19 sitios para levantar uno.
|
|
#
|
|
# repo.gioser.net {
|
|
# root * /srv/repo
|
|
# file_server browse
|
|
# }
|
|
#
|
|
# Con `--install-vhost` lo hace, pero **el orden importa y es lo único que lo vuelve seguro**:
|
|
# copia de seguridad → añade → `caddy validate` → sólo entonces `caddy reload`. Si validate o
|
|
# reload fallan, **restaura la copia y recarga con ella** antes de salir: el estado final es el que
|
|
# había, no uno a medias. Y si el nombre ya está en la config, no añade nada (idempotente).
|
|
#
|
|
# El DNS ya está: `repo` A → 2.29.29.217, ttl 60, en la zona de Hetzner (alta del 2026-09-21).
|
|
#
|
|
# ⚠ **Un solo origen es exactamente lo que el ADR 0014 existe para no tener.** Esta caja sirviéndose
|
|
# a sí misma está bien como camino normal, pero el `--repo` de los clientes debería llevar también
|
|
# el Storage Box: el primer origen roto, si es el único, es el último.
|
|
#
|
|
# Uso: sudo scripts/servidor/publicar-repo.sh [perfil]
|
|
# sudo scripts/servidor/publicar-repo.sh --install-vhost [perfil]
|
|
# Env: PERFIL (def servidor) · STORE (def /store) · DESTINO (def /srv/repo)
|
|
# NOMBRE (def repo.gioser.net) · TRUST (def ./trust) · CADDYFILE (def /etc/caddy/Caddyfile)
|
|
set -eu
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
|
|
|
INSTALL_VHOST=0
|
|
case "${1:-}" in --install-vhost) INSTALL_VHOST=1; shift ;; esac
|
|
|
|
PERFIL="${1:-${PERFIL:-servidor}}"
|
|
STORE="${STORE:-/store}"
|
|
DESTINO="${DESTINO:-/srv/repo}"
|
|
NOMBRE="${NOMBRE:-repo.gioser.net}"
|
|
TRUST="${TRUST:-$ROOT/trust}"
|
|
TAKANA="${TAKANA:-$ROOT/target/release/takana}"
|
|
CADDYFILE="${CADDYFILE:-/etc/caddy/Caddyfile}"
|
|
|
|
# La guarda pregunta por lo que de verdad hace falta —poder LEER la clave— y no por `id -u == 0`,
|
|
# que es sólo su causa habitual: así el guion se puede ensayar contra un repo y una clave de
|
|
# prueba sin ser root, y el día que la clave viva en otro sitio no hay que tocar la condición.
|
|
KEY="${KEY:-$HOME/.config/takana/keys/release.ed25519}"
|
|
[ -r "$KEY" ] || {
|
|
echo "!! no puedo leer la clave de release: $KEY" >&2
|
|
echo " En la caja vive en /root/.config/takana/keys/ ⇒ esto va como root (SDD 28 §2116)." >&2
|
|
exit 2
|
|
}
|
|
export KEY
|
|
[ -x "$TAKANA" ] || { echo "!! falta $TAKANA (cargo build --release)" >&2; exit 2; }
|
|
|
|
# ⚠ El binario TIENE que traer el arreglo de los parches múltiples y del target_bin (SDD 28 §5.5):
|
|
# con uno anterior, las 16 recetas con ≥2 parches se publican irreproducibles y 8 paquetes más
|
|
# (bash, sed, tar, parted, dhcpcd, squid, wpa_supplicant, zsh) prometen un binario que no existe.
|
|
"$TAKANA" outdated --help >/dev/null 2>&1 || {
|
|
echo "!! este $TAKANA es anterior al arreglo del SDD 28 §5.5 (no conoce 'outdated')." >&2
|
|
echo " Reconstruilo —cargo build --release— antes de publicar, o el repo sale roto." >&2
|
|
exit 2
|
|
}
|
|
|
|
echo "==> publicando el perfil '$PERFIL' en $DESTINO"
|
|
install -d -m 755 "$DESTINO"
|
|
PERFIL="$PERFIL" STORE="$STORE" REPO="$DESTINO" TRUST="$TRUST" TAKANA="$TAKANA" \
|
|
"$ROOT/scripts/repo-perfil.sh" "$PERFIL"
|
|
|
|
if [ "$INSTALL_VHOST" = "1" ]; then
|
|
echo
|
|
echo "==> vhost de Caddy en $CADDYFILE"
|
|
[ -w "$CADDYFILE" ] || { echo "!! no puedo escribir $CADDYFILE" >&2; exit 2; }
|
|
command -v caddy >/dev/null || { echo "!! no encuentro el binario 'caddy' en el PATH" >&2; exit 2; }
|
|
|
|
if grep -qE "^[[:space:]]*(https?://)?$NOMBRE[[:space:],{]" "$CADDYFILE"; then
|
|
echo " = $NOMBRE ya está en la config; no toco nada"
|
|
else
|
|
COPIA="$CADDYFILE.antes-de-$NOMBRE-$(date -u +%Y%m%dT%H%M%SZ)"
|
|
cp -p "$CADDYFILE" "$COPIA"
|
|
echo " copia de seguridad: $COPIA"
|
|
|
|
# Un site block va al FINAL: las opciones globales de Caddy tienen que ser lo primero del
|
|
# fichero, así que añadir al final nunca las invalida.
|
|
cat >> "$CADDYFILE" <<EOF
|
|
|
|
# repo de paquetes takana — SDD 28 §5.7. Lo publica scripts/servidor/publicar-repo.sh.
|
|
$NOMBRE {
|
|
root * $DESTINO
|
|
file_server browse
|
|
}
|
|
EOF
|
|
|
|
# ⚠ VALIDAR ANTES DE RECARGAR. Un reload con la config rota se lleva los 19 dominios de la
|
|
# caja, no sólo el que estoy añadiendo. Si no valida, vuelvo atrás y ni recargo.
|
|
if ! caddy validate --config "$CADDYFILE" >/dev/null 2>&1; then
|
|
cp -p "$COPIA" "$CADDYFILE"
|
|
echo "!! la config NO valida con el bloque añadido — restaurada $COPIA, no recargué" >&2
|
|
caddy validate --config "$CADDYFILE" >/dev/null 2>&1 \
|
|
|| echo "!! y la ORIGINAL tampoco valida: eso ya venía roto, miralo antes de recargar" >&2
|
|
exit 1
|
|
fi
|
|
echo " ✓ la config valida"
|
|
|
|
if ! caddy reload --config "$CADDYFILE"; then
|
|
# El reload falló: Caddy sigue con lo que tenía cargado, pero el fichero en disco ya no
|
|
# es eso. Se restaura para que el próximo arranque no levante una config que no probó
|
|
# nadie, y se reintenta el reload con la buena.
|
|
cp -p "$COPIA" "$CADDYFILE"
|
|
caddy reload --config "$CADDYFILE" >/dev/null 2>&1 || true
|
|
echo "!! el reload falló — restaurada $COPIA y recargada" >&2
|
|
exit 1
|
|
fi
|
|
echo " ✓ recargado"
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
echo "==> ¿lo sirve Caddy?"
|
|
if curl -fsS -m 10 "https://$NOMBRE/index.json" -o /dev/null 2>/dev/null; then
|
|
echo " ✓ https://$NOMBRE/index.json responde"
|
|
echo
|
|
echo " probalo desde cualquier hub (necesita el lab: install reproduce desde fuente):"
|
|
echo " takana install zsh --repo https://$NOMBRE --trust ./trust --require-signed"
|
|
echo " y el aviso periódico:"
|
|
echo " REPO=https://$NOMBRE scripts/servidor/avisar-actualizaciones.sh"
|
|
else
|
|
if [ "$INSTALL_VHOST" = "1" ]; then
|
|
# Decir «falta el vhost» justo después de haberlo puesto es la clase de mensaje que manda a
|
|
# buscar el problema donde no está.
|
|
cat <<EOF
|
|
✗ el vhost está puesto y Caddy recargado, pero https://$NOMBRE todavía no responde.
|
|
Lo normal a mirar, en este orden:
|
|
· el CERTIFICADO: Caddy lo pide en la primera petición y tarda unos segundos; si falla,
|
|
`journalctl -u caddy` (o el log de arje) lo dice — casi siempre es el :80 tapado o el DNS.
|
|
· el DNS: $NOMBRE tiene que resolver a ESTA caja (hoy: A → 2.29.29.217).
|
|
· el repo: $DESTINO tiene que tener el index.json que acaba de firmarse.
|
|
EOF
|
|
exit 1
|
|
fi
|
|
cat <<EOF
|
|
✗ https://$NOMBRE todavía no responde. El repo YA está publicado y firmado en $DESTINO;
|
|
lo que falta es el vhost. O lo pone este mismo guion:
|
|
|
|
sudo $0 --install-vhost $PERFIL
|
|
|
|
(copia de seguridad → añade → valida → recarga, y si algo falla vuelve atrás)
|
|
o a mano, en $CADDYFILE:
|
|
|
|
$NOMBRE {
|
|
root * $DESTINO
|
|
file_server browse
|
|
}
|
|
|
|
y recargá: caddy reload --config $CADDYFILE
|
|
Después volvé a correr este guion: comprueba el lazo entero en vez de darlo por hecho.
|
|
EOF
|
|
exit 1
|
|
fi
|