deamon-vault/_adn/infra/backup-strategy-2026-04-18.md
2026-04-19 17:24:28 +02:00

15 KiB
Raw Permalink Blame History

title type created updated owner status tags related
Stratégie de backup DAEMON infra 2026-04-18 2026-04-19 jerem déployé et opérationnel
infra
backup
kopia
security
_adn/infra/vps-config-2026-04-17
_adn/soul

Stratégie de backup DAEMON — 2026-04-18

État (2026-04-19) : Déployé. 3 destinations actives (local VPS + Google Drive + NAS Ugreen). Premier run automatique via systemd timer prévu lundi 20 avril 03h00 Paris.


🎯 Objectif

Protéger les données critiques de l'écosystème DAEMON (vault Obsidian, data Notion, configs infrastructure) contre :

  • Panne matérielle VPS
  • Corruption / suppression accidentelle
  • Compromission sécurité (ransomware, intrusion)
  • Perte compte iCloud / Google
  • Faillite / coupure d'un fournisseur

Règle appliquée : 3-2-1 (3 copies, 2 médias différents, 1 offsite minimum). En réalité on vise 3-3-2 : 3 destinations de backup, 3 médias distincts, 2 offsites.


📊 Ce qui est backupé (par criticité)

Tier 1 — catastrophique si perdu (backup quotidien)

Source Contenu Pourquoi critique
/home/jerem/vault/ Vault Obsidian (cerveau DAEMON) Contient toute la mémoire, les règles, l'historique
/home/jerem/notion-backup/ Export Notion quotidien en Markdown Clients, projets, habitudes, journaling

Tier 2 — reinstallable mais douloureux (backup hebdomadaire le dimanche)

Source Contenu
/home/jerem/.openclaw/ Config OpenClaw, credentials Google/Gmail
/home/jerem/daemon-infra/ Scripts, systemd units, config nginx/wireguard
/etc/systemd/system/ Services systemd
/etc/nginx/ Config reverse proxy
/etc/wireguard/ Tunnel VPN

Tier 3 — pas backupé

  • Logs OpenClaw
  • Caches, sessions
  • Dossiers /tmp/, /var/cache/
  • Anything ephemeral

Non backupé volontairement

  • Doppler : MFA activé, régénération possible en 4h. Risque accepté.
  • Bot tokens Slack/WhatsApp : stockés dans Doppler, idem ci-dessus.

🗺️ Topologie

                   /home/jerem/vault (live VPS)
                               │
        ┌──────────────────────┼──────────────────────┐
        │                      │                      │
        ▼                      ▼                      ▼
  Kopia local          Kopia NAS ami (WebDAV)   Kopia Google Drive
  /var/backups/        nasprodfab.duckdns       gdrive jerem
  7 jours rétention    5 ans rétention          5 ans rétention
        │                      │                      │
        └── restore rapide     └── offsite physique   └── offsite cloud

Pas d'offsite : Kopia local (même machine que la source). Offsite physique : NAS Ugreen de Fab. Offsite cloud : Google Drive (compte jerem, 5TB).


🔒 Sécurité

Chiffrement

  • Côté client, avant transit : Kopia AES-256-GCM
  • Le NAS de Fab voit uniquement des blobs chiffrés illisibles
  • Google Drive idem

Gestion de la passphrase Kopia

La passphrase de chiffrement est stockée à 3 endroits :

  1. VPS : /root/.kopia-password (mode 600, root-only)
  2. Bitwarden : entrée "Kopia DAEMON backup passphrase"
  3. Papier : dans le coffre physique de Jerem

Si VPS détruit + Bitwarden perdu → backups restaurables via la copie papier. Si tous les 3 sont perdus → backups illisibles. Risque accepté : probabilité extrêmement faible.

Secrets NAS (WebDAV)

Stockés dans Doppler daemon-infra/prd :

  • NAS_WEBDAV_URL
  • NAS_WEBDAV_USER
  • NAS_WEBDAV_PASS

Injectés dans l'environnement Kopia au moment du backup par le script de démarrage, jamais écrits en clair sur disque.

Secrets Google Drive

Utilise l'OAuth Google déjà configuré pour Calendar/Gmail. Refresh token dans Doppler : GOOGLE_REFRESH_TOKEN.


Planning

Quand Quoi
Chaque nuit à 3h00 Paris (heure VPS fixe, ne change jamais en voyage) Snapshot Kopia Tier 1 + 2 Local VPS + NAS + gdrive
Chaque dimanche 3h30 Snapshot Tier 2 seul si rien de Tier 1 n'a tourné Idem
Chaque lundi 4h00 Test de restore automatique (extraction d'un snapshot random vers /tmp, comparaison hash) Local

Pourquoi 3h Paris fixe : le backup tourne sur le VPS (pas sur le Mac), donc l'heure physique de Jerem importe peu. 3h Paris = toujours pendant une période de faible activité chez lui, peu importe le fuseau.


📦 Rétention GFS (Grandfather-Father-Son)

Kopia applique automatiquement :

Horizon Snapshots gardés
7 derniers jours Tous (1 par jour)
4 dernières semaines 1 par semaine
12 derniers mois 1 par mois
5 dernières années 1 par an

Total théorique : ~28 snapshots actifs à n'importe quel instant. Taille réelle : grâce à la déduplication Kopia (chunks unique stockés une seule fois), ~150-300 MB pour le repo complet, pas 28× la taille du vault.


🔄 Pipeline de backup (nightly 3h)

┌──────────────────────────────────────────────────────────────┐
│  0. Pull secrets Doppler (NAS_*, GOOGLE_*)                   │
│                                                              │
│  1. Notion export                                            │
│     ├─ notion-export → /home/jerem/notion-backup/*.md        │
│     ├─ git commit + push (repo Gitea dédié)                  │
│     ├─ SI ÉCHEC : alerte Slack + continue (export J-1 dispo) │
│     └─ ping Kuma push monitor "notion-export-ok"             │
│                                                              │
│  2. Kopia snapshot local                                     │
│     └─ sources : vault, notion-backup, daemon-infra,         │
│        .openclaw, /etc/* (hebdo seulement)                   │
│                                                              │
│  3. Kopia sync vers NAS ami (WebDAV)                         │
│     └─ SI ÉCHEC (NAS offline) : alerte + continue            │
│                                                              │
│  4. Kopia sync vers Google Drive                             │
│     └─ SI ÉCHEC : alerte + continue                          │
│                                                              │
│  5. Kopia maintenance (GFS prune, compaction)                │
│                                                              │
│  6. Ping Kuma "daemon-backup-ok"                             │
└──────────────────────────────────────────────────────────────┘

Philosophie : jamais d'arrêt brutal. Une étape qui rate alerte et laisse les autres continuer.


🚨 Monitoring et alertes

Uptime Kuma Push Monitors

Monitor Seuil Action si KO
daemon-backup-ok Pas de ping en 25h Alerte Slack (normal)
notion-export-ok Pas de ping en 25h J1 Slack normal · J2 Slack urgent + email · J3 appel téléphonique
kopia-local-integrity Check intégrité hebdo Slack urgent
restore-test-ok Test restore hebdo Slack normal si échec, urgent si 2× consécutif

Cascade d'escalade (conforme _adn/soul section 2)

Palier Canal Niveau Kuma
Échec J1 Slack DM Normal
Échec J2 Slack all devices ring + email Urgent
Échec J3 Appel téléphonique + tout ce qui précède Très urgent

🖥️ Interface Kopia UI

  • URL : https://kopia.daemon.jeremunlimited.com
  • Auth : basic auth + token header (pattern identique à Control UI OpenClaw)
  • Headers : X-Robots-Tag: noindex
  • Accès : public HTTPS ou LAN via WireGuard
  • Permissions : lecture + restore uniquement (création snapshots bloquée en UI, seul le cron peut créer)

Utilisations :

  • Voir les snapshots passés et leur taille
  • Restaurer un fichier en 2 clics
  • Vérifier l'intégrité d'un snapshot
  • Monitorer l'espace disque des repos

🛠️ Commandes utiles (CLI)

# Lister les snapshots locaux
kopia snapshot list

# Restaurer la dernière version du vault
kopia snapshot restore latest /tmp/restore-vault

# Restaurer un fichier spécifique d'un snapshot donné
kopia snapshot restore <snapshot-id>/path/to/file /tmp/restored-file

# Vérifier l'intégrité
kopia snapshot verify

# Voir l'espace utilisé
kopia content stats

# Purger manuellement (suit la policy GFS)
kopia policy set --keep-latest 7 --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --keep-annual 5

# Forcer un backup immédiat
sudo systemctl start daemon-backup.service

📝 Plan de restauration (runbook)

Scénario 1 : j'ai supprimé un fichier du vault

  1. Ouvrir l'UI Kopia
  2. Naviguer dans le dernier snapshot → vault/
  3. Clic droit sur le fichier → Restore → /home/jerem/vault/
  4. git add + commit dans le vault

Scénario 2 : le VPS est HS, il faut tout reconstruire

  1. Déployer un nouveau VPS (Hetzner, même config)
  2. Installer Kopia
  3. Restaurer la passphrase depuis Bitwarden (ou papier)
  4. Connecter Kopia au repo Google Drive (plus rapide que NAS distant)
    kopia repository connect gdrive \
      --folder-id $FOLDER_ID \
      --credentials-file /path/to/oauth.json
    
  5. kopia snapshot restore latest --target /
  6. Reconfigurer secrets (régénérer OAuth Google, Doppler tokens)
  7. Relancer systemd : systemctl start openclaw

Temps cible de restauration complète : < 4h (avec config nginx + wireguard + openclaw à reconfigurer).

Scénario 3 : compromission sécurité, besoin de rollback

  1. Identifier la date de compromission depuis les logs
  2. Restaurer le snapshot de la veille (avant compromission)
  3. kopia snapshot restore <snapshot-pre-compromission> --target /home/jerem/
  4. Rotation de tous les secrets (Doppler → regenerate)
  5. Invalider tous les OAuth Google / Slack / Notion tokens
  6. Recréer depuis zéro les tokens

📐 Dimensionnement estimé

Donnée Taille actuelle Taille dans 5 ans (×10)
Vault Obsidian ~10 Mo ~100 Mo
Notion export mirror ~50 Mo ~500 Mo
daemon-infra ~5 Mo ~50 Mo
.openclaw config ~2 Mo ~20 Mo
/etc/* (tier 2) ~10 Mo ~10 Mo (stable)
Total source ~77 Mo ~680 Mo
Total repo Kopia (28 snapshots + dedup + compression) ~200 Mo ~1.5 Go

Quota à demander à Fab : 10 Go (avec marge confortable). Usage Google Drive : 10 Go sur 5 TB (0.2 %). Usage VPS local : ~200 Mo (7 jours de rétention, largement acceptable).


Déploiement effectué (2026-04-19)

  • Validation Fab : dossier Jerem Unlimited Création de contenus/DAEMON-Backup créé
  • Passphrase Kopia : générée + stockée Doppler (KOPIA_PASSPHRASE) + Bitwarden + papier
  • Secrets NAS Doppler : NAS_WEBDAV_URL, NAS_WEBDAV_USER, NAS_WEBDAV_PASS (rclone-obscured)
  • OAuth Google unifié : nouveau client Web (Calendar + Gmail + Drive scopes)
  • Repo Kopia local : /var/backups/kopia-local actif
  • Sync Kopia → gdrive : gdrive:DAEMON-Backups/kopia actif
  • Sync Kopia → NAS : nas:Jerem Unlimited Création de contenus/DAEMON-Backup/kopia actif
  • Script : /home/jerem/daemon-infra/scripts/daemon-backup.sh
  • Systemd timer : daemon-backup.timer (cron 03:00 Europe/Paris, persistent=true)
  • Kopia UI : https://kopia.jeremunlimited.com (basicAuth + noindex via Traefik/Coolify)
  • Service systemd Kopia : kopia-server.service (port 51515 sur 0.0.0.0, UFW restreint à 10.0.0.0/8)
  • Monitoring Kuma : Push Monitor DAEMON Backup, URL dans Doppler KUMA_BACKUP_PUSH_URL
  • Sudoers rsync : /etc/sudoers.d/daemon-backup-rsync (jerem peut rsync /etc/* sans password)

🔜 À faire ensuite

  • Test de restore réel sur VPS blank (exercice trimestriel — premier exo Q3 2026)
  • Notion export (Phase 2.6) : intégration script Python notion-sdk-py + commit Gitea daemon-notion-mirror
  • Monitoring sync vault Obsidian (vérifier Git push toutes les minutes ne rate pas)

🔧 Notes techniques de déploiement

TLS NAS : le certificat NAS est auto-signé (duckdns). On utilise --rclone-args=--no-check-certificate côté Kopia + no_check_certificate = true dans ~/.config/rclone/rclone.conf. Acceptable car Kopia chiffre AES-256-GCM côté client AVANT transit — TLS n'est qu'une couche additionnelle de protection contre la modification.

Préservation rclone.conf : le script daemon-backup.sh reconstruit la section [gdrive] à chaque run (avec un access_token frais), mais préserve les autres sections (notamment [nas]) via un parser awk dans le script.

Permissions /etc/* : impossible de snapshoter /etc/{systemd,nginx,wireguard} directement comme jerem (root-owned + secrets wireguard chmod 600). Solution : staging via rsync --chown=jerem:jerem vers /var/backups/etc-staging/ avant snapshot Kopia. Sudoers granulaire pour autoriser uniquement ce rsync sans password.

Docker networking : Coolify-proxy (Traefik) tourne dans Docker sur le réseau coolify (10.0.1.0/24). Pour qu'il puisse joindre Kopia server bare-metal, on bind sur 0.0.0.0:51515 puis on filtre via UFW : allow from 10.0.0.0/8 to any port 51515 (même pattern que OpenClaw 18789).

Mot de passe NAS exposé : durant le debug, j'ai dû lire ~/.config/rclone/rclone.conf du Mac de Jerem pour identifier un mismatch avec Doppler. Le mot de passe en clair (Jerem2026) a été vu. Décision Jerem : pas de rotation (mot de passe partagé Fab existant, accessible via iCloud Mac).


📚 Alternatives considérées (historique)

Option Rejetée car
Obsidian Sync officiel Payant, vendor lock-in, ne couvre pas Notion
Backblaze B2 Jerem a le NAS ami gratuit, pas besoin de cloud payant
iCloud pour backup serveur Pas d'API serveur native, besoin d'un Mac allumé
Restic (vs Kopia) Pas d'UI web — Jerem préfère avoir le visuel
Borg (vs Kopia) Pas de backend WebDAV natif, moins polyvalent
Notion → incremental seulement Aucun outil mature, complexité de code non justifiée vu que Kopia dedupe déjà

🔗 Références


Document vivant, à mettre à jour à chaque évolution de la stratégie.