Compare commits
10
Commits
e6295cffc2
...
2f98c32da1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f98c32da1 | ||
|
|
a5b2909722 | ||
|
|
6ee136b35d | ||
|
|
d4f68cb8f7 | ||
|
|
71f1ec33fd | ||
|
|
a3a198027d | ||
|
|
6516d67d0b | ||
|
|
0b95d24046 | ||
|
|
e2dc0176ff | ||
|
|
ffe3f2ac91 |
@@ -12,3 +12,6 @@ g9-migration/configs/restic.env
|
|||||||
|
|
||||||
# Artefacts playwright-cli (snapshots navigateur)
|
# Artefacts playwright-cli (snapshots navigateur)
|
||||||
.playwright-cli/
|
.playwright-cli/
|
||||||
|
|
||||||
|
# Config WireGuard générée (clés privées — G9)
|
||||||
|
**/wireguard/wg/
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
| Jellyfin (T4.5) | `jellyfin/jellyfin:10.9` (10.9.11) | healthy, média par RÉFÉRENCE (`/mnt/usb-media` → `/media:ro`, aucune copie), 1 médiathèque = `/media/films`, Items Movie = 1 | false |
|
| Jellyfin (T4.5) | `jellyfin/jellyfin:10.9` (10.9.11) | healthy, média par RÉFÉRENCE (`/mnt/usb-media` → `/media:ro`, aucune copie), 1 médiathèque = `/media/films`, Items Movie = 1 | false |
|
||||||
| edge (T4.6) | `caddy:2` (digest `f2a1290d…`) | healthy, HTTPS interne (CA interne), 443 seul port exposé WAN | **true** (stateless) |
|
| edge (T4.6) | `caddy:2` (digest `f2a1290d…`) | healthy, HTTPS interne (CA interne), 443 seul port exposé WAN | **true** (stateless) |
|
||||||
| Watchtower (T4.7) | `nickfedor/watchtower@sha256:45b54f6d…` | healthy, scope label-enable : dry-run `scanned=2` (edge + watchtower), 14 stateful ignorés | true |
|
| Watchtower (T4.7) | `nickfedor/watchtower@sha256:45b54f6d…` | healthy, scope label-enable : dry-run `scanned=2` (edge + watchtower), 14 stateful ignorés | true |
|
||||||
| PhotoPrism (T4.3) | — | **non déployée** : `07d-migrate-photoprism.sh` écrit/testé, exécution reportée (coût = copie 530 Go ; média servi par Jellyfin) | — |
|
| PhotoPrism (T4.3) | `photoprism/photoprism:260919` + `mariadb:10.11` | **✅ 12/10** : 433 941 fichiers / 567 Go copiés depuis `/volume1/photo` (manifeste chemins+tailles 0 écart, sha256 200/200), stack healthy port 2342 (LAN) + `photos.folzocorp.fr` via edge J5040 ; index progressif (AUTO_INDEX 300 s, chaîne index→faces→analyze) ; snapshot restic + restore photo prouvée (`10-verify-restore-photo.sh`) | false |
|
||||||
|
|
||||||
### 0.2 Incidents rencontrés et correctifs (détail = journal.md)
|
### 0.2 Incidents rencontrés et correctifs (détail = journal.md)
|
||||||
|
|
||||||
@@ -41,6 +41,15 @@
|
|||||||
- `nextcloud_app`, `nextcloud_db`, `nextcloud_redis`, `vaultwarden-server-1`, `gitea` **relancés et up** après les exports ; dumps/transitaires supprimés sur .25, pilot et G9.
|
- `nextcloud_app`, `nextcloud_db`, `nextcloud_redis`, `vaultwarden-server-1`, `gitea` **relancés et up** après les exports ; dumps/transitaires supprimés sur .25, pilot et G9.
|
||||||
- Aucun partage, aucune donnée, aucun conteneur .25 supprimé ; transports .25→pilot = `ssh 'sudo tar cz'`/`mysqldump` en lecture seule (sshd DSM refuse `rsync --server` et scp).
|
- Aucun partage, aucune donnée, aucun conteneur .25 supprimé ; transports .25→pilot = `ssh 'sudo tar cz'`/`mysqldump` en lecture seule (sshd DSM refuse `rsync --server` et scp).
|
||||||
|
|
||||||
|
### 0.5 J5040 (.27) — E1→E4 (11–12/10)
|
||||||
|
|
||||||
|
| Étape | État | Preuve |
|
||||||
|
|---|---|---|
|
||||||
|
| E1 durcissement | ✅ | SSH clés-only, ufw (LAN 53/3000/8081 + WAN 80/443/51820), fail2ban, unattended-upgrades, Docker 29.1.3 ; RAM 8 Go sous test (upgrade 16 Go si > 70 %) |
|
||||||
|
| E2 AdGuard | ✅ bascule faite | `adguard/adguardhome` (digest `aba9e3bf…`) sur .27, UI 8081, DNS 53 ; config VM .1 migrée (2 filtres 182 128 + 6 540 règles, safe_search, profils parents, users bcrypt) ; resolvants clients → .27 ; filtrage testé ; VM .1 conservée secours |
|
||||||
|
| E3 DHCP | préparé, **GO utilisateur (G6)** | baux statiques Freebox repris (35 appareils, OCR 38) dans `/srv/dnsmasq/leases`, `dnsmasq --test` OK, script `09-j5040-dhcp.sh` prêt ; bascule = Freebox Pop + dnsmasq |
|
||||||
|
| E4 edge + WireGuard | certs ✅, **GO utilisateur pour WAN** | Caddy OVH DNS-01 (plugin `maxcryger/caddy-ovh` digest `502996b5…`) : **7/7 certs LE** + proxy 6 briques (photos 307 depuis 12/10) ; WireGuard `wg0.conf` + client prêts (600, gitignorés) ; forward WAN Freebox→.27 et démontage edge G9 = après GO |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Rapport de récupération (thread supprimé)
|
# Rapport de récupération (thread supprimé)
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ g9-migration/
|
|||||||
│ ├── 07d-migrate-photoprism.sh ← T4.3 : PhotoPrism (copie 530 Go + indexation)
|
│ ├── 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)
|
│ ├── 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)
|
│ ├── 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)
|
||||||
│ └── 08-decommission.sh ← T6.4 : décommissionnement xpenology [DESTRUCTIF]
|
│ └── 08-decommission.sh ← T6.4 : décommissionnement xpenology [DESTRUCTIF]
|
||||||
├── compose/ ← docker-compose.yml versionnés (stockés aussi dans Gitea)
|
├── compose/ ← docker-compose.yml versionnés (stockés aussi dans Gitea)
|
||||||
│ ├── vaultwarden/ ← T4.1
|
│ ├── vaultwarden/ ← T4.1
|
||||||
@@ -69,7 +71,9 @@ g9-migration/
|
|||||||
│ ├── gitea/ ← T4.4
|
│ ├── gitea/ ← T4.4
|
||||||
│ ├── n8n/ ← T4.5
|
│ ├── n8n/ ← T4.5
|
||||||
│ ├── jellyfin/ ← T4.5 (média par référence, /media:ro)
|
│ ├── jellyfin/ ← T4.5 (média par référence, /media:ro)
|
||||||
│ ├── edge/ ← T4.6 : Caddy (Caddyfile versionné = config)
|
│ ├── 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
|
│ └── watchtower/ ← S11.4 : Watchtower sélectif
|
||||||
└── configs/
|
└── configs/
|
||||||
├── fail2ban.local.conf ← config fail2ban (T2.1)
|
├── fail2ban.local.conf ← config fail2ban (T2.1)
|
||||||
@@ -83,8 +87,9 @@ g9-migration/
|
|||||||
| 1 | (xpenology) exclure stateful de Watchtower + inventaire | G4 |
|
| 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` | — |
|
| 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 |
|
| 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, **reporté**) → `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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 7 | (G7) wipe DSM + Ubuntu neuf + boot test | G3, G10 |
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# AdGuard Home — E2 (J5040 .27 → .1) : DNS filtré LAN (migration depuis VM Freebox Delta).
|
||||||
|
# Stateless (config = AdGuardHome.yaml versionnée dans le kit) → Watchtower ON (S11.4).
|
||||||
|
# Image épinglée par digest. UI = 8081 LAN-only (ufw), DNS = 53 tcp/udp LAN-only.
|
||||||
|
services:
|
||||||
|
adguard:
|
||||||
|
image: adguard/adguardhome@sha256:aba9e3bf0613be3ba3755e1fc311b126e2c24bec25e18b6483894a88283074f0
|
||||||
|
container_name: adguard
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
volumes:
|
||||||
|
- /srv/adguard/work:/work
|
||||||
|
- /srv/adguard/data:/opt/adguardhome/conf
|
||||||
|
- /srv/adguard/state:/opt/adguardhome/work
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:53:53/tcp"
|
||||||
|
- "0.0.0.0:53:53/udp"
|
||||||
|
- "0.0.0.0:8081:80/tcp"
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
email admin@folzocorp.fr
|
||||||
|
# ACME staging désactivé = certs de production
|
||||||
|
}
|
||||||
|
|
||||||
|
(ovh_tls) {
|
||||||
|
tls {
|
||||||
|
dns ovh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(folzocorp) {
|
||||||
|
import ovh_tls
|
||||||
|
encode gzip
|
||||||
|
log {
|
||||||
|
output stdout
|
||||||
|
format console
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cloud.folzocorp.fr {
|
||||||
|
import folzocorp
|
||||||
|
redir 308 {
|
||||||
|
/ /login*
|
||||||
|
}
|
||||||
|
reverse_proxy 192.168.1.24:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.folzocorp.fr {
|
||||||
|
import folzocorp
|
||||||
|
reverse_proxy 192.168.1.24:8081
|
||||||
|
}
|
||||||
|
|
||||||
|
git.folzocorp.fr {
|
||||||
|
import folzocorp
|
||||||
|
reverse_proxy 192.168.1.24:3000
|
||||||
|
}
|
||||||
|
|
||||||
|
media.folzocorp.fr {
|
||||||
|
import folzocorp
|
||||||
|
reverse_proxy 192.168.1.24:8096
|
||||||
|
}
|
||||||
|
|
||||||
|
photos.folzocorp.fr {
|
||||||
|
import folzocorp
|
||||||
|
reverse_proxy 192.168.1.24:2342
|
||||||
|
}
|
||||||
|
|
||||||
|
n8n.folzocorp.fr {
|
||||||
|
import folzocorp
|
||||||
|
reverse_proxy 192.168.1.24:5678
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix.folzocorp.fr {
|
||||||
|
import folzocorp
|
||||||
|
reverse_proxy 192.168.1.24:8008
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Edge J5040 (E4) : Caddy + plugin DNS OVH = Let's Encrypt par DNS-01 (achève T4.6/S9.1).
|
||||||
|
# Image épinglée (caddy 2.8.4 + dns.providers.ovh). Stateless → Watchtower ON.
|
||||||
|
# Remplace l'edge du G9 (CA interne) après bascule vérifiée. Secrets = ./secrets/ovh.env (600, hors git).
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
image: maxcryger/caddy-ovh@sha256:502996b5b60aec3ee565324143c14ee4a0c99ff55ace167629981c6c2f2eb597
|
||||||
|
container_name: caddy
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
env_file:
|
||||||
|
- ./secrets/ovh.env
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:80:80/tcp"
|
||||||
|
- "0.0.0.0:443:443/tcp"
|
||||||
|
- "0.0.0.0:443:443/udp"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "caddy", "validate", "--config", "/etc/caddy/Caddyfile"]
|
||||||
|
interval: 60s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# WireGuard — E4 (J5040) : accès VPN famille (portable). LAN-only (ufw 51820 udp WAN).
|
||||||
|
# Config client générée à l'activation (wg genkey, QR via `wg show`); secrets hors git.
|
||||||
|
services:
|
||||||
|
wireguard:
|
||||||
|
image: lscr.io/linuxserver/wireguard@sha256:437a490f7afc6969434251351ba47455a3829e71cef9c6172f3f533332d04063
|
||||||
|
container_name: wireguard
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=false"
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
- SYS_MODULE
|
||||||
|
sysctls:
|
||||||
|
- net.ipv4.ip_forward=1
|
||||||
|
- net.ipv6.conf.all.forwarding=1
|
||||||
|
environment:
|
||||||
|
- WG_HOST=folzocorp.fr
|
||||||
|
- WG_PORT=51820
|
||||||
|
volumes:
|
||||||
|
- ./wg:/etc/wireguard
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:51820:51820/udp"
|
||||||
@@ -104,3 +104,23 @@
|
|||||||
| 2026-10-11 14:10 | 4 (T4.6) | edge : Caddy déployé (reverse proxy LAN + HTTPS interne) | Briquage T4.6 : TLS interne pour vaultwarden/nextcloud/gitea/jellyfin/n8n/matrix, sous-domaines déjà configurés côté apps (DOMAIN/ROOT_URL/N8N_HOST) → aucune brique touchée | `caddy:2` (digest f2a1290d…) up healthy (healthcheck = `caddy validate`) ; **tls internal** = CA interne (LE + DNS challenge = suite S9.1 via AdGuard J5040) ; **PIÈGES mesurés** : (1) snippet `(name)` importé → « ambiguous site definition » si utilisé comme adresse ; (2) `request_id` nest PAS un directive site (validate KO) ; (3) Caddy logue les en-têtes **reçus du client**, pas ceux envoyés en amont → preuve X-Forwarded prise côté app ; ufw 443/tcp exposé WAN (seul, default deny protège les ports briques) + 80 redirect + 9980 LAN-only ; **Nextcloud** : merge config = `array_replace_recursive` (le dépôt dun 2e fichier *.config.php a été avalé par lentrée existante → résolu en écrivant g9-double-run.config.php = source de vérité du kit, monté :ro) → occ : overwritehost=cloud.folzocorp.fr, overwriteprotocol=https, trusted_proxies=[192.168.1.0/24, 172.16.0.0/12] ; **preuve côté app** : redirect `https://cloud.folzocorp.fr/` → Location `https://cloud.folzocorp.fr/index.php/login` (schéma https + host forwardé rendus par Nextcloud) ; curl -k via edge : pass 200 / git 200 / cloud 200 / media 200 / n8n 200 / matrix 302 / SNI IP 200 ; vaultwarden TRUSTED_HOSTS += 172.16.0.0/12 (aligné) | `docker compose -f /data/docker/edge/docker-compose.yml down` ; `ufw delete allow 443` ; supprimer g9-double-run.config.php (retour double-run http LAN) |
|
| 2026-10-11 14:10 | 4 (T4.6) | edge : Caddy déployé (reverse proxy LAN + HTTPS interne) | Briquage T4.6 : TLS interne pour vaultwarden/nextcloud/gitea/jellyfin/n8n/matrix, sous-domaines déjà configurés côté apps (DOMAIN/ROOT_URL/N8N_HOST) → aucune brique touchée | `caddy:2` (digest f2a1290d…) up healthy (healthcheck = `caddy validate`) ; **tls internal** = CA interne (LE + DNS challenge = suite S9.1 via AdGuard J5040) ; **PIÈGES mesurés** : (1) snippet `(name)` importé → « ambiguous site definition » si utilisé comme adresse ; (2) `request_id` nest PAS un directive site (validate KO) ; (3) Caddy logue les en-têtes **reçus du client**, pas ceux envoyés en amont → preuve X-Forwarded prise côté app ; ufw 443/tcp exposé WAN (seul, default deny protège les ports briques) + 80 redirect + 9980 LAN-only ; **Nextcloud** : merge config = `array_replace_recursive` (le dépôt dun 2e fichier *.config.php a été avalé par lentrée existante → résolu en écrivant g9-double-run.config.php = source de vérité du kit, monté :ro) → occ : overwritehost=cloud.folzocorp.fr, overwriteprotocol=https, trusted_proxies=[192.168.1.0/24, 172.16.0.0/12] ; **preuve côté app** : redirect `https://cloud.folzocorp.fr/` → Location `https://cloud.folzocorp.fr/index.php/login` (schéma https + host forwardé rendus par Nextcloud) ; curl -k via edge : pass 200 / git 200 / cloud 200 / media 200 / n8n 200 / matrix 302 / SNI IP 200 ; vaultwarden TRUSTED_HOSTS += 172.16.0.0/12 (aligné) | `docker compose -f /data/docker/edge/docker-compose.yml down` ; `ufw delete allow 443` ; supprimer g9-double-run.config.php (retour double-run http LAN) |
|
||||||
| 2026-10-11 14:20 | 4 (T4.7) | watchtower : vérification scope stateless + dry-run | S11.4 : MAJ auto uniquement des briques stateless ; leçon incident Nextcloud 06/10 = CLEANUP=false | Conteneur up healthy, image **nickfedor/watchtower épinglée par digest** sha256:45b54f6d… (fork : containrrr/watchtower:1 n'existe pas) ; env mesuré = POLL=3600, LABEL_ENABLE=true, CLEANUP=false, INCLUDE_STOPPED=false ; **labels vérifiés sur les 16 conteneurs .24** : stateful enable=false = nextcloud, collabora, jellyfin, nextcloud_db, nextcloud_redis, vaultwarden, gitea, gitea-db, valheim, n8n, element, synapse, couchdb, obsidian ; enable=true = edge (stateless, T4.6) + watchtower ; **dry-run** `docker run --rm -v /var/run/docker.sock nickfedor/watchtower@sha256:45b54f6d… --label-enable --monitor-only -R` → « Only checking containers with label … enable », **scanned=2 updated=0 failed=0** = les 14 stateful sont ignorés ; notification : webhook n8n testé en POST depuis le réseau Docker → **200** (le compose affichait un placeholder https://n8n.folzocorp.fr → aligné sur generic+http://n8n:5678, nom de service : l'edge n'écoute que 443) ; nickfedor : SCHEDULE et POLL exclusifs, pas de --once (=-R) | `docker compose -f /data/docker/watchtower/docker-compose.yml down` ; aucun effet sur les briques (label-driven) |
|
| 2026-10-11 14:20 | 4 (T4.7) | watchtower : vérification scope stateless + dry-run | S11.4 : MAJ auto uniquement des briques stateless ; leçon incident Nextcloud 06/10 = CLEANUP=false | Conteneur up healthy, image **nickfedor/watchtower épinglée par digest** sha256:45b54f6d… (fork : containrrr/watchtower:1 n'existe pas) ; env mesuré = POLL=3600, LABEL_ENABLE=true, CLEANUP=false, INCLUDE_STOPPED=false ; **labels vérifiés sur les 16 conteneurs .24** : stateful enable=false = nextcloud, collabora, jellyfin, nextcloud_db, nextcloud_redis, vaultwarden, gitea, gitea-db, valheim, n8n, element, synapse, couchdb, obsidian ; enable=true = edge (stateless, T4.6) + watchtower ; **dry-run** `docker run --rm -v /var/run/docker.sock nickfedor/watchtower@sha256:45b54f6d… --label-enable --monitor-only -R` → « Only checking containers with label … enable », **scanned=2 updated=0 failed=0** = les 14 stateful sont ignorés ; notification : webhook n8n testé en POST depuis le réseau Docker → **200** (le compose affichait un placeholder https://n8n.folzocorp.fr → aligné sur generic+http://n8n:5678, nom de service : l'edge n'écoute que 443) ; nickfedor : SCHEDULE et POLL exclusifs, pas de --once (=-R) | `docker compose -f /data/docker/watchtower/docker-compose.yml down` ; aucun effet sur les briques (label-driven) |
|
||||||
| 2026-10-11 14:25 | 4 (clôture) | Phase 4 consolidée + kit poussé sur Gitea | Clôture Phase 4 (critère 5) | `PHASE4-RECOLLEMENT.md` §0 ajouté : briques déployées + versions épinglées (vaultwarden 1.37.4, nextcloud 35.0.1/mariadb 10.11/redis 7/collabora 26.04.5.1.1, gitea 1.27.3/postgres 16, jellyfin 10.9.11, caddy 2 digest f2a1290d, watchtower digest 45b54f6d), **6 incidents + correctifs mesurés**, alignement proxy (trusted_proxies/overwrite/TRUSTED_HOSTS/ufw 443 seul WAN), état .25 (services relancés, dumps supprimés, rien de destructif) ; PhotoPrism T4.3 = **reportée** (script 07d prêt, copie 530 Go) ; README aligné (07e + compose/edge + jellyfin T4.5) ; **push gitea master : HEAD local 366c5ca == gitea/master 366c5ca** (API Gitea vérifiée) ; vault Obsidian = repo `g9ops/obsidian-vault` présent (47 Mo, import T4.4) ; **zéro secret dans git** : `git ls-files` (secrets/*.env/token/key/cred) = 0 fichier de secret, `git grep` des valeurs (token, hash jellyfin, Pw=) = 0, historique `-S token` = 0 ; durcissement : token retiré de l'URL du remote → `~/.git-credentials` 600 + credential.helper store | `git push gitea a1864c6:master` (fast-forward annulable) ; suppression de ~/.git-credentials |
|
| 2026-10-11 14:25 | 4 (clôture) | Phase 4 consolidée + kit poussé sur Gitea | Clôture Phase 4 (critère 5) | `PHASE4-RECOLLEMENT.md` §0 ajouté : briques déployées + versions épinglées (vaultwarden 1.37.4, nextcloud 35.0.1/mariadb 10.11/redis 7/collabora 26.04.5.1.1, gitea 1.27.3/postgres 16, jellyfin 10.9.11, caddy 2 digest f2a1290d, watchtower digest 45b54f6d), **6 incidents + correctifs mesurés**, alignement proxy (trusted_proxies/overwrite/TRUSTED_HOSTS/ufw 443 seul WAN), état .25 (services relancés, dumps supprimés, rien de destructif) ; PhotoPrism T4.3 = **reportée** (script 07d prêt, copie 530 Go) ; README aligné (07e + compose/edge + jellyfin T4.5) ; **push gitea master : HEAD local 366c5ca == gitea/master 366c5ca** (API Gitea vérifiée) ; vault Obsidian = repo `g9ops/obsidian-vault` présent (47 Mo, import T4.4) ; **zéro secret dans git** : `git ls-files` (secrets/*.env/token/key/cred) = 0 fichier de secret, `git grep` des valeurs (token, hash jellyfin, Pw=) = 0, historique `-S token` = 0 ; durcissement : token retiré de l'URL du remote → `~/.git-credentials` 600 + credential.helper store | `git push gitea a1864c6:master` (fast-forward annulable) ; suppression de ~/.git-credentials |
|
||||||
|
| 2026-10-11 15:49 | 4 | migrate:photoprism : backup restic pré-migration | Garde-fou G2 avant import PhotoPrism | snapshot restic Freebox (log /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
|
||||||
|
| 2026-10-11 15:56 | 4 | migrate:photoprism : backup restic pré-migration | Garde-fou G2 avant import PhotoPrism | snapshot restic Freebox (log /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
|
||||||
|
| 2026-10-11 16:21 | 4 | migrate:photoprism : backup restic pré-migration | Garde-fou G2 avant import PhotoPrism | snapshot restic Freebox (log /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
|
||||||
|
| 2026-10-11 16:10 | J5040 (E1) | durcissement J5040 .27 (01-hardening paramétré) | E1 : edge Ubuntu 26.04 neuf, RAM 8 Go test (décision utilisateur) | SSH keys-only + ufw actif (LAN-only 53/3000/8081 + WAN 80/443/51820) + unattended-upgrades + fail2ban ; sudo NOPASSWD folzo ; clés SSH pilot installées ; portable à ajouter via ssh-copy-id | restaurer sshd_config .bak + ufw disable |
|
||||||
|
| 2026-10-11 16:12 | J5040 (E1) | Docker + compose installés sur .27 | Runtime edge | docker 29.1.3 + compose 2.40.3, folzo dans groupe docker | apt-get remove docker.io docker-compose-v2 |
|
||||||
|
| 2026-10-11 16:14 | 07d : copie SANS gzip (photos déjà compressées) | gzip sur CPU NAS = 10-13 Mo/s mesuré → flux brut ~17 Mo/s | tar c|x (sans compression), copie relancée propre (originals purgés, rien de destructif sur .25) | restaurer le script depuis git |
|
||||||
|
| 2026-10-11 16:20 | J5040 (E2) | AdGuard Home déployé sur .27 (migration depuis VM Freebox .1) | E2 : DNS filtré sur l'edge | config VM récupérée en LECTURE SEULE (ssh master@.1, AdGuardHome.yaml 415 l. : 2 filtres 182128+6540 règles, safe_search, services bloqués parents, users bcrypt conservé) ; image épinglée digest aba9e3bf ; UI 8081 LAN, DNS 53 LAN ; systemd-resolved désactivé sur .27 ; tests : doubleclick→0.0.0.0, example.org résolu, UI 302 | docker compose down -v ; VM .1 intacte ; resolv.conf .27 restauré |
|
||||||
|
| 2026-10-11 17:05 | J5040 (E3) | Préparation DHCP : baux statiques récupérés (38 appareils, capture Freebox via OCR utilisateur) + /srv/dnsmasq/leases (35 entrées, .1 réservé E4, VM adguard exclue) + 09-j5040-dhcp.sh | E3 : pré-bascule bridge, G6 = baux identiques (mêmes IP) | dnsmasq --test = syntax OK ; scan ARP cross-check (g9 48:ea:62, j5040 60:be:b4, nas 10:e7:c6, backup-218 .26 00:11:32) ; anomalie relevée : VM debian 02:11:32 en conflit sur .24 → à repositionner .23 côté Freebox (utilisateur) | rien (préparation, dnsmasq non démarré) |
|
||||||
|
| 2026-10-11 17:15 | J5040 (E2) | Bascule DNS : Freebox DHCP DNS1 .1 → .27 (GO utilisateur) + bail G9 renouvelé | E2 : AdGuard J5040 remplace la VM Freebox | resolvectl G9 = DNS .27 ; doubleclick→0.0.0.0, example.org résolu ; clients = renouvellement de bail progressif (baux 12 h) ; VM .1 conservée en secours (arrêt = action utilisateur G3 après stabilité) ; .254 freebox intact | remettre DNS1=.1 dans la Freebox (VM intacte, rollback immédiat) |
|
||||||
|
| 2026-10-11 18:05 | J5040 (E4) | Clé API OVH du NAS retrouvée (LE env, longueurs 16/32/32 OK) mais NON SOUSCRITE/EXPIRÉE (test /auth/credential → 404) | E4 : LE DNS-01 nécessite clé valide | secret copié en 600 sur J5040 (compose/edge-j5040/secrets/ovh.env, hors git) ; NAS : renewal LE cassée depuis expiration → nouvelle clé requise (droits API legacy /domain/zone/folzocorp.fr/*, utilisateur) | rien |
|
||||||
|
| 2026-10-11 18:40 | J5040 (E4) | Nouvelle clé API OVH créée+souscrite (droits legacy /domain/zone) ; clé NAS expirée confirmée (90 j) | E4 : LE DNS-01 | python-ovh GET /domain/zone/folzocorp.fr/record = 12 records OK ; secrets 600 sur J5040 (ovh.env hors git) ; NAS : mettra la nouvelle clé lors du renouvellement LE | révoquer la clé dans manager OVH |
|
||||||
|
| 2026-10-11 18:45 | J5040 (E4) | Caddy OVH déployé sur .27 : 7/7 certs Let's Encrypt émis (DNS-01) + proxy 6 briques | E4 : TLS auto achève T4.6/S9.1 | maxcryger/caddy-ovh digest 502996b5 healthy ; cloud 302/pass 200/git 200/media 302/photos 502 (stack en copie)/n8n 200/matrix 302, issuer = Let's Encrypt ; WAN (forward Freebox→G9) et démontage edge G9 = après bascule bridge E3 | docker compose down ; révoquer clé |
|
||||||
|
| 2026-10-11 18:10 | 07d : copie parallèle 4 flux (tar par dossier racine) | mono-flux SSH plafonné ~13 Mo/s (lecture HDD NAS, CPU NAS 7% = non saturé) | 29 dirs, xargs -P4 ; + chmod u+rwX post-copie ; intégrité/stack/index = 07d SKIP_COPY=1 | rien (source .25 lecture seule) |
|
||||||
|
| 2026-10-11 21:30 | 4 | décision chiffrement restic MAINTENU | Critère 3 : arbitrage chiffrement vs simplicité restore | Décision utilisateur : chiffrement restic conservé, clé de repo locale G9 (/etc/restic/repo.key 600, sauvegardée hors repo git), restore simple via sudo restic sur le G9 ; test restore photo prévu par scripts/10-verify-restore-photo.sh | révoquer/cloner la clé si changement de politique |
|
||||||
|
| 2026-10-12 13:40 | 4 | migrate:photoprism : backup restic pré-migration | Garde-fou G2 avant import PhotoPrism | snapshot restic Freebox (log /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
|
||||||
|
| 2026-10-12 14:06 | 4 | migrate:photoprism : intégration PhotoPrism | copie de 530 Go à vérifier avant indexation | manifeste chemins+tailles identique (433941 fichiers, 567394182751 o) + échantillon sha256 200/200 | re-copie ciblée via tar/ssh |
|
||||||
|
| 2026-10-12 14:11 | 4 | migrate:photoprism : intégration PhotoPrism | copie de 530 Go à vérifier avant indexation | manifeste chemins+tailles identique (433941 fichiers, 567394182751 o) + échantillon sha256 200/200 | re-copie ciblée via tar/ssh |
|
||||||
|
| 2026-10-12 14:17 | 4 | migrate:photoprism : intégration PhotoPrism | copie de 530 Go à vérifier avant indexation | manifeste chemins+tailles identique (433941 fichiers, 567394182751 o) + échantillon sha256 200/200 | re-copie ciblée via tar/ssh |
|
||||||
|
| 2026-10-12 14:18 | 4 | migrate:photoprism : stack PhotoPrism déployée + indexation lancée | T4.3 : stack saine, index progressif (photothèque 530 Go sur le G9) | image épinglée 260919, MariaDB 10.11 (watchtower OFF), secrets via env_file 600, port 2342 LAN ; originals = copie locale | docker compose down + rm -rf /data/docker/photoprism |
|
||||||
|
| 2026-10-12 15:07 | 4 | test restauration photo depuis restic | Critère 3 : preuve restore du snapshot incluant les photos | snapshot 06a7e3b89ef3e97d5f5c1dc9098496fab8ad79d48f4455eabdde07255143e33b contient 505836 entrées sous /data/docker/photoprism/originals ; restore 1 fichier ; sha256 restauré == source .25 (00cbfed8da81…) ; chiffrement maintenu (clé locale G9) | rm -rf /tmp/restore-photo-test (fait) |
|
||||||
|
|||||||
@@ -68,22 +68,29 @@ ufw --force reset >/dev/null
|
|||||||
ufw default deny incoming
|
ufw default deny incoming
|
||||||
ufw default allow outgoing
|
ufw default allow outgoing
|
||||||
ufw allow OpenSSH
|
ufw allow OpenSSH
|
||||||
# Services LAN-only (G9 = LAN-only, l'exposition WAN passe par l'edge — S9.2/S9.3).
|
# Services LAN-only (LAN-only, l'exposition WAN passe par l'edge — S9.2/S9.3).
|
||||||
# Ports des services RÉELS sur .24 (n8n, synapse, element, couchdb, obsidian) +
|
# Listes paramétrables (défaut = G9 .24) : J5040 edge = TCP_LAN="53 3000 8081"
|
||||||
# briques famille futures + Valheim (TCP+UDP). Accessibles depuis le LAN uniquement.
|
# UDP_LAN="53" WAN_TCP="80 443" WAN_UDP="51820".
|
||||||
for port in 2342 3000 3100 5678 5984 8008 8009 8080 8081 8096 9980; do
|
TCP_LAN="${TCP_LAN:-2342 3000 3100 5678 5984 8008 8009 8080 8081 8096 9980}"
|
||||||
|
UDP_LAN="${UDP_LAN:-2456 2457 2458}"
|
||||||
|
WAN_TCP="${WAN_TCP:-443 80}"
|
||||||
|
WAN_UDP="${WAN_UDP:-}"
|
||||||
|
for port in $TCP_LAN; do
|
||||||
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
|
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
|
||||||
done
|
done
|
||||||
for port in 2456 2457 2458; do
|
for port in $UDP_LAN; do
|
||||||
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
|
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
|
||||||
ufw allow from 192.168.1.0/24 to any port "$port" proto udp
|
ufw allow from 192.168.1.0/24 to any port "$port" proto udp
|
||||||
done
|
done
|
||||||
# T4.6 edge (Caddy) : SEUL port exposé WAN = 443 (+ 80 = redirect ACME/HTTP→HTTPS).
|
# Edge : ports exposés WAN (443 service + 80 redirect ACME/HTTP→HTTPS ; 51820 = WireGuard).
|
||||||
# L'exposition WAN des briques passe par l'edge uniquement (S9.2/S9.3, default deny incoming).
|
for port in $WAN_TCP; do
|
||||||
ufw allow 443/tcp comment 'edge T4.6 (seul port exposé WAN)'
|
ufw allow "$port/tcp" comment "edge WAN ($port)"
|
||||||
ufw allow 80/tcp comment 'edge T4.6 (redirect ACME)'
|
done
|
||||||
|
for port in $WAN_UDP; do
|
||||||
|
ufw allow "$port/udp" comment "edge WAN ($port)"
|
||||||
|
done
|
||||||
ufw --force enable
|
ufw --force enable
|
||||||
journal "ufw activé" "Firewall LAN-only (G6)" "deny incoming, allow outgoing, OpenSSH + services LAN-only 192.168.1.0/24 (TCP 2342 3000 3100 5678 5984 8008 8009 8080 8081 8096 9980 + Valheim 2456-2458 TCP/UDP) + edge 443/80 exposés WAN (T4.6)" "ufw disable"
|
journal "ufw activé" "Firewall LAN-only (G6)" "deny incoming, allow outgoing, OpenSSH + TCP_LAN=[$TCP_LAN] + UDP_LAN=[$UDP_LAN] (LAN-only) + WAN_TCP=[$WAN_TCP] WAN_UDP=[$WAN_UDP]" "ufw disable"
|
||||||
|
|
||||||
# --- 3. unattended-upgrades ---
|
# --- 3. unattended-upgrades ---
|
||||||
log "unattended-upgrades : activation"
|
log "unattended-upgrades : activation"
|
||||||
|
|||||||
@@ -59,10 +59,11 @@ log "source .25 : $SRC_COUNT fichiers, $SRC_BYTES octets"
|
|||||||
|
|
||||||
# ---------------------------------------------------------------- 1. backup G2
|
# ---------------------------------------------------------------- 1. backup G2
|
||||||
log "=== 1/5 BACKUP restic (G2) ==="
|
log "=== 1/5 BACKUP restic (G2) ==="
|
||||||
if $SSH_G9 "sudo restic snapshots 2>/dev/null | grep -q '$(date '+%Y-%m-%d')'"; then
|
SNAP_TODAY=$(date '+%Y-%m-%d')
|
||||||
|
if $SSH_G9 "sudo sh -c 'set -a; . /etc/restic.env; set +a; restic -r \"\$RESTIC_REPOSITORY\" snapshots 2>/dev/null' | grep -q '$SNAP_TODAY'"; then
|
||||||
log " ⏭ snapshot du jour déjà présent — backup complet sauté (idempotence)"
|
log " ⏭ snapshot du jour déjà présent — backup complet sauté (idempotence)"
|
||||||
else
|
else
|
||||||
$SSH_G9 "sudo /usr/local/bin/restic-daily.sh >/dev/null 2>&1; sudo restic snapshots 2>/dev/null | tail -2"
|
$SSH_G9 "sudo /usr/local/bin/restic-daily.sh >/dev/null 2>&1; sudo sh -c 'set -a; . /etc/restic.env; set +a; restic -r \"\$RESTIC_REPOSITORY\" snapshots 2>/dev/null' | tail -2"
|
||||||
journal "backup restic pré-migration" "Garde-fou G2 avant import PhotoPrism" "snapshot restic Freebox (log /var/log/restic-daily.log)" "restic forget <snapshot> && restic prune"
|
journal "backup restic pré-migration" "Garde-fou G2 avant import PhotoPrism" "snapshot restic Freebox (log /var/log/restic-daily.log)" "restic forget <snapshot> && restic prune"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -75,10 +76,14 @@ if [[ "${SKIP_COPY:-0}" != "1" ]]; then
|
|||||||
t0=$(date +%s)
|
t0=$(date +%s)
|
||||||
# Progression : tar GNU du G9 émet un pointage sur stderr tous les 50 000 enregistrements
|
# Progression : tar GNU du G9 émet un pointage sur stderr tous les 50 000 enregistrements
|
||||||
# (stderr relayé par ssh) → la copie de 530 Go n'est pas silencieuse pendant des heures.
|
# (stderr relayé par ssh) → la copie de 530 Go n'est pas silencieuse pendant des heures.
|
||||||
if ! $SSH_XP "sudo tar cz -C '$XP_PHOTO' ." | $SSH_G9 "tar xz --checkpoint=50000 --checkpoint-action=echo='[copie] %n enregistrements' -C $G9_DIR/originals"; then
|
# PAS de gzip : photos déjà compressées (JPEG/RAW), gzip sur le CPU du NAS = 10 Mo/s mesuré.
|
||||||
|
# Flux brut = lié par le réseau (~100 Mo/s).
|
||||||
|
if ! $SSH_XP "sudo tar c -C '$XP_PHOTO' ." | $SSH_G9 "tar x --checkpoint=50000 --checkpoint-action=echo='[copie] %n enregistrements' -C $G9_DIR/originals"; then
|
||||||
echo "ERREUR : flux tar interrompu"; exit 1
|
echo "ERREUR : flux tar interrompu"; exit 1
|
||||||
fi
|
fi
|
||||||
t=$(( $(date +%s) - t0 ))
|
t=$(( $(date +%s) - t0 ))
|
||||||
|
# DSM arrive avec des modes 0000/2700 (ACL Synology) → les dirs sont illisibles par folzo.
|
||||||
|
$SSH_G9 "sudo chmod -R u+rwX $G9_DIR/originals"
|
||||||
log " copie terminée en ${t} s"
|
log " copie terminée en ${t} s"
|
||||||
journal "copie photos .25 → G9" "530 Go, source en lecture seule (aucune écriture sur .25)" "tar/ssh relayé par le pilot, ${t} s ; $SRC_COUNT fichiers source" "rm -rf $G9_DIR/originals (source .25 intacte)"
|
journal "copie photos .25 → G9" "530 Go, source en lecture seule (aucune écriture sur .25)" "tar/ssh relayé par le pilot, ${t} s ; $SRC_COUNT fichiers source" "rm -rf $G9_DIR/originals (source .25 intacte)"
|
||||||
fi
|
fi
|
||||||
@@ -115,13 +120,13 @@ $SSH_G9 "sudo rsync -a $KIT_ON_G9/compose/$BRIQUE/ $G9_DIR/ && sudo chmod 755 $G
|
|||||||
$SSH_G9 "sudo chown -R 999:999 $G9_DIR/db && sudo chmod 750 $G9_DIR/db"
|
$SSH_G9 "sudo chown -R 999:999 $G9_DIR/db && sudo chmod 750 $G9_DIR/db"
|
||||||
# PhotoPrism n'accepte pas les *_FILE : env_file construit à partir des secrets (600, root, hors git).
|
# PhotoPrism n'accepte pas les *_FILE : env_file construit à partir des secrets (600, root, hors git).
|
||||||
$SSH_G9 "sudo sh -c 'umask 077; { echo PHOTOPRISM_ADMIN_PASSWORD=\$(cat $G9_DIR/secrets/admin); echo PHOTOPRISM_DATABASE_PASSWORD=\$(cat $G9_DIR/secrets/dbpass); } > $G9_DIR/secrets/photoprism.env' && sudo chown root:root $G9_DIR/secrets/photoprism.env"
|
$SSH_G9 "sudo sh -c 'umask 077; { echo PHOTOPRISM_ADMIN_PASSWORD=\$(cat $G9_DIR/secrets/admin); echo PHOTOPRISM_DATABASE_PASSWORD=\$(cat $G9_DIR/secrets/dbpass); } > $G9_DIR/secrets/photoprism.env' && sudo chown root:root $G9_DIR/secrets/photoprism.env"
|
||||||
$SSH_G9 "cd $G9_DIR && docker compose up -d && sleep 30 && docker compose ps --format '{{.Name}} {{.Status}}'"
|
$SSH_G9 "cd $G9_DIR && sudo docker compose up -d && sleep 30 && sudo docker compose ps --format '{{.Name}} {{.Status}}'"
|
||||||
|
|
||||||
# ---------------------------------------------------------------- 5. indexation + vérifs
|
# ---------------------------------------------------------------- 5. indexation + vérifs
|
||||||
log "=== 5/5 INDEXATION (progressive, décision #4) + VÉRIFS ==="
|
log "=== 5/5 INDEXATION (progressive, décision #4) + VÉRIFS ==="
|
||||||
$SSH_G9 "curl -sf -o /dev/null -w 'photoprism HTTP %{http_code}\n' http://127.0.0.1:2342/api/v1/session/health"
|
$SSH_G9 "curl -s -o /dev/null -w 'photoprism HTTP %{http_code}\n' http://127.0.0.1:2342/api/v1/health"
|
||||||
$SSH_G9 "cd $G9_DIR && timeout $INDEX_TIMEOUT docker compose exec -T photoprism photoprism index 2>&1 | tail -6 < /dev/null || true"
|
$SSH_G9 "cd $G9_DIR && timeout $INDEX_TIMEOUT sudo docker compose exec -T photoprism photoprism index < /dev/null 2>&1 | tail -6 || true"
|
||||||
$SSH_G9 "cd $G9_DIR && docker compose exec -T photoprism sh -c 'photoprism count 2>/dev/null | head -12' < /dev/null || true"
|
$SSH_G9 "cd $G9_DIR && sudo docker compose exec -T db sh -c 'mariadb -u photoprism -p\"\$(cat /run/secrets/dbpass)\" photoprism -e \"SELECT COUNT(*) AS photos_indexes FROM photos\"' 2>/dev/null || true"
|
||||||
journal "stack PhotoPrism déployée + indexation lancée" "T4.3 : stack saine, index progressif (photothèque 530 Go sur le G9)" "image épinglée 260919, MariaDB 10.11 (watchtower OFF), secrets via env_file 600, port 2342 LAN ; originals = copie locale" "docker compose down + rm -rf /data/docker/photoprism"
|
journal "stack PhotoPrism déployée + indexation lancée" "T4.3 : stack saine, index progressif (photothèque 530 Go sur le G9)" "image épinglée 260919, MariaDB 10.11 (watchtower OFF), secrets via env_file 600, port 2342 LAN ; originals = copie locale" "docker compose down + rm -rf /data/docker/photoprism"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
Executable
+45
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 09-j5040-dhcp.sh — E3 : DHCP sur le J5040 (dnsmasq) en préparation du mode bridge Freebox.
|
||||||
|
#
|
||||||
|
# RÔLE : dnsmasq = DHCP uniquement (DNS coupé : port=0, le DNS LAN = AdGuard .27:53).
|
||||||
|
# Les baux statiques sont lus depuis /srv/dnsmasq/leases (format: MAC,IP,nom — 1 par ligne),
|
||||||
|
# repris de la config DHCP Freebox (baux statiques existants, mêmes IP → G6 : pas de changement d'IP).
|
||||||
|
#
|
||||||
|
# ⚠️ BASCULE (G6) : ce script active le DHCP sur le J5040. La bascule Freebox (DHCP off /
|
||||||
|
# mode bridge) est une ACTION UTILISATEUR explicite. Rollback = couper dnsmasq + rallumer
|
||||||
|
# le DHCP Freebox (baux statiques Freebox intacts, rien n'y a été supprimé).
|
||||||
|
#
|
||||||
|
# Usage : sudo bash 09-j5040-dhcp.sh [interface_LAN] (défaut : enp1s0)
|
||||||
|
set -euo pipefail
|
||||||
|
IFACE="${1:-enp1s0}"
|
||||||
|
LEASES=/srv/dnsmasq/leases
|
||||||
|
log() { echo -e "\n[dhcp] $*"; }
|
||||||
|
|
||||||
|
[[ $EUID -eq 0 ]] || { echo "ERREUR : sudo"; exit 1; }
|
||||||
|
[[ -f "$LEASES" ]] || { echo "ERREUR : $LEASES absent (copier la liste des baux statiques Freebox, format MAC,IP,nom)"; exit 1; }
|
||||||
|
|
||||||
|
log "installation dnsmasq"
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq dnsmasq
|
||||||
|
systemctl disable --now dnsmasq 2>/dev/null || true
|
||||||
|
|
||||||
|
log "config dnsmasq (DHCP only, DNS coupé)"
|
||||||
|
cat > /etc/dnsmasq.conf << EOF
|
||||||
|
interface=$IFACE
|
||||||
|
bind-interfaces
|
||||||
|
port=0
|
||||||
|
dhcp-range=192.168.1.100,192.168.1.240,12h
|
||||||
|
dhcp-option=6,192.168.1.27
|
||||||
|
dhcp-option=3,192.168.1.27
|
||||||
|
no-resolv
|
||||||
|
strict-order
|
||||||
|
dhcp-lease-max=256
|
||||||
|
dhcp-no-override
|
||||||
|
EOF
|
||||||
|
while IFS=, read -r mac ip name; do
|
||||||
|
[[ -z "${mac// }" || "$mac" == \#* ]] && continue
|
||||||
|
echo "dhcp-host=$mac,$ip,$name"
|
||||||
|
done < "$LEASES" >> /etc/dnsmasq.conf
|
||||||
|
dnsmasq --test && log "config dnsmasq valide"
|
||||||
|
systemctl enable --now dnsmasq
|
||||||
|
log "DHCP actif sur $IFACE (baux = $LEASES). Bascule Freebox = action utilisateur (mode bridge / DHCP off)."
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 10-verify-restore-photo.sh — Critère 3 : preuve que le snapshot restic contient les photos
|
||||||
|
# et qu'un fichier photo se restaure à l'identique (sha256 vs source .25).
|
||||||
|
#
|
||||||
|
# Chiffrement MAINTENU (décision utilisateur 11/10) : clé de repo locale G9 (/etc/restic/repo.key),
|
||||||
|
# restore via sudo sur le G9. Source .25 en lecture seule (G4).
|
||||||
|
#
|
||||||
|
# Usage : sur la machine de pilotage (détenant la clé), ./10-verify-restore-photo.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
XP_USER="Master"; XP_HOST="192.168.1.25"; XP_PORT=222
|
||||||
|
XP_PHOTO="/volume1/photo"
|
||||||
|
G9_USER="folzo"; G9_HOST="192.168.1.24"
|
||||||
|
KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_openfox11}"
|
||||||
|
G9_ORIG="/data/docker/photoprism/originals"
|
||||||
|
TARGET="/tmp/restore-photo-test"
|
||||||
|
JOURNAL="$(cd "$(dirname "$0")/.." && pwd)/journal.md"
|
||||||
|
|
||||||
|
SSH_XP="ssh -p $XP_PORT -i $KEY -o ConnectTimeout=10 $XP_USER@$XP_HOST"
|
||||||
|
SSH_G9="ssh -i $KEY -o ConnectTimeout=10 $G9_USER@$G9_HOST"
|
||||||
|
RST_PRELUDE='set -a; . /etc/restic.env; set +a'
|
||||||
|
|
||||||
|
log() { echo -e "\n[restore-test] $*"; }
|
||||||
|
journal() {
|
||||||
|
printf '| %s | 4 | %s | %s | %s | %s |\n' \
|
||||||
|
"$(date '+%Y-%m-%d %H:%M')" "$1" "$2" "$3" "$4" >> "$JOURNAL"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "=== 1/3 snapshot g9 le plus récent ==="
|
||||||
|
SNAP=$($SSH_G9 "sudo bash -s" <<EOF
|
||||||
|
$RST_PRELUDE
|
||||||
|
restic -r "\$RESTIC_REPOSITORY" snapshots --tag g9 --json 2>/dev/null | jq -r '.[-1].id'
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
[[ "$SNAP" =~ ^[0-9a-f]{8,}$ ]] || { echo "ERREUR : snapshot introuvable"; exit 1; }
|
||||||
|
log "snapshot = $SNAP"
|
||||||
|
N=$($SSH_G9 "sudo bash -s -- '$G9_ORIG' '$SNAP'" <<'EOF'
|
||||||
|
set -a; . /etc/restic.env; set +a
|
||||||
|
restic -r "$RESTIC_REPOSITORY" ls "$2" 2>/dev/null | grep -c "$1"
|
||||||
|
true
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
[[ "${N:-0}" -gt 0 ]] || { echo "ERREUR : le snapshot $SNAP ne contient pas $G9_ORIG"; exit 1; }
|
||||||
|
log " ✅ snapshot contient $N entrées sous $G9_ORIG"
|
||||||
|
|
||||||
|
log "=== 2/3 restauration d'un fichier photo ==="
|
||||||
|
G9_META="${HOME}/migration-staging/photoprism/g9.meta"
|
||||||
|
if [[ -f "$G9_META" ]]; then
|
||||||
|
REL=$(cut -f1 "$G9_META" | grep -i '\.jpg$' | LC_ALL=C sort | tail -1)
|
||||||
|
REL="${G9_ORIG}/${REL#./}"
|
||||||
|
else
|
||||||
|
REL=$($SSH_G9 "find '$G9_ORIG' -type f -iname '*.jpg' | LC_ALL=C sort | tail -1")
|
||||||
|
fi
|
||||||
|
log "fichier test = ${REL#$G9_ORIG/}"
|
||||||
|
B64=$(printf '%s' "$REL" | base64 -w0)
|
||||||
|
$SSH_G9 "sudo bash -s -- '$B64' '$SNAP' '$TARGET'" <<'EOF'
|
||||||
|
set -a; . /etc/restic.env; set +a
|
||||||
|
rel=$(printf '%s' "$1" | base64 -d)
|
||||||
|
rm -rf "$3"
|
||||||
|
restic -r "$RESTIC_REPOSITORY" restore --target "$3" --include "*${rel##*/}" "$2" >/dev/null
|
||||||
|
EOF
|
||||||
|
$SSH_G9 "sudo bash -s -- '$B64' '$TARGET'" <<'EOF' || { echo "ERREUR : fichier restauré absent"; exit 1; }
|
||||||
|
rel=$(printf '%s' "$1" | base64 -d)
|
||||||
|
test -f "$2$rel"
|
||||||
|
EOF
|
||||||
|
log "=== 3/3 sha256 restauré vs source .25 ==="
|
||||||
|
H_SRC=$($SSH_XP "sudo bash -s -- '$B64' '$XP_PHOTO'" <<'EOF'
|
||||||
|
f=$(printf '%s' "$1" | base64 -d)
|
||||||
|
sudo sha256sum "$2${f#/data/docker/photoprism/originals}" | cut -d' ' -f1
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
H_RST=$($SSH_G9 "sudo bash -s -- '$B64' '$TARGET'" <<'EOF'
|
||||||
|
f=$(printf '%s' "$1" | base64 -d)
|
||||||
|
sudo sha256sum "$2$f" | cut -d' ' -f1
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
$SSH_G9 "sudo rm -rf '$TARGET'"
|
||||||
|
if [[ "$H_SRC" == "$H_RST" && -n "$H_SRC" ]]; then
|
||||||
|
log "✅ sha256 identique : $H_RST"
|
||||||
|
journal "test restauration photo depuis restic" "Critère 3 : preuve restore du snapshot incluant les photos" "snapshot $SNAP contient $N entrées sous $G9_ORIG ; restore 1 fichier ; sha256 restauré == source .25 (${H_RST:0:12}…) ; chiffrement maintenu (clé locale G9)" "rm -rf $TARGET (fait)"
|
||||||
|
else
|
||||||
|
log "❌ sha256 divergents : src=$H_SRC rst=$H_RST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user