141 lines
6.3 KiB
Bash
Executable File
141 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# 10-registry.sh — T-A.1 + T-A.5 : moitié technique du registre, GÉNÉRÉE, + détection de dérive.
|
|
#
|
|
# Lecture seule : `docker ps` / `docker inspect` distants, lecture des compose du kit.
|
|
# Aucun conteneur n'est démarré, arrêté, modifié. Aucun secret n'est lu ni affiché.
|
|
#
|
|
# Modes :
|
|
# ./10-registry.sh génère REGISTRE-BRIQUES.md
|
|
# ./10-registry.sh --check régénère en mémoire, compare, alerte si dérive (rc=1)
|
|
# ./10-registry.sh --check --alert idem + envoie le payload JSON dans le salon ops
|
|
#
|
|
# Sortie : REGISTRE-BRIQUES.md (moitié A, générée — ne pas éditer à la main)
|
|
# La moitié humaine (comptes, reset, responsables) est dans REGISTRE-IDENTITES.md.
|
|
|
|
set -euo pipefail
|
|
|
|
KIT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
OUT="$KIT/REGISTRE-BRIQUES.md"
|
|
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_openfox11}"
|
|
HOSTS="${HOSTS:-g9:folzo@192.168.1.24 j5040:folzo@192.168.1.27}"
|
|
RESTIC_COVERAGE="${RESTIC_COVERAGE:-g9=restic (sources /data /home/folzo /etc, cron 03:30) ; j5040=non (à couvrir Phase 5)}"
|
|
MODE="generate"
|
|
ALERT=0
|
|
for a in "$@"; do
|
|
[[ "$a" == "--check" ]] && MODE="check"
|
|
[[ "$a" == "--alert" ]] && ALERT=1
|
|
done
|
|
export SSH_KEY
|
|
|
|
TMP="$(mktemp -d)"
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
|
|
declared_json="$(python3 "$KIT/scripts/registry_inputs.py" declared)"
|
|
caddy_json="$(python3 "$KIT/scripts/registry_inputs.py" caddy)"
|
|
echo "$caddy_json" > "$TMP/caddy.json"
|
|
|
|
rows_all="$TMP/rows.jsonl"
|
|
findings_all="$TMP/findings.txt"
|
|
: > "$rows_all"
|
|
: > "$findings_all"
|
|
|
|
for entry in $HOSTS; do
|
|
machine="${entry%%:*}"
|
|
host="${entry#*:}"
|
|
mip="${host#*@}"
|
|
ssh -o ConnectTimeout=8 -o BatchMode=yes -i "$SSH_KEY" "$host" \
|
|
'docker ps -a --format json' > "$TMP/$machine.ps.json" 2>"$TMP/$machine.err" || {
|
|
echo "[registry] SSH/docker indisponible sur $machine ($host)" >&2
|
|
echo "$machine: inaccessible (SSH ou docker)" >> "$findings_all"
|
|
continue
|
|
}
|
|
ssh -o ConnectTimeout=8 -o BatchMode=yes -i "$SSH_KEY" "$host" \
|
|
'docker ps --format "{{.Names}}\t{{.Ports}}"' > "$TMP/$machine.ports.tsv" 2>/dev/null || : > "$TMP/$machine.ports.tsv"
|
|
python3 "$KIT/scripts/registry_inputs.py" endpoints "$TMP/$machine.ports.tsv" "$TMP/caddy.json" "$mip" > "$TMP/$machine.ep.json"
|
|
python3 "$KIT/scripts/registry_parse.py" "$machine" "$(cat "$TMP/$machine.ep.json")" "$declared_json" \
|
|
< "$TMP/$machine.ps.json" > "$TMP/$machine.rows.json" 2>"$TMP/$machine.findings"
|
|
python3 -c 'import json,sys
|
|
for r in json.load(open(sys.argv[1])):
|
|
print(json.dumps(r, ensure_ascii=False))' "$TMP/$machine.rows.json" >> "$rows_all"
|
|
sed -n 's/^#findings# //p' "$TMP/$machine.findings" | python3 -c 'import json,sys
|
|
for line in sys.stdin:
|
|
line=line.strip()
|
|
if line:
|
|
for f in json.loads(line): print(f)' >> "$findings_all"
|
|
done
|
|
|
|
nb_conteneurs="$(wc -l < "$rows_all" | tr -d ' ')"
|
|
nb_findings="$(grep -c . "$findings_all" || true)"
|
|
|
|
generated="$(python3 - "$rows_all" "$nb_conteneurs" <<'PY'
|
|
import json, sys, datetime
|
|
rows = [json.loads(l) for l in open(sys.argv[1]) if l.strip()]
|
|
ts = datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M UTC')
|
|
print(f"<!-- GÉNÉRÉ PAR scripts/10-registry.sh — ne pas éditer à la main. ts={ts} ; {sys.argv[2]} conteneurs -->")
|
|
print()
|
|
print("| Machine | Conteneur | Projet / service (compose) | Image | État | Épinglée | Watchtower | Type | Dans le kit | Endpoint (Caddy) |")
|
|
print("|---|---|---|---|---|---|---|---|---|---|")
|
|
for r in rows:
|
|
print(f"| {r['machine']} | `{r['name']}` | {r['project']} / {r['service']} | `{r['image']}` | {r['state']} | {r['pinned']} | {r['wt']} | {r['stateful']} | {r['kit']} | {r['endpoint'] or '—'} |")
|
|
PY
|
|
)"
|
|
|
|
render="$(cat <<EOF
|
|
# REGISTRE-BRIQUES — moitié technique (générée)
|
|
|
|
> Source : \`scripts/10-registry.sh\` (\`docker ps\` + \`docker inspect\` en **lecture seule** sur .24 et .27,
|
|
> compose du kit, Caddyfile pour les endpoints). **Ne pas éditer à la main** : la moitié humaine
|
|
> (comptes, 2FA, procédure de réinitialisation, responsable, break-glass) vit dans
|
|
> \`REGISTRE-IDENTITES.md\`.
|
|
>
|
|
> Périmètre (T-A.10) : **.24 (G9) et .27 (J5040) uniquement**. Le xpenology **.25 est hors périmètre** :
|
|
> il est décrit dans \`INVENTAIRE-STATEFUL.md\` et ses briques sont **à mourir en Phase 6**.
|
|
>
|
|
> Couverture backup : $RESTIC_COVERAGE
|
|
|
|
$generated
|
|
|
|
## Constats automatiques (détection de dérive)
|
|
|
|
$(if [[ -s "$findings_all" ]]; then sed 's/^/- /' "$findings_all"; else echo "- aucun"; fi)
|
|
|
|
## Utiliser ce fichier
|
|
|
|
- \`./10-registry.sh\` : régénère (à lancer après chaque déploiement / MAJ d'image).
|
|
- \`./10-registry.sh --check --alert\` : régénère, compare à la version commitée, **alerte en payload JSON**
|
|
si le réel a dérivé du registre (label perdu, image non épinglée, conteneur hors kit, état ≠ running).
|
|
- Un \`git diff\` sur ce fichier est, par construction, la détection de dérive.
|
|
EOF
|
|
)"
|
|
|
|
if [[ "$MODE" == "check" ]]; then
|
|
if [[ ! -f "$OUT" ]]; then
|
|
echo "[registry] REGISTRE-BRIQUES.md absent — générer d'abord" >&2
|
|
exit 2
|
|
fi
|
|
base="$(git -C "$KIT" show HEAD:g9-migration/REGISTRE-BRIQUES.md 2>/dev/null || git -C "$KIT" show HEAD:REGISTRE-BRIQUES.md 2>/dev/null || true)"
|
|
body_new="$(printf '%s' "$render" | sed '1,4d')"
|
|
body_old="$(printf '%s' "$base" | sed '1,4d')"
|
|
if [[ -z "$body_old" ]]; then
|
|
echo "[registry] pas de version commitée de comparaison" >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$(printf '%s' "$body_new" | md5sum)" == "$(printf '%s' "$body_old" | md5sum)" ]]; then
|
|
echo "[registry] aucune dérive ($nb_conteneurs conteneurs, $nb_findings constats)"
|
|
exit 0
|
|
fi
|
|
echo "[registry] DÉRIVE détectée :"
|
|
printf '%s\n' "$nb_findings constats"
|
|
if [[ "$ALERT" == "1" ]]; then
|
|
source "$KIT/scripts/lib-notify.sh"
|
|
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
payload="$(python3 -c 'import json,sys; print(json.dumps({"ts":sys.argv[1],"source":"10-registry","brique":"registre","host":"g9","check":"registry-drift","http_code":0,"severite":"warn","message":sys.argv[2],"runbook":"RUNBOOKS/registre.md"},ensure_ascii=False))' "$ts" "$nb_conteneurs conteneurs, $nb_findings constats automatiques, diff registre/réel à lire dans REGISTRE-BRIQUES.md")"
|
|
notify_json "$payload"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
printf '%s\n' "$render" > "$OUT"
|
|
echo "[registry] $OUT généré : $nb_conteneurs conteneurs, $nb_findings constats automatiques"
|