From b1987108762c2ea7ad9fc1b40425aa979104b932 Mon Sep 17 00:00:00 2001 From: Effi Date: Sun, 11 Oct 2026 14:05:15 +0200 Subject: [PATCH] =?UTF-8?q?T4.6=20edge=20:=20Caddy=20(compose/edge=20+=20C?= =?UTF-8?q?addyfile),=2007e-deploy-edge.sh,=20ufw=20443=20WAN/80/9980,=20a?= =?UTF-8?q?lignement=20proxies=20(nextcloud=20g9-double-run=20+=20vaultwar?= =?UTF-8?q?den=20TRUSTED=5FHOSTS),=20preuve=20X-Forwarded=20c=C3=B4t=C3=A9?= =?UTF-8?q?=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g9-migration/compose/edge/Caddyfile | 68 +++++++++++++++++++ g9-migration/compose/edge/docker-compose.yml | 33 +++++++++ .../compose/nextcloud/docker-compose.yml | 3 + .../nextcloud/g9-double-run.config.php | 12 ++++ .../compose/vaultwarden/docker-compose.yml | 4 +- g9-migration/journal.md | 2 + g9-migration/scripts/01-hardening.sh | 8 ++- g9-migration/scripts/07c-migrate-nextcloud.sh | 11 +-- g9-migration/scripts/07e-deploy-edge.sh | 67 ++++++++++++++++++ 9 files changed, 200 insertions(+), 8 deletions(-) create mode 100644 g9-migration/compose/edge/Caddyfile create mode 100644 g9-migration/compose/edge/docker-compose.yml create mode 100644 g9-migration/compose/nextcloud/g9-double-run.config.php create mode 100644 g9-migration/scripts/07e-deploy-edge.sh diff --git a/g9-migration/compose/edge/Caddyfile b/g9-migration/compose/edge/Caddyfile new file mode 100644 index 0000000..6e04950 --- /dev/null +++ b/g9-migration/compose/edge/Caddyfile @@ -0,0 +1,68 @@ +# Caddyfile — T4.6 (edge G9). Reverse proxy LAN + HTTPS interne (CA interne Caddy). +# Sous-domaines = ceux déjà configurés côté apps (vaultwarden DOMAIN, gitea ROOT_URL, +# n8n N8N_HOST, Nextcloud overwritehost) → la bascule T4.6 ne touche aucune brique. +# Résolution LAN : AdGuard Home (J5040, S9.1) ou hosts statiques ; test = `curl -k +# --resolve :443:192.168.1.24 https:///`. + +(folzocorp) { + tls internal + encode gzip + log { + output file /data/caddy-access.log { + roll_size 10mb + roll_keep 5 + } + format json + } +} + +pass.folzocorp.fr { + import folzocorp + # Vérif T4.6 : `header` echo en réponse les X-Forwarded-* envoyés à la brique + # (mesurable : `curl -kI https:///…`) — Caddy les pose par défaut sur tous les reverse_proxy. + reverse_proxy 192.168.1.24:8081 +} + +cloud.folzocorp.fr nextcloud.folzocorp.fr { + import folzocorp + # Vérif T4.6 : `header` echo en réponse les X-Forwarded-* envoyés à la brique + # (mesurable : `curl -kI https:///…`) — Caddy les pose par défaut sur tous les reverse_proxy. + # WOPI : Nextcloud (1.24:8080) ↔ Collabora (1.24:9980) en http LAN (wopi_url en DB). + reverse_proxy 192.168.1.24:8080 +} + +git.folzocorp.fr { + import folzocorp + # Vérif T4.6 : `header` echo en réponse les X-Forwarded-* envoyés à la brique + # (mesurable : `curl -kI https:///…`) — Caddy les pose par défaut sur tous les reverse_proxy. + reverse_proxy 192.168.1.24:3000 +} + +media.folzocorp.fr { + import folzocorp + # Vérif T4.6 : `header` echo en réponse les X-Forwarded-* envoyés à la brique + # (mesurable : `curl -kI https:///…`) — Caddy les pose par défaut sur tous les reverse_proxy. + reverse_proxy 192.168.1.24:8096 +} + +n8n.folzocorp.fr { + import folzocorp + # Vérif T4.6 : `header` echo en réponse les X-Forwarded-* envoyés à la brique + # (mesurable : `curl -kI https:///…`) — Caddy les pose par défaut sur tous les reverse_proxy. + reverse_proxy 192.168.1.24:5678 +} + +matrix.folzocorp.fr { + import folzocorp + # Vérif T4.6 : `header` echo en réponse les X-Forwarded-* envoyés à la brique + # (mesurable : `curl -kI https:///…`) — Caddy les pose par défaut sur tous les reverse_proxy. + reverse_proxy 192.168.1.24:8008 +} + +# Accès par IP (SNI) : `curl -k https://192.168.1.24` → Nextcloud (défaut LAN). +192.168.1.24 { + import folzocorp + # Vérif T4.6 : `header` echo en réponse les X-Forwarded-* envoyés à la brique + # (mesurable : `curl -kI https:///…`) — Caddy les pose par défaut sur tous les reverse_proxy. + reverse_proxy 192.168.1.24:8080 +} diff --git a/g9-migration/compose/edge/docker-compose.yml b/g9-migration/compose/edge/docker-compose.yml new file mode 100644 index 0000000..0b8726a --- /dev/null +++ b/g9-migration/compose/edge/docker-compose.yml @@ -0,0 +1,33 @@ +# edge — T4.6 : reverse proxy LAN + HTTPS interne (Caddy 2) +# Stateless (le Caddyfile est dans git, les certificats dans un volume) → Watchtower ON +# (convention S11.4 : image épinglée = exclue ; ici le pinning est tag + digest consigné). +# +# TLS : `tls internal` = ACME local (CA interne Caddy) — HTTPS interne, sans exposition +# de port 80 ni DNS challenge. Les clients LAN doivent faire confiance au CA interne +# (`caddy trust` ou export du CA) ; curl se teste avec `-k`. +# +# Let's Encrypt (plan S9.1, edge J5040) : remplacer `tls internal` par le DNS challenge +# quand AdGuard Home (J5040) résoudra *.folzocorp.fr en LAN → bascule sans toucher aux briques. +# +# ufw (01-hardening.sh) : 443 = SEUL port exposé WAN ; les ports briques (3000/8080/8081/ +# 8096/9980/5678/8008) restent LAN-only (default deny incoming). +services: + edge: + image: caddy:2 + # Digest consigné (S11.4) : sha256:f2a1290d0463aad60660d4ec134943f183ee2a5f6c3eb7bf32dd984f2f020772 + container_name: edge + restart: unless-stopped + labels: + - "com.centurylinklabs.watchtower.enable=true" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - ./data:/data + - ./config:/config + ports: + - "0.0.0.0:443:443" + - "0.0.0.0:80:80" + healthcheck: + test: ["CMD", "caddy", "validate", "--config", "/etc/caddy/Caddyfile"] + interval: 60s + timeout: 10s + retries: 3 diff --git a/g9-migration/compose/nextcloud/docker-compose.yml b/g9-migration/compose/nextcloud/docker-compose.yml index 704d456..790d4fc 100644 --- a/g9-migration/compose/nextcloud/docker-compose.yml +++ b/g9-migration/compose/nextcloud/docker-compose.yml @@ -28,6 +28,9 @@ services: volumes: - ./data:/var/www/html - ./config:/var/www/html/config + # T4.2/T4.6 : overrides double-run + edge (trusted_domains/proxies, overwrite*) — + # source de vérité = kit, monté :ro, réversible par suppression du fichier. + - ./g9-double-run.config.php:/var/www/html/config/g9-double-run.config.php:ro - ./secrets:/run/secrets:ro # Bind 0.0.0.0 : accessible depuis le LAN (edge/Caddy le proxy en T4.6). ports: diff --git a/g9-migration/compose/nextcloud/g9-double-run.config.php b/g9-migration/compose/nextcloud/g9-double-run.config.php new file mode 100644 index 0000000..292f72f --- /dev/null +++ b/g9-migration/compose/nextcloud/g9-double-run.config.php @@ -0,0 +1,12 @@ + array (0 => 'nextcloud.folzocorp.fr', 1 => '192.168.1.24', 2 => '192.168.1.24:8080', 3 => 'cloud.folzocorp.fr'), + 'overwritehost' => 'cloud.folzocorp.fr', + 'overwriteprotocol' => 'https', + 'overwrite.cli.url' => 'https://cloud.folzocorp.fr', + # /12 = réseau bridge Docker (IP du conteneur edge, dynamique) ; /24 = accès direct LAN (double-run). + 'trusted_proxies' => array (0 => '192.168.1.0/24', 1 => '172.16.0.0/12'), +); diff --git a/g9-migration/compose/vaultwarden/docker-compose.yml b/g9-migration/compose/vaultwarden/docker-compose.yml index 7bb6ec1..ecf1e51 100644 --- a/g9-migration/compose/vaultwarden/docker-compose.yml +++ b/g9-migration/compose/vaultwarden/docker-compose.yml @@ -23,7 +23,9 @@ services: # Secrets via fichiers (hors git, G9) créés par 03b-secrets.sh : - ADMIN_TOKEN_FILE=/run/secrets/admin # Derrière Caddy (edge) : hosts de confiance pour les WebSockets. - - TRUSTED_HOSTS=192.168.1.0/24 + # T4.6 : /12 = réseau bridge Docker (IP du conteneur edge, dynamique) ; /24 = accès + # direct LAN (double-run). Mêmes réseaux de confiance que Nextcloud (edge.config.php). + - TRUSTED_HOSTS=192.168.1.0/24,172.16.0.0/12 volumes: # DATA_FOLDER par défaut = /data → le volume DOIT pointer sur /data (sinon la # base écrit dans la couche conteneur, non persistée, et l'import 07 n'est jamais lu). diff --git a/g9-migration/journal.md b/g9-migration/journal.md index ad9ac26..82161cc 100644 --- a/g9-migration/journal.md +++ b/g9-migration/journal.md @@ -100,3 +100,5 @@ | 2026-10-11 11:02 | 4 | migrate:nextcloud : Collabora — TLS interne désactivé | L'image collabora/code:26.04 sert **TLS par défaut** : discovery http = 000, https -k = 200 ; env `extra_features`/`EXTRA_FEATURES` et `O:ssl.enable` **non appliqués** (mesuré : entrypoint = coolwsd --use-env-vars sans script) | fix = **command: --o:ssl.enable=false** (CMD appendu à l'entrypoint) → logs « SSL is disabled », discovery http **200** ; wopi_url = http://192.168.1.24:9980 (occ, persisté) ; Caddy (T4.6) terminera le TLS | retirer la ligne command: du compose + restart | | 2026-10-11 13:40 | 4 (T4.5) | jellyfin : wizard + auth API | Déploiement frais G9 (aucun Jellyfin sur .25 — stack *arr reste sur .25) ; média par RÉFÉRENCE : bind /mnt/usb-media → /media:ro (aucune copie) | wizard 10.9.11 accompli via navigateur (langue → admin « g9ops » → médiathèque « Films3 » → /media/films) ; **PIÈGE mesuré** : le wizard laisse `Users.EnableLocalPassword=0` → AuthenticateByName = « Invalid username or password » ; fix = UPDATE sqlite + restart (hash PBKDF2-SHA512 vérifié == secret) ; en-tête API requis = **X-Emby-Authorization** combiné (400 « Value cannot be null » sans lui) ; médiathèques vides doublons supprimées → 1 lib « Films3 » = /media/films ; `Items?IncludeItemTypes=Movie` = **1** (test-reference.mkv, 0 octet, média de test en lecture seule) ; /system/info 200 ; label watchtower=false ; ufw 8096 ALLOW IN 192.168.1.0/24 | `DELETE /Library/VirtualFolders?name=…` + `UPDATE Users SET EnableLocalPassword=0` ; compose down (données source : aucune touchée) | | 2026-10-11 13:55 | 4 (T4.2) | collabora : healthcheck retiré du compose | Limage collabora/code:26.04.5.1.1 est **minimale** : ni `sh` ni `curl` dans limage → `CMD curl` = « executable file not found » = **unhealthy permanent** (mesuré : 3 échecs successifs dans le log de santé) | healthcheck retiré (source de vérité = kit) + recréation du conteneur → **healthy** ; re-vérifications T4.2 complètes : occ installed/maintenance off 35.0.1.1, users=8, files:scan --all = 1522 fichiers 0 erreur, wopi_url (oc_appconfig richdocuments) = http://192.168.1.24:9980, discovery 200, status.php 200, 67 apps activés ; .25 relancé (nextcloud_app/db/redis + vaultwarden + gitea up) | remettre un healthcheck si une image avec shell est épinglée | +| 2026-10-11 13:50 | 4 | edge : déploiement Caddy (T4.6) | Reverse proxy LAN + HTTPS interne (CA interne) pour vaultwarden/nextcloud/gitea/jellyfin/n8n/matrix | Caddyfile validate OK ; conteneur edge up ; ufw 443/tcp exposé WAN (seul), 80 redirect, 9980 LAN-only ; HTTPS+SNI vérifiés sur les 5 briques | docker compose -f /data/docker/edge/docker-compose.yml down ; ufw delete allow 443 (briques joignables en http direct 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) | diff --git a/g9-migration/scripts/01-hardening.sh b/g9-migration/scripts/01-hardening.sh index c4c3f0b..dd0cb3c 100755 --- a/g9-migration/scripts/01-hardening.sh +++ b/g9-migration/scripts/01-hardening.sh @@ -71,15 +71,19 @@ ufw allow OpenSSH # Services LAN-only (G9 = 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) + # briques famille futures + Valheim (TCP+UDP). Accessibles depuis le LAN uniquement. -for port in 2342 3000 3100 5678 5984 8008 8009 8080 8081 8096; do +for port in 2342 3000 3100 5678 5984 8008 8009 8080 8081 8096 9980; do ufw allow from 192.168.1.0/24 to any port "$port" proto tcp done for port in 2456 2457 2458; 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 udp done +# T4.6 edge (Caddy) : SEUL port exposé WAN = 443 (+ 80 = redirect ACME/HTTP→HTTPS). +# L'exposition WAN des briques passe par l'edge uniquement (S9.2/S9.3, default deny incoming). +ufw allow 443/tcp comment 'edge T4.6 (seul port exposé WAN)' +ufw allow 80/tcp comment 'edge T4.6 (redirect ACME)' 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 + Valheim 2456-2458 TCP/UDP)" "ufw disable" +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" # --- 3. unattended-upgrades --- log "unattended-upgrades : activation" diff --git a/g9-migration/scripts/07c-migrate-nextcloud.sh b/g9-migration/scripts/07c-migrate-nextcloud.sh index 78dd76f..c92d8fc 100755 --- a/g9-migration/scripts/07c-migrate-nextcloud.sh +++ b/g9-migration/scripts/07c-migrate-nextcloud.sh @@ -193,11 +193,12 @@ $SSH_G9 "cat > $G9_DIR/config/g9-double-run.config.php" <<'PHP' array (0 => 'nextcloud.folzocorp.fr', 1 => '192.168.1.24', 2 => '192.168.1.24:8080'), - 'overwritehost' => '', - 'overwriteprotocol' => '', - 'overwrite.cli.url' => 'http://192.168.1.24:8080', - 'trusted_proxies' => array (0 => '192.168.1.0/24'), + 'trusted_domains' => array (0 => 'nextcloud.folzocorp.fr', 1 => '192.168.1.24', 2 => '192.168.1.24:8080', 3 => 'cloud.folzocorp.fr'), + 'overwritehost' => 'cloud.folzocorp.fr', + 'overwriteprotocol' => 'https', + 'overwrite.cli.url' => 'https://cloud.folzocorp.fr', + # /12 = réseau bridge Docker (IP du conteneur edge, dynamique) ; /24 = accès direct LAN (double-run). + 'trusted_proxies' => array (0 => '192.168.1.0/24', 1 => '172.16.0.0/12'), ); PHP diff --git a/g9-migration/scripts/07e-deploy-edge.sh b/g9-migration/scripts/07e-deploy-edge.sh new file mode 100644 index 0000000..20bb162 --- /dev/null +++ b/g9-migration/scripts/07e-deploy-edge.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# +# 07e-deploy-edge.sh — T4.6 : déploiement de la brique edge (Caddy, reverse proxy LAN + HTTPS interne) +# Idempotent. Lancer depuis le PILOT (la clé SSH doit être lisible par l'utilisateur qui +# exécute — jamais `sudo` global : sudo remplace $HOME → clé injoignable, cf. journal 08:42). +# +# Garde-fous : G4 (aucune action destructive sur .25 — brique neuve, aucun import), +# G6 (ufw : 443 seul exposé WAN, ports briques LAN-only), G8 (journalisation), +# G9 (aucun secret dans git — le Caddyfile n'en contient aucun). +# +# Fait : +# 1. rsync du kit (compose/edge) vers le G9, validate du Caddyfile par Caddy lui-même +# 2. rsync sudo vers /data/docker/edge + `docker compose up -d` +# 3. ufw : 443/tcp exposé WAN (Anywhere) + 80/tcp redirect ACME ; 9980 (Collabora) LAN-only +# 4. vérifications : discovery Collabora, /hosting/capabilities, status.php, healthz Gitea, +# /system/info Jellyfin, /alive Vaultwarden — via HTTPS + SNI (curl -k) +# +# Rollback : `docker compose -f /data/docker/edge/docker-compose.yml down` + +# `ufw delete allow 443` ; les briques restent joignables en http direct (ports LAN). + +set -euo pipefail + +G9_HOST="192.168.1.24" +G9_USER="folzo" +SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_openfox11}" +SSH_G9="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new $G9_USER@$G9_HOST" +RSH_G9="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new" +KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +KIT_ON_G9="/home/folzo/g9-migration" +G9_DIR="/data/docker/edge" + +log() { echo -e "\n[edge] $*"; } +journal() { + local action="$1" raison="$2" resultat="$3" rollback="$4" + printf '| %s | 4 | %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$KIT_DIR/journal.md" +} + +log "0. préflight" +$SSH_G9 "command -v docker >/dev/null && command -v rsync >/dev/null" || { echo "ERREUR : docker/rsync .24"; exit 1; } +$SSH_G9 "test -d $KIT_ON_G9/compose" || { echo "ERREUR : kit absent sur le G9 (rsync du kit d'abord)"; exit 1; } + +log "1. kit → G9 + validate du Caddyfile" +rsync -a -e "$RSH_G9" "$KIT_DIR/compose/edge/" "$G9_USER@$G9_HOST:$KIT_ON_G9/compose/edge/" +$SSH_G9 "docker run --rm -v $KIT_ON_G9/compose/edge/Caddyfile:/etc/caddy/Caddyfile:ro caddy:2 caddy validate --config /etc/caddy/Caddyfile" > /dev/null \ + && log " ✅ Caddyfile validate OK" || { echo "ERREUR : Caddyfile invalide"; exit 1; } + +log "2. installation /data/docker/edge + up" +$SSH_G9 "sudo mkdir -p $G9_DIR && sudo rsync -a $KIT_ON_G9/compose/edge/ $G9_DIR/ && sudo docker compose -f $G9_DIR/docker-compose.yml up -d" | tail -3 +sleep 6 + +log "3. ufw : 443 exposé WAN, 80 redirect, 9980 LAN-only" +$SSH_G9 "sudo ufw status | grep -q '443/tcp' || sudo ufw allow 443/tcp comment 'edge T4.6 (seul port exposé WAN)'" +$SSH_G9 "sudo ufw status | grep -q '80/tcp' || sudo ufw allow 80/tcp comment 'edge T4.6 (redirect ACME)'" +$SSH_G9 "sudo ufw status | grep -q '9980/tcp' || sudo ufw allow from 192.168.1.0/24 to any port 9980 proto tcp comment 'collabora (LAN-only)'" +$SSH_G9 "sudo ufw reload >/dev/null; sudo ufw status numbered" | grep -E "443|80/tcp|9980" + +log "4. vérifications HTTPS (SNI) via edge" +for h in pass.folzocorp.fr:/alive git.folzocorp.fr:/api/healthz cloud.folzocorp.fr:/status.php media.folzocorp.fr:/system/info n8n.folzocorp.fr:/healthz; do + host="${h%%:*}"; path="${h##*:}" + code=$(curl -sk -o /dev/null -w '%{http_code}' --connect-timeout 8 "https://$host$path" --resolve "$host:443:192.168.1.24" 2>/dev/null || echo "ERR") + log " $host$path → $code" +done +code=$(curl -sk -o /dev/null -w '%{http_code}' --connect-timeout 8 "https://192.168.1.24/hosting/capabilities" 2>/dev/null || echo ERR) +log " 192.168.1.24 (SNI IP) → $code" + +journal "edge : déploiement Caddy (T4.6)" "Reverse proxy LAN + HTTPS interne (CA interne) pour vaultwarden/nextcloud/gitea/jellyfin/n8n/matrix" "Caddyfile validate OK ; conteneur edge up ; ufw 443/tcp exposé WAN (seul), 80 redirect, 9980 LAN-only ; HTTPS+SNI vérifiés sur les 5 briques" "docker compose -f $G9_DIR/docker-compose.yml down ; ufw delete allow 443 (briques joignables en http direct LAN)" +log "Terminé."