Files
g9-migration/g9-migration/README.md
T

14 KiB
Raw Blame History

Migration G9 — Kit d'exécution

Objectif : tout le matériel (scripts + configs + compose) prêt à exécuter la migration xpenology → G9, phase par phase, quand le G9 sera branché (IP temporaire .24).

Règle d'or : chaque script est idempotent et sécurisé. Les opérations destructives (partitionnement, LUKS, wipe) sont bloquées sans le drapeau --i-understand et la validation explicite de l'utilisateur (garde-fou G3).

Charte d'autonomie (garde-fous G1–G10)

# Garde-fou
G1 Lecture seule libre ; toute modif délibérée et documentée.
G2 Aucune modif de données sans backup vérifié en place.
G3 Op. destructives (rm -rf, mkfs, wipe, dd, partition, RAID/LUKS, suppression conteneur) = validation explicite avant exécution.
G4 Double-run : xpenology = source de vérité ; aucune suppression dessus tant que le contrepartie G9 n'est pas validé.
G5 IP G9 = .24 tant que le xpenology n'est pas décommissionné.
G6 Aucun changement réseau impactant d'autres appareils sans validation.
G7 Chaque modif a un rollback documenté.
G8 Journalisation de chaque action (quoi, pourquoi, résultat).
G9 Aucun secret loggué ou commité.
G10 Périmètre = G9 (+ lecture xpenology) ; autres machines sur validation.

Correspondance garde-fou → point d'application

Garde-fou Point d'application
G1 Scripts idempotents, en-têtes documentant chaque modification.
G2 06-restic-backup.sh : backup + test de restauration avant toute migration.
G3 06b-usb-media.sh : drapeau --i-understand + saisie du nom du disque. 08-decommission.sh : saisie ETEINDRE XPENOLOGY — aucun wipe exécuté par le kit (décision 12/10 : débrancher, disques intacts, récupération RESTORE-NAS-USB.md).
G4 07a–07d (+ 07-migrate-stateful.sh générique) : lecture seule du xpenology + intégrité sha256 ; 08-decommission.sh exige la validation de toutes les briques avant wipe.
G5 README (ordre d'exécution) + 08-decommission.sh : IP .24→.25 uniquement au décommissionnement.
G6 01-hardening.sh : ufw LAN-only (192.168.1.0/24), pas d'exposition WAN.
G7 Chaque script : section Rollback en en-tête + colonne Rollback du journal.
G8 journal() de chaque script → journal.md (6 colonnes : date, phase, action, raison, résultat, rollback).
G9 .gitignore (*.env, secrets/, configs remplis) + 03b-secrets.sh (chmod 600, non loggué).
G10 Scripts ne modifient que le G9 ; xpenology en lecture seule (SSH Master@192.168.1.25:222, clé SSH — compte effi bloqué par Synology 10/2026).
G11 Autonomie de l'assistante IA par paliers (décision 12/10) : L0 lecture seule → L1 propose, l'humain exécute → L2 exécute les actions stateless documentées des RUNBOOKS/ → L3 actions stateful uniquement sur GO explicite de l'utilisateur. Portes : L1→L2 = 20 propositions journalisées dont ≥ 90 % pertinentes sur 30 jours ; L2→L3 = sinistre simulé sur une brique stateful sans perte de données + rollback vérifié. Interdits permanents : opérations destructives (G3), suppression de données, modification réseau impactant d'autres appareils (G6), wipe, création de compte ou de privilège, lecture de secrets hors coffre. Traçabilité : chaque action de l'IA = ligne de journal (quoi, pourquoi, résultat, rollback) + notification dans le salon ops. Aucune exécution L2/L3 sans validation utilisateur tracée.

Arborescence

g9-migration/
├── README.md                 ← ce fichier (runbook)
├── journal.md                ← journal d'exécution (G8) — à remplir pendant la migration
├── scripts/
│   ├── 01-hardening.sh       ← T2.1 : SSH keys, ufw (LAN-only), unattended-upgrades, fail2ban
│   ├── 02-data.sh            ← T2.2 : espace /data (NON destructif — LUKS abandonné 10/10/2026)
│   ├── 03-docker.sh          ← T2.3 : Docker + compose + groupe docker
│   ├── 03b-secrets.sh        ← T2.3b : bootstrap des fichiers secrets des briques (chmod 600)
│   ├── 04-smb.sh             ← T2.4 : partage SMB (données famille)
│   ├── 05-beszel.sh          ← T2.5 : agent Beszel
│   ├── 06-restic-backup.sh   ← T3.1 : restic G9 → Freebox + test restauration
│   ├── 06b-usb-media.sh      ← T6.1 : montage des disques USB média → /mnt/usb-media
│   ├── 06c-postboot-check.sh ← contrôles post-reboot (lecture seule, 15+ checks)
│   ├── 07-migrate-stateful.sh← T4.x : générique (rsync) — INOPÉRANT sur le DSM : sshd
│   │                          refuse `rsync --server` et scp → utiliser 07a–07d
│   ├── 07a-migrate-gitea.sh  ← T4.4 : Gitea + backend Postgres (transport tar sur SSH)
│   ├── 07b-migrate-vaultwarden.sh ← T4.1 : Vaultwarden (coffre + 2FA + ADMIN_TOKEN)
│   ├── 07c-migrate-nextcloud.sh   ← T4.2 : Nextcloud + MariaDB + Collabora
│   ├── 07d-migrate-photoprism.sh  ← T4.3 : PhotoPrism (copie 530 Go + indexation)
│   ├── 07e-deploy-edge.sh    ← T4.6 : edge Caddy (validate → up → ufw 443 → vérifs HTTPS/SNI)
│   ├── restic-notify.sh      ← alerte Matrix des jobs restic (posée par 06)
│   ├── 09-j5040-dhcp.sh      ← E3 : DHCP dnsmasq sur J5040 (pré-bascule bridge)
│   ├── 10-verify-restore-photo.sh ← critère 3 : preuve restore photo (sha256 vs .25)
│   ├── 11-registry.sh        ← T-A.1/T-A.5 : génère REGISTRE-BRIQUES.md + --check --alert (dérive)
│   ├── registry_parse.py     ← helper lecture seule de 11-registry.sh (docker ps → lignes + constats)
│   ├── registry_inputs.py    ← helper : compose du kit + Caddyfile → declarations & endpoints
│   ├── 12-probes.sh          ← T-C.1/C.2/C.5 : sondes APPLICATIVES + alertes JSON + digest (cron G9)
│   ├── g9-notify.py          ← poste un payload JSON dans le salon ops (install /usr/local/bin)
│   ├── g9-verify-alert.py    ← vérifie la conformité des payloads JSON reçus (C-AP2)
│   ├── lib-notify.sh         ← côté pilot : notifie via g9-notify.py (aucun secret dans le kit)
│   └── 08-decommission.sh    ← T6.4 : arrêt + débranchement xpenology  [0 wipe — disques intacts]
├── compose/                  ← docker-compose.yml versionnés (stockés aussi dans Gitea)
│   ├── vaultwarden/          ← T4.1
│   ├── nextcloud/            ← T4.2
│   ├── photoprism/           ← T4.3
│   ├── gitea/                ← T4.4
│   ├── n8n/                  ← T4.5
│   ├── jellyfin/             ← T4.5 (média par référence, /media:ro)
│   ├── edge/                 ← T4.6 : Caddy G9 (CA interne, edge transitoire)
│   ├── edge-j5040/           ← E4 : Caddy J5040 + plugin OVH (LE DNS-01, Caddyfile versionné)
│   ├── adguard/              ← E2 : AdGuard Home (DNS filtré, image épinglée digest)
│   └── watchtower/           ← S11.4 : Watchtower sélectif
└── configs/
    ├── fail2ban.local.conf   ← config fail2ban (T2.1)
    └── restic.env.example    ← template restic (T3.1) — REMPLIR les secrets

Docs d'identité, d'observabilité et d'études (Phase 4.5+)

Fichier Rôle Nature
REGISTRE-BRIQUES.md Moitié technique du registre : briques, images épinglées, labels Watchtower, endpoints, état, backup. Générée par scripts/11-registry.sh — ne pas éditer
REGISTRE-IDENTITES.md Moitié humaine : quel compte sur quelle brique, type, 2FA, référence d'entrée du coffre, comment réinitialiser, responsable. Versionnée, 0 secret
RUNBOOKS/ nextcloud · matrix · jellyfin · photoprism · vaultwarden · registre · digest : symptôme → diagnostic → action → rollback matière première de l'assistante IA (G11)
PARTAGE-SECRET-MAITRE.md Qui peut déverrouiller le coffre si je suis injoignable : détenteurs, contenu de l'enveloppe, procédure, test annuel 0 secret
RESTORE-NAS-USB.md Lire les disques DSM hors boîtier (mdadm/LVM, lecture seule), procédure + contrôles + interdits test = critère Phase 6
EXPOSITION-INTERNET.md Étude : exposition mesurée, grille par brique, lien avec l'IdP, playbook classé 0 règle posée
BRIDGE-WHATSAPP.md Étude mautrix-whatsapp : apports, limites, risque de bannissement, interdits, pilote borné 0 installation

Régler le registre et les sondes (une fois, puis ça tourne)

# machine de pilotage — lecture seule sur .24/.27
./scripts/11-registry.sh                 # génère REGISTRE-BRIQUES.md
./scripts/11-registry.sh --check --alert # dérive -> payload JSON dans le salon ops (rc=1)

# G9 (root) — sondes applicatives + digest
sudo ./scripts/12-probes.sh --install    # /usr/local/bin/g9-probes.sh + cron */15 + digest 08:30
sudo ./scripts/12-probes.sh --selftest   # provocation d'un KO -> NOTIFY_OK 200
sudo ./scripts/g9-verify-alert.py 20     # payloads JSON conformes ? -> VERIFY_OK
sudo ./scripts/12-probes.sh --uninstall  # rollback complet (cron + script + état)

Ordre d'exécution (phase par phase)

Phase Script / action Garde-fou
1 (xpenology) exclure stateful de Watchtower + inventaire G4
2 01-hardening.sh → 02-data.sh → 03-docker.sh → 03b-secrets.sh → 04-smb.sh → 05-beszel.sh —
3 06-restic-backup.sh (backup + test restauration) G2
4 par brique, dans l'ordre — après 03b-secrets.sh : 07b-migrate-vaultwarden.sh (T4.1) → 07c-migrate-nextcloud.sh (T4.2) → 07d-migrate-photoprism.sh (T4.3) ✅ 12/10 : 433 941 fichiers / 567 Go copiés, manifeste 0 écart + sha256 200/200, stack up (port 2342), index progressif, snapshot restic + restore prouvée (10-verify-restore-photo.sh) → 07a-migrate-gitea.sh (T4.4) → Jellyfin (T4.5, frais) → 07e-deploy-edge.sh (T4.6, Caddy TLS interne) → Watchtower (T4.7, scope stateless). 07-migrate-stateful.sh = script générique rsync, inopérant sur le DSM (sshd refuse rsync --server/scp) ; transport réel des 07a–07d = tar sur SSH G4
5 restic 3-2-1 complet (G9 + PC IA → Freebox → 218play) G2
E (J5040, parallèle) E1 durcissement + Docker (01/03 paramétrés, .27) ✅ → E2 AdGuard (VM Freebox .1 → .27) ✅ bascule DNS faite (résolvants clients → .27, filtres testés) → E3 DHCP (09-j5040-dhcp.sh, baux 35 appareils prêts) + Freebox mode bridge = GO utilisateur (G6) → E4 Caddy OVH (compose/edge-j5040) ✅ 7/7 certs LE (DNS-01) + WireGuard prêt ; forward WAN Freebox→.27 + démontage edge G9 = GO utilisateur G6, G7
6 06b-usb-media.sh (T6.1) → Jellyfin (T6.2) → IP .24→.25 → 08-decommission.sh G3, G4, G5
7 (G7) wipe DSM + Ubuntu neuf + boot test G3, G10

Note disque (décision 10/10/2026 — Option 0) : le G9 est mono-disque (Samsung 990 Pro 2 To, seul disque interne, aucun RAID prévu). 02-data.sh crée l'espace /data en simple dossier sur le LV racine existant : ni partitionnement, ni LUKS, ni réinstallation (LUKS abandonné — données familiales non critiques ; protection = ufw LAN-only + backups restic chiffrés, voir DOSSIER-SOFTWARE.md S9/S10 changelog 10/10/2026). La tolérance panne disque est assurée par le 3-2-1 (Phases 3/5) et le spare G7 (Phase 7).

Phase 3 — backup restic : état au 10/10/2026

Repo rclone:freeboxftp:Freebox/backup/g9 (Freebox Delta, partage Save/Freebox, 1.79 To, 1.785 To libres). Snapshot initial 21de0787 : 4 564 fichiers, 6.956 GiB lus / 4.178 GiB stockés (sources /data, /home/folzo, /etc).

  • Transport & append-only : backend rclone de restic, remote FTP local freeboxftp: = 192.168.1.254:21 (SMB freebox: en secours), aucun montage ; append-only prouvé. Transports testés/écartés et limitations : source unique → RESTORE-G9.md §6.
  • Test de restauration : fichier restauré depuis la Freebox, sha256 identique (9352a905d9bb…, 470 637 o, restore < 2 s) — voir RESTORE-G9.md §2/§5 et journal.md.
  • Automatisation (cron root, logs /var/log/restic-*.log) : 03:30 backup (conteneurs stateful stoppés ~1–3 min : valheim, n8n, synapse, couchdb, obsidian), 04:00 prune (7 quotidiens / 4 hebdo / 12 mensuels / 5 annuels), 09:15 health (alerte si snapshot > 26 h). Les 2 jobs ont été lancés à la main (rc=0, snapshot cccdb2c7).
  • Alerte Matrix : directe via Synapse (/etc/restic/matrix.env, token ops-bot, salon ops) — testée, messages réellement reçus. Voie de secours webhook n8n conservée (RESTIC_NOTIFY_WEBHOOK) ; accès n8n à recréer (clé API à générer dans l'UI).
  • Persistance post-reboot : validée par un vrai reboot (21:36) — scripts/06c-postboot-check.sh = 15 contrôles OK / 0 KO (binaires, env/clé/config en 600, aucun montage requis, cron, jobs, repo lisible + check OK, conteneurs relancés). À rejouer après tout changement.
  • Secrets : /etc/restic/repo.key (clé du repo, jamais affichée — à consigner dans Vaultwarden, item « G9 – clé repo restic »), /etc/restic/cifs.cred, /etc/restic/matrix.env, /root/.config/rclone/{rclone,ftp}.conf, /etc/restic.env → tous 600 root ; aucun secret dans le repo git (.gitignore : *.env, *.cred, *.key).

Convention

  • Chaque script affiche ce qu'il fait, est idempotent, et écrit dans journal.md.
  • Les secrets (mots de passe, clés restic) vont dans configs/*.env hors git (G9).
  • Un script échoue → il s'arrête (set -euo pipefail) → on lit le journal → on corrige.