Ctrl K
ComunidadGuías…

deploy/caddy-guard.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/bin/bash
# caddy-guard — gardien du Caddyfile du VPS.
# Chaque minute : caddy validate. Si KO -> restauration du dernier snapshot
# valide (écriture in-place, inode préservée) + reload + alerte Vektor.
# Jamais de mv/rename sur le Caddyfile (bind-mount fichier, piège inode).
set -u
BASE=/opt/caddy
CF="$BASE/Caddyfile"
SNAPDIR="$BASE/guard-snapshots"
LOG=/var/log/caddy-guard.log
INCIDENT=/var/lib/caddy-guard/incident

ts() { date '+%F %T'; }
log() { echo "$(ts) $*" >> "$LOG"; }

validate() { docker exec caddy-caddy-1 caddy validate --config /etc/caddy/Caddyfile >/dev/null 2>&1; }

post_vektor_alert() {
  ip=$(docker inspect vektor-api --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 2>/dev/null)
  secret=$(grep -E '^VEKTOR_SELFHEAL_SECRET=' /opt/vektor/.env 2>/dev/null | head -1 | cut -d= -f2-)
  [ -n "$ip" ] && [ -n "$secret" ] || return 0
  curl -s -m 30 -X POST "http://$ip:8000/api/webhook/grafana" \
    -H "Content-Type: application/json" -H "X-Vektor-Selfheal: $secret" \
    -d "{\"alerts\":[{\"fingerprint\":\"caddy-guard-restore\",\"title\":\"Caddyfile corrompu restauré automatiquement par caddy-guard\",\"status\":\"firing\",\"labels\":{\"severity\":\"critical\"}}]}" \
    >/dev/null 2>&1
}

if validate; then
  mkdir -p "$SNAPDIR"
  latest=$(ls -1t "$SNAPDIR"/snap-*.conf 2>/dev/null | head -1)
  # snapshot si aucun, ou si le fichier a changé depuis le dernier snapshot
  if [ -z "$latest" ] || ! cmp -s "$CF" "$latest"; then
    cp "$CF" "$SNAPDIR/snap-$(date +%Y%m%d%H%M%S).conf"
    ls -1t "$SNAPDIR"/snap-*.conf | tail -n +13 | xargs -r rm -f
    log "snapshot de config valide pris"
  fi
  if [ -f "$INCIDENT" ]; then
    log "config redevenue valide — incident caddy-guard clos"
    rm -f "$INCIDENT"
    printf '{"active": false}\n' > "$BASE/config/maintenance/incident.json"
    # résolution côté Vektor
    ip=$(docker inspect vektor-api --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 2>/dev/null)
    secret=$(grep -E '^VEKTOR_SELFHEAL_SECRET=' /opt/vektor/.env 2>/dev/null | head -1 | cut -d= -f2-)
    if [ -n "$ip" ] && [ -n "$secret" ]; then
      curl -s -m 30 -X POST "http://$ip:8000/api/webhook/grafana" \
        -H "Content-Type: application/json" -H "X-Vektor-Selfheal: $secret" \
        -d '{"alerts":[{"fingerprint":"caddy-guard-restore","title":"Caddyfile corrompu restauré automatiquement par caddy-guard","status":"resolved","labels":{"severity":"critical"}}]}' \
        >/dev/null 2>&1
    fi
  fi
  exit 0
fi

# --- validate KO : restauration ---
latest=$(ls -1t "$SNAPDIR"/snap-*.conf 2>/dev/null | head -1)
if [ -z "$latest" ]; then
  log "VALIDATE KO et AUCUN snapshot — intervention manuelle requise"
  exit 1
fi
log "VALIDATE KO — restauration depuis $latest"
cat "$latest" > "$CF"
if ! validate; then
  log "le snapshot restauré est lui aussi KO — NON reload, intervention manuelle"
  exit 1
fi
docker exec caddy-caddy-1 caddy reload --config /etc/caddy/Caddyfile >/dev/null 2>&1 \
  && log "reload OK après restauration" || log "reload en échec après restauration"
printf '{"active": true, "title": "Config reverse-proxy restaurée automatiquement", "message": "Le Caddyfile est devenu invalide ; un snapshot valide a été remis en place par le gardien.", "since": "%s"}\n' "$(ts)" \
  > "$BASE/config/maintenance/incident.json"
touch "$INCIDENT"
post_vektor_alert