#!/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"