Files
g9-migration/g9-migration

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, 08-decommission.sh : drapeau --i-understand + saisie du nom du disque / WIPE XPENOLOGY.
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).

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)
│   ├── restic-notify.sh      ← alerte Matrix des jobs restic (posée par 06)
│   └── 08-decommission.sh    ← T6.4 : décommissionnement xpenology  [DESTRUCTIF]
├── 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/             ← T6.2
│   └── watchtower/           ← S11.4 : Watchtower sélectif
└── configs/
    ├── fail2ban.local.conf   ← config fail2ban (T2.1)
    └── restic.env.example    ← template restic (T3.1) — REMPLIR les secrets

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) → 07a-migrate-gitea.sh (T4.4) → n8n (T4.5) → DNS/TLS (T4.6). 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
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.