diff --git a/_adn/infra/backup-strategy-2026-04-18.md b/_adn/infra/backup-strategy-2026-04-18.md index 62bfcaf..bda68dd 100644 --- a/_adn/infra/backup-strategy-2026-04-18.md +++ b/_adn/infra/backup-strategy-2026-04-18.md @@ -2,9 +2,9 @@ title: Stratégie de backup DAEMON type: infra created: 2026-04-18 -updated: 2026-04-18 +updated: 2026-04-19 owner: jerem -status: draft-en-cours-de-déploiement +status: ✅ déployé et opérationnel tags: - infra - backup @@ -17,7 +17,7 @@ related: # Stratégie de backup DAEMON — 2026-04-18 -> **État** : plan validé, déploiement en cours. NAS en attente de validation par Fab. +> **É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. --- @@ -302,12 +302,39 @@ sudo systemctl start daemon-backup.service --- -## ❓ Points en attente +## ✅ Déploiement effectué (2026-04-19) -- [ ] **Validation Fab** : autorisation de créer un dossier `Jerem Unlimited Backup DAEMON` sur le NAS + quota -- [ ] **Passphrase Kopia** : à générer et à stocker dans Bitwarden + papier -- [ ] **Secrets NAS dans Doppler** : `NAS_WEBDAV_URL`, `NAS_WEBDAV_USER`, `NAS_WEBDAV_PASS` -- [ ] **Tests de restore** : valider le scénario complet sur un VPS blank (exercice trimestriel) +- [x] **Validation Fab** : dossier `Jerem Unlimited Création de contenus/DAEMON-Backup` créé +- [x] **Passphrase Kopia** : générée + stockée Doppler (`KOPIA_PASSPHRASE`) + Bitwarden + papier +- [x] **Secrets NAS Doppler** : `NAS_WEBDAV_URL`, `NAS_WEBDAV_USER`, `NAS_WEBDAV_PASS` (rclone-obscured) +- [x] **OAuth Google unifié** : nouveau client Web (Calendar + Gmail + Drive scopes) +- [x] **Repo Kopia local** : `/var/backups/kopia-local` actif +- [x] **Sync Kopia → gdrive** : `gdrive:DAEMON-Backups/kopia` actif +- [x] **Sync Kopia → NAS** : `nas:Jerem Unlimited Création de contenus/DAEMON-Backup/kopia` actif +- [x] **Script** : `/home/jerem/daemon-infra/scripts/daemon-backup.sh` +- [x] **Systemd timer** : `daemon-backup.timer` (cron 03:00 Europe/Paris, persistent=true) +- [x] **Kopia UI** : `https://kopia.jeremunlimited.com` (basicAuth + noindex via Traefik/Coolify) +- [x] **Service systemd Kopia** : `kopia-server.service` (port 51515 sur 0.0.0.0, UFW restreint à 10.0.0.0/8) +- [x] **Monitoring Kuma** : Push Monitor `DAEMON Backup`, URL dans Doppler `KUMA_BACKUP_PUSH_URL` +- [x] **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). --- diff --git a/_adn/memory/learnings.md b/_adn/memory/learnings.md index 9a011d5..13002b8 100644 --- a/_adn/memory/learnings.md +++ b/_adn/memory/learnings.md @@ -196,3 +196,35 @@ Catégories possibles : **Apprentissage** : accès SSH au VPS = `ssh jerem@10.66.66.1 -p 2222` (via WireGuard). Le port 22 standard n'est pas exposé sur l'interface WireGuard. **Implication DAEMON** : toujours utiliser port 2222 + IP WireGuard pour les commandes VPS. **Source** : conversation 2026-04-18 + +--- + +### 2026-04-19 — #outil — Backup stack Kopia déployée +**Contexte** : déploiement complet de la stratégie de backup ([[_adn/infra/backup-strategy-2026-04-18]]). +**Apprentissage** : Kopia + rclone installés. 3 destinations actives — local VPS (7j) + Google Drive (5 ans GFS) + NAS Ugreen Fab via WebDAV (5 ans GFS). Chiffrement AES-256-GCM client-side. Systemd timer 03h Paris. UI publique `https://kopia.jeremunlimited.com`. Push monitor Uptime Kuma. Script `/home/jerem/daemon-infra/scripts/daemon-backup.sh` orchestre tout en séquentiel avec fail-soft (un échec n'arrête pas le reste). +**Implication DAEMON** : backup quotidien automatique fonctionnel. Pour restorer un fichier : Kopia UI → snapshot → restore. CLI : `KOPIA_PASSWORD=$(doppler get KOPIA_PASSPHRASE) kopia snapshot list`. Tier 2 (`/etc/*`) snapshoté uniquement le dimanche via staging `/var/backups/etc-staging/`. +**Source** : conversation 2026-04-18 et 2026-04-19 + +--- + +### 2026-04-19 — #outil — OAuth Google unifié (1 client Web pour tout) +**Contexte** : ajout du scope Drive nécessitait nouvel OAuth client (l'ancien était type Desktop, pas compatible OAuth Playground). +**Apprentissage** : maintenant **un seul** client OAuth Google (type Web Application, nom `daemon-web`) couvre Calendar + Gmail + Drive. Refresh token unique dans Doppler (`GOOGLE_REFRESH_TOKEN`). L'ancien client Desktop a été supprimé. Tokens propagés via le script `openclaw-start.sh` (Calendar/Gmail) et `daemon-backup.sh` (Drive via rclone). +**Implication DAEMON** : pour ajouter un nouveau service Google à l'avenir, ajouter le scope sur le client Web `daemon-web` + relancer OAuth Playground pour générer un nouveau refresh token englobant tous les scopes. +**Source** : conversation 2026-04-19 + +--- + +### 2026-04-19 — #pattern — Sécurité : ne pas écraser les configs partagées +**Contexte** : le script backup écrasait `~/.config/rclone/rclone.conf` à chaque run pour rafraîchir le token gdrive, ce qui supprimait la section `[nas]` créée séparément. +**Apprentissage** : quand un script reconstruit un fichier de config partagé, il faut **préserver les sections étrangères**. Pattern à appliquer ailleurs : avant tout `cat > config_file`, vérifier si le fichier contient des sections que le script ne gère pas et les réinjecter. +**Implication DAEMON** : pour toute modification de config multi-section (rclone, openclaw, nginx, etc.) → soit edit ciblé in-place, soit reconstruction préservant l'existant. +**Source** : conversation 2026-04-19 + +--- + +### 2026-04-19 — #relation — Fab autorisé pour backup NAS +**Contexte** : Fab (collaborateur vidéo + ami) a accepté que le NAS Ugreen serve aussi pour les backups DAEMON. +**Apprentissage** : dossier `Jerem Unlimited Création de contenus/DAEMON-Backup/` sur le NAS contient les blobs Kopia chiffrés (Fab ne peut PAS les lire sans la passphrase Kopia). Mot de passe WebDAV partagé `Jerem2026` (sans rotation pour ne pas casser le workflow Mac existant). +**Implication DAEMON** : Fab reste un partenaire de confiance technique sans accès aux données Jerem (chiffrement client-side garantit la confidentialité). Pour les autres tiers, **toujours chiffrer avant transit** vers leurs infras. +**Source** : conversation 2026-04-19