diff --git a/scripts/prune-acme-stale.sh b/scripts/prune-acme-stale.sh new file mode 100755 index 0000000..b13086d --- /dev/null +++ b/scripts/prune-acme-stale.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# Rimuove certificati stale da acme.json di Traefik (domini non più serviti). +# Idempotente e atomico: stop Traefik -> backup -> filtro+valida -> ripristino +# perms/owner (600 root:adriano) -> restart Traefik. +# +# Uso: sudo bash scripts/prune-acme-stale.sh +set -euo pipefail + +ACME="/opt/docker/traefik/acme.json" +CONTAINER="traefik-traefik-1" +# Domini da rimuovere (match esatto su .domain.main). Estendibile. +STALE=("mattermost.tielogic.xyz" "zar.tielogic.xyz" "registry.tielogic.xyz") + +if [[ $EUID -ne 0 ]]; then + echo "Deve girare come root (usa: sudo bash $0)" >&2 + exit 1 +fi +[[ -f "$ACME" ]] || { echo "Non trovo $ACME" >&2; exit 1; } + +ts="$(date +%Y%m%d-%H%M%S)" +backup="${ACME}.bak-${ts}" +cp -p "$ACME" "$backup" +echo "Backup: $backup" + +echo "=== Domini attualmente in storage ===" +python3 - "$ACME" <<'PY' +import json,sys +d=json.load(open(sys.argv[1])) +for res,blk in d.items(): + for c in (blk or {}).get("Certificates") or []: + print(f" [{res}] {c.get('domain',{}).get('main')}") +PY + +echo "Stop $CONTAINER (evita riscrittura concorrente)…" +docker stop "$CONTAINER" >/dev/null + +# Filtro in Python: rimuove i cert il cui domain.main è nella lista STALE. +removed=$(python3 - "$ACME" "${STALE[@]}" <<'PY' +import json,sys,os,tempfile +path=sys.argv[1]; stale=set(sys.argv[2:]) +d=json.load(open(path)) +removed=[] +for res,blk in d.items(): + certs=(blk or {}).get("Certificates") + if not certs: continue + kept=[] + for c in certs: + main=c.get("domain",{}).get("main") + if main in stale: removed.append(main) + else: kept.append(c) + blk["Certificates"]=kept +# scrittura atomica nella stessa dir, poi os.replace +dir_=os.path.dirname(path) +fd,tmp=tempfile.mkstemp(dir=dir_) +with os.fdopen(fd,"w") as f: + json.dump(d,f,indent=2) # ri-serializza valido +os.replace(tmp,path) +print("\n".join(removed)) +PY +) + +# Ripristina perms/owner che Traefik esige (600) e ownership originale. +chmod 600 "$ACME" +chown root:adriano "$ACME" + +# Validazione finale: deve essere JSON valido (altrimenti rollback dal backup). +if ! python3 -c "import json,sys; json.load(open('$ACME'))" 2>/dev/null; then + echo "JSON risultante NON valido — rollback dal backup." >&2 + cp -p "$backup" "$ACME"; chmod 600 "$ACME"; chown root:adriano "$ACME" + docker start "$CONTAINER" >/dev/null + exit 1 +fi + +echo "Restart $CONTAINER…" +docker start "$CONTAINER" >/dev/null + +echo "=== Rimossi ===" +if [[ -n "${removed//[$'\n ']/}" ]]; then echo "$removed" | sed 's/^/ /'; else echo " (nessuno — già puliti)"; fi +echo "=== Permessi finali ===" +ls -la "$ACME" +echo "Fatto. Se qualcosa va storto, ripristina: sudo cp -p $backup $ACME && sudo chmod 600 $ACME && docker restart $CONTAINER"