Ce document décrit l'architecture technique de l'infrastructure homelab. Il est divisé en deux parties :
L'approche repose sur des outils open-source éprouvés, une automatisation via Ansible et N8N, une sécurité renforcée par segmentation réseau et accès SSH par clés, un usage intelligent des LLM (local prioritaire, Claude en escalade), un serveur mail personnel (HestiaCP), un système de contrôle parental dynamique, et une stratégie complète de disaster recovery basée sur Infrastructure-as-Code.
┌─────────────────────────────────────────────────────────────────────┐
│ RÉSEAU FLAT — PAS DE VLANs │
│ │
│ Deux sous-réseaux, règles pfSense any/any entre eux : │
│ │
│ 192.168.202.0/24 (FIBRE_202_254 — SFP+ 10G) │
│ Toutes les VMs, LXCs et services applicatifs │
│ │
│ 192.168.150.0/24 (RJ45_150_254 — 1 Gbps) │
│ PBS, Synology NAS, NUC Proxmox secondaire │
└─────────────────────────────────────────────────────────────────────┘
Freebox (WAN/Internet) — IP publique : 82.64.93.34
| SFP+ DAC
v
pfSense (Netgate 6100) — WAN: 192.168.254.20
|
|── SFP+ 10G (FIBRE_202_254) ──→ Proxmox principal vmbr0
| 192.168.202.0/24
| (toutes les VMs/LXCs)
|
|── RJ45 1G (RJ45_150_254) ──→ Switch 8 ports (non manageable)
|
|── Synology NAS (.150.181)
|── PBS principal (.150.10)
|── Freebox TV
|── WiFi (domotique HA)
|── Cable plafond
|
v
Switch Cisco Business (manageable)
|── Chambre fils (switch local → 2 PCs)
|── Autres chambres
|── NUC Proxmox secondaire (.150.249)
| Machine | IP | Type | ID Proxmox | Rôle | Ports |
|---|---|---|---|---|---|
| pve-main | .101 | Physical | — | Hyperviseur principal (RTX 2060) | 8006, 9100 |
| vm-rustdesk-n8n | .9 | VM | 106 | N8N, Rustdesk, TeamSpeak, RCON | 5678, 21115, 8443, 9987, 9100 |
| nginx-proxy-manager | .10 | LXC | 101 | Reverse proxy + SSL | 80, 81, 443, 9100 |
| vm-hestiacp | .12 | VM | 110 | Mail HestiaCP (scijoly.fr) | 25, 587, 993, 8083, 9100 |
| lxc-llm | .14 | LXC | 113 | Ollama LLM (GPU RTX 2060) | 11434, 3000, 9100 |
| home-assistant | .20 | VM | 102 | Domotique | 8123 |
| vm-adguard | .24 | VM | 103 | DNS + filtrage AdGuard | 53, 80 |
| wireguard | .26 | LXC | 104 | VPN WireGuard | 10086, 9100 |
| infra-mgmt | .50 | LXC | 120 | Ansible, Prometheus, Grafana, Wiki.js | 9090, 3000, 3002, 9100 |
| uptime-monitor | .51 | LXC | 121 | Uptime Kuma | 3001 |
| netbox | .52 | LXC | 122 | NetBox (prévu) | — |
| worker-ia | .56 | LXC | 126 | Worker IA (OCR, tri mails) | 8889 |
| guillaume-trading | .105 | VM | 105 | VM Trading Windows | — |
| vm-admin | .108 | VM | 108 | Administration | — |
| vm-csgo2 | .112 | VM | 112 | Counter-Strike 2 | 27015 |
| Machine | IP | Type | Rôle |
|---|---|---|---|
| PBS | .10 | Physical | Proxmox Backup Server principal |
| Synology NAS | .181 | Physical | Stockage NAS + CloudSync |
| pve-nuc | .249 | Physical | Proxmox secondaire + PBS VM |
| pfSense | .254 | Physical | Passerelle (interface RJ45) |
| Paramètre | Valeur |
|---|---|
| Modèle | Netgate 6100 |
| Version | 25.11.1-RELEASE (FreeBSD 16.0) |
| WAN | 192.168.254.20 (derrière Freebox) |
| FIBRE_202_254 | 192.168.202.254 (SFP+ 10G → pve-main) |
| RJ45_150_254 | 192.168.150.254 (RJ45 1G → switch) |
| Règles | any/any entre les 2 réseaux |
| API REST | Installée et active (jaredhendrickson13) |
| Interfaces OPT | 6 interfaces down (200, 201, 203, 204 + LAN 100) — prêtes pour VLANs |
| Service | Hébergé sur | Port | Statut |
|---|---|---|---|
| Node Exporter v1.9.1 | 8 machines | 9100 | ✅ Actif |
| Prometheus | infra-mgmt (.50) | 9090 | ✅ 10/10 targets UP |
| Grafana | infra-mgmt (.50) | 3000 | ✅ Dashboards actifs |
| cAdvisor | vm-rustdesk-n8n (.9) | 8080 | ✅ Métriques Docker |
| Uptime Kuma | uptime-monitor (.51) | 3001 | ✅ 29 monitors |
| Alertes email | Uptime Kuma → HestiaCP SMTP | — | ✅ Fonctionnel |
| Service | Hébergé sur | Port | Statut |
|---|---|---|---|
| N8N | vm-rustdesk-n8n (.9) Docker | 5678 | ✅ Actif |
| Workflow restart | N8N → SSH → infra-mgmt | — | ✅ Validé |
| Script restart-service.sh | infra-mgmt (.50) | — | ✅ 17 services |
| Wiki.js | infra-mgmt (.50) | 3002 | ✅ Sync Git 5min |
| Paramètre | Valeur |
|---|---|
| Hébergé sur | infra-mgmt (192.168.202.50) |
| Répertoire | /opt/ansible/ |
| Clé SSH | ed25519, déployée sur 8 machines |
| Machines joignables | 7/7 (manque AdGuard, HA, Windows) |
| Domaine | Usage |
|---|---|
| dev13.fr | Services (proxy hosts NPM) |
| scijoly.fr | Mail (HestiaCP) |
| IP publique | 82.64.93.34 |
┌─────────────────────────────────────────────────────────────────────┐
│ 192.168.202.0/24 — Réseau principal │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ infra-mgmt │ │ uptime- │ │ netbox │ │
│ │ .50 │ │ monitor .51 │ │ .52 (prévu) │ │
│ │ Ansible │ │ Uptime Kuma │ │ NetBox │ │
│ │ Prometheus │ │ (3001) │ │ │ │
│ │ Grafana │ └─────────────┘ └─────────────┘ │
│ │ Wiki.js │ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ vm-rustdesk │ │ lxc-llm │ │ NPM │ │
│ │ -n8n .9 │ │ .14 │ │ .10 │ │
│ │ N8N (5678) │ │ Ollama │ │ Reverse │ │
│ │ Rustdesk │ │ (11434) │ │ Proxy (81) │ │
│ │ TeamSpeak │ │ GPU RTX2060 │ │ SSL (443) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ vm-hestiacp │ │ home- │ │ vm-adguard │ │
│ │ .12 │ │ assistant │ │ .24 │ │
│ │ Mail SMTP │ │ .20 │ │ DNS + │ │
│ │ (587/993) │ │ Domotique │ │ Filtrage │ │
│ └─────────────┘ │ (8123) │ │ (53/80) │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ wireguard │ │ vm-jitsi │ + VMs non-critiques : │
│ │ .26 │ │ .13 │ guillaume-trading (.105) │
│ │ VPN (10086) │ │ Jitsi (80) │ workertest (.111) │
│ └─────────────┘ └─────────────┘ vm-csgo2 (.112) │
│ vm-admin (.108) │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 192.168.150.0/24 — Réseau stockage/backup │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PBS │ │ Synology │ │ pve-nuc │ │
│ │ .10 │ │ .181 │ │ .249 │ │
│ │ Backups │ │ NAS + │ │ Proxmox │ │
│ │ (8007) │ │ CloudSync │ │ secondaire │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ pfSense │ Netgate 6100
│ .202.254 │ any/any entre les 2 réseaux
│ .150.254 │ API REST active
└──────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ FLUX ACTUEL (fonctionnel) │
│ │
│ node_exporter (8 machines, :9100) │
│ │ │
│ v │
│ Prometheus (.50:9090) ──→ Grafana (.50:3000) │
│ │ Dashboards Node Exporter + cAdvisor │
│ │ │
│ Uptime Kuma (.51:3001) │
│ │ 29 monitors (HTTP/TCP/Ping) │
│ │ │
│ v │
│ SMTP HestiaCP (.12:587) │
│ │ │
│ v │
│ Email alertes → lpascalin@scijoly.fr │
└──────────────────────────────────────────────────────────────────┘
Uptime Kuma détecte DOWN
→ Email d'alerte
→ (TODO: bouton webhook dans l'email)
→ N8N (.9:5678) reçoit webhook
→ Nœud SSH → infra-mgmt (.50)
→ /opt/ansible/scripts/restart-service.sh {service_id}
→ SSH vers machine cible
→ systemctl restart / docker restart
| Disque | Modèle | Taille | Usage | Santé |
|---|---|---|---|---|
| /dev/nvme0n1 | Samsung 860 EVO | 200 Go | LVM (système + VMs rapides) | OK (9% wear) |
| /dev/sda | Samsung 870 QVO | 1 To | LVM (VMs/stockage rapide) | OK (3% wear) |
| /dev/sdb + /dev/sdd | Seagate 8TB ×2 | 2×8 To | ZFS mirror (HDD_8Terra_RAID) | OK |
| /dev/sdc | Samsung 840 PRO | 256 Go | Boot (BIOS+EFI+LVM) | ⚠️ 31% wear, 9 ans |
| Disque | Modèle | Taille | Usage | Santé |
|---|---|---|---|---|
| /dev/sda | HGST 1TB | 1 To | PBS datastore | ⚠️ 8 pending sectors |
| /dev/sdb | Kingston 120GB | 120 Go | Boot + OS Proxmox | OK (0% wear) |
L'objectif est de passer du réseau flat actuel à une architecture segmentée par VLANs, avec CI/CD, documentation auto-générée, mises à jour automatiques contrôlées, et disaster recovery complet.
┌─────────────────────────────────────────────────────────────────────┐
│ VLAN GESTION (10) │
│ Sous-réseau à définir │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Ansible │ │ Grafana │ │ Uptime │ │ Netbox │ │
│ │ Control │ │ + Prom. │ │ Kuma │ │ (IPAM/Topologie) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ N8N │ │ GitLab │ │ Wiki.js │ │ HestiaCP │ │
│ │(Workflow)│ │ (CI/CD) │ │ (Doc) │ │ (Mail) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ VLAN SERVICES (20) │
│ Sous-réseau à définir │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ LLM Local│ │ Rustdesk │ │Teamspeak │ │ CS Server│ │
│ │ (Ollama) │ │ │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Jitsi │ │ WireGuard│ │ NPM │ │
│ │ │ │ (VPN) │ │ (Proxy) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ VLAN IoT (30) │
│ ┌──────────────┐ ┌──────────────────────────┐ │
│ │Home Assistant │ │ Appareils domotique │ │
│ │ │ │ + AdGuard DNS │ │
│ └──────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ VLAN ENFANTS/INVITÉS (40) │
│ ┌──────────────────────────────────────────┐ │
│ │ Accès contrôlé — Système Parental actif │ │
│ │ pfSense API + AdGuard API = dynamique │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
# VLAN Gestion (10) — Accès total (admin)
ALLOW Gestion → ANY
# VLAN Services (20) — Internet + ports monitoring entrants
ALLOW Services → Internet
ALLOW Services → Gestion:monitoring_ports (9090, 3000, 3001)
DENY Services → IoT
DENY Services → Invités
# VLAN IoT (30) — Uniquement HA ↔ N8N
ALLOW IoT:home-assistant → Gestion:n8n:5678 (webhooks)
DENY IoT → Gestion (sauf exception ci-dessus)
DENY IoT → Invités
DENY IoT → Internet (sauf NTP + firmware updates)
# VLAN Invités (40) — Internet uniquement
ALLOW Invités → Internet
DENY Invités → Gestion
DENY Invités → Services
DENY Invités → IoT
| Équipement | Manageable | Impact |
|---|---|---|
| Switch 8 ports (proche pfSense) | ❌ Non | Ne peut pas tagger les VLANs — limitation |
| Switch Cisco Business (plafond) | ✅ Oui | Peut gérer les VLANs vers chambres/NUC |
| pfSense | ✅ Oui | 6 interfaces OPT disponibles (physiquement séparées) |
État actuel : Ansible fonctionnel sur infra-mgmt (.50), 7 machines joignables, playbooks 00-01-02 appliqués.
Cible : Structure complète avec rôles, CI/CD, hook nouvelles machines.
/opt/ansible/
├── ansible.cfg
├── inventory/
│ └── hosts.yml # Inventaire (IPs réelles 192.168.202.x)
├── group_vars/
│ └── all.yml # Variables globales
├── playbooks/
│ ├── 00-bootstrap.yml # Clé SSH + sécurisation
│ ├── 01-base.yml # NTP, DNS, packages
│ ├── 02-monitoring.yml # Prometheus stack
│ ├── 02-node-exporter.yml # node_exporter sur toutes les cibles
│ ├── 05-maintenance.yml # Nettoyage, updates
│ └── 06-auto-update.yml # (futur) Mises à jour par vagues
├── roles/
│ ├── ssh-keys/ # Déploiement clé SSH + hardening
│ ├── node-exporter/ # Installation node_exporter
│ ├── gpu-exporter/ # (futur) nvidia_gpu_exporter
│ └── base-security/ # (futur) Fail2ban, UFW
├── templates/
│ └── timesyncd.conf.j2 # Template NTP
└── scripts/
└── restart-service.sh # Script restart pour N8N (17 services)
# roles/ssh-keys/tasks/main.yml
- name: Déployer la clé publique
authorized_key:
user: "{{ ansible_user }}"
key: "{{ admin_ssh_public_key }}"
state: present
- name: Désactiver l'authentification par mot de passe
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
notify: restart sshd
- name: Limiter les tentatives de connexion
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?MaxAuthTries'
line: 'MaxAuthTries 3'
notify: restart sshd
État actuel : Fonctionnel. Prometheus 10/10 UP, Grafana avec dashboards, Uptime Kuma 29 monitors, alertes email.
Cible : Ajouter gpu-exporter, smartctl_exporter, alertes Prometheus (Alertmanager → N8N), node_exporter sur AdGuard.
# /etc/prometheus/prometheus.yml (sur infra-mgmt 192.168.202.50)
global:
scrape_interval: 30s
scrape_configs:
- job_name: 'proxmox-hosts'
static_configs:
- targets:
- '192.168.202.101:9100' # pve-main
- '192.168.150.249:9100' # pve-nuc
- job_name: 'lxc'
static_configs:
- targets:
- '192.168.202.10:9100' # NPM
- '192.168.202.26:9100' # WireGuard
- '192.168.202.14:9100' # LXC-LLM
- '192.168.202.50:9100' # infra-mgmt
- job_name: 'vms'
static_configs:
- targets:
- '192.168.202.12:9100' # HestiaCP
- '192.168.202.9:9100' # Rustdesk-N8N
- job_name: 'docker'
static_configs:
- targets:
- '192.168.202.9:8080' # cAdvisor
# /etc/prometheus/rules/infrastructure.yml (CIBLE)
groups:
- name: infrastructure
rules:
- alert: HighCPU
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
for: 5m
labels:
severity: warning
- alert: HighMemory
expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
for: 5m
labels:
severity: warning
- alert: DiskSpaceLow
expr: (1 - node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 > 90
for: 1m
labels:
severity: critical
- alert: GPUHighTemp
expr: nvidia_gpu_temperature_celsius > 85
for: 2m
labels:
severity: critical
État actuel : Uptime Kuma → SMTP HestiaCP (.12:587) → email lpascalin@scijoly.fr. Fonctionne.
Cible : Ajouter Alertmanager → webhook N8N → anti-spam 15 min → format → envoi. Chemin secondaire via NUC.
Prometheus Alert → Alertmanager → Webhook N8N (.9:5678)
│
Uptime Kuma Down → Webhook N8N ────────→│
▼
┌──────────────────┐
│ N8N Workflow │
│ - Anti-spam 15m │
│ - Format email │
│ - SMTP .12:587 │
│ - Log événement │
└──────────────────┘
│
v
lpascalin@scijoly.fr
Script bash autonome sur pve-nuc (.150.249)
│ ping + check API Proxmox (.202.101:8006) toutes les 60s
│
│ Si pve-main DOWN > 10 min :
│ → relay SMTP externe (Gmail App Password)
│ → alerte email indépendante
│ → séquence WoL + failover VMs critiques
SMTP Host : 192.168.202.12 (vm-hestiacp)
Port : 587 (STARTTLS)
Expéditeur : alertes@scijoly.fr
Destinataire : lpascalin@scijoly.fr
État actuel : Wiki.js sur infra-mgmt (.50:3002), sync Git toutes les 5 min depuis GitHub. Repo GitHub sylexozorus/infrastructure.
Cible : NetBox pour IPAM/topologie interactive, Wiki.js bidirectionnel, GitLab self-hosted (phase 2).
| Paramètre | Valeur |
|---|---|
| URL | http://192.168.202.50:3002 |
| Mode sync | Pull from GitHub |
| Repo | github.com/sylexozorus/infrastructure |
| Intervalle | 5 minutes |
| Direction | Unidirectionnelle (GitHub → Wiki) |
État actuel : N8N actif (.9:5678), workflow restart validé, Ollama actif (.14:11434).
Audit Ollama (2026-06-29) :
| Paramètre | Valeur |
|---|---|
| Version | 0.30.11 |
| Driver NVIDIA | 550.163.01 (stable, NE PAS TOUCHER) |
| CUDA | 12.4 |
| GPU | RTX 2060 6144 MiB |
| VRAM libre (repos) | 5745 MiB |
| Disque | 81% utilisé (13 Go libres) |
| Uptime | 68 jours |
Modèles installés :
| Modèle | Taille | Params | Quantization | Tient en VRAM | Recommandation |
|---|---|---|---|---|---|
| qwen2.5:7b | 4.7 Go | 7.6B | Q4_K_M | ✅ Oui | Modèle principal — meilleur pour classification, résumé, tri |
| llama3:8b | 4.7 Go | 8B | Q4_0 | ✅ Oui | Backup polyvalent |
| mistral-nemo | 7.1 Go | 12.2B | Q4_0 | ⚠️ Limite | Garder en option pour tâches complexes |
| qwen2.5:14b | 9 Go | 14.8B | Q4_K_M | ❌ Non | À supprimer (CPU only = trop lent) |
| mistral-small | 14 Go | 23.6B | Q4_K_M | ❌ Non | À supprimer (libère 14 Go disque) |
Plan d'action Ollama :
LXC Worker-IA (126, 192.168.202.56) — déployé 2026-06-29 :
| Paramètre | Valeur |
|---|---|
| Hostname | worker-ia |
| IP | 192.168.202.56 |
| ID Proxmox | 126 |
| Cores | 4 (scalable à chaud via pct set) |
| RAM | 4 Go |
| Disque | 20 Go (17 Go libres) |
| Rôle | OCR, hashing photos, extraction PDF/Word/Excel, pipeline documents |
| Montage Synology | /mnt/synology-scan (bind mount depuis pve-main, lecture seule) |
| Accès Ollama | http://192.168.202.14:11434 (API réseau) |
Outils installés sur worker-ia :
Pipeline classement documentaire (validé en POC) :
Fichier dans /mnt/synology-scan/
│
├── PDF natif → pymupdf extrait le texte
│
├── PDF scan → pdftoppm (image) → Tesseract OCR → texte
│
├── Excel → openpyxl extrait le contenu
│
├── Word → python-docx extrait le contenu
│
└── Image → Tesseract OCR si doc, pHash si photo
│
▼
Texte extrait envoyé à Ollama (API http://192.168.202.14:11434)
│
▼
JSON structuré : {type, entreprise, date, montant, résumé, nom_fichier_proposé}
│
▼
Rangement dans dossiers + rapport + email notification
Scaling dynamique CPU (futur) :
N8N cron (5 min) :
→ Check load pve-main
→ Si load < 5 : pct set 126 -cores 8
→ Si load > 15 : pct set 126 -cores 2
→ Si heures creuses (22h-06h) : pct set 126 -cores 12
Cible : Routage LLM local → Claude, workflows maintenance, tri emails, extraction factures, filtre alertes.
N8N Workflow reçoit une requête IA
│
v
Ollama (192.168.202.14:11434)
│
├── Réponse OK (< 60s, > 20 chars) → utiliser
│
└── Timeout/vide → Escalade Claude API
│
├── Log coût
└── Circuit-breaker : max 10/jour
| Workflow | Statut | Description |
|---|---|---|
| Service Restart | ✅ Validé | SSH → infra-mgmt → restart-service.sh |
| Webhook trigger restart | 🔲 À faire | GET /webhook/restart?serviceId=xxx |
| Status All (rapport email) | 🔲 À faire | Prometheus API → résumé → email |
| Alerte email (Alertmanager) | 🔲 À faire | Anti-spam + format + SMTP |
| Rapport Hebdo | 🔲 À faire | Cron dimanche → LLM résumé → email |
| Backup Check | 🔲 À faire | PBS API → vérifier âge < 24h |
| Tri Emails | 🔲 À faire | IMAP → Ollama classifie → trie |
| Extraction Factures | 🔲 À faire | PDF → LLM → renommer → stocker |
| Contrôle Parental | 🔲 À faire | Email code → pfSense + AdGuard |
| Backup Configs | 🔲 À faire | Export pfSense/AdGuard/N8N → Git |
État actuel : PBS sur .150.10, backups quotidiens des VMs. Pas de sync vers NUC encore.
Cible : Sync critiques vers NUC, vérification intégrité hebdo, workflow N8N export configs.
Niveau 1 : PBS Principal (.150.10)
└── Backup quotidien toutes les VMs/LXCs
Niveau 2 : PBS sur NUC (.150.249)
└── Sync quotidien des VMs critiques
Niveau 3 : GitHub (cloud)
└── Config-as-code (playbooks, docker-compose, configs, workflows)
Niveau 4 : Backup offsite chiffré
└── Configs chiffrées GPG → Synology CloudSync (ShadowDrive)
État actuel : Pas encore implémenté. PBS fonctionne mais pas de procédure testée, pas de watchdog NUC.
Cible :
| Composant | Objectif |
|---|---|
| Watchdog NUC | Script bash autonome : ping pve-main toutes les 60s, failover si down > 10 min |
| Failover VMs | Restore NPM + HestiaCP + AdGuard sur NUC depuis PBS |
| Image boot | dd du disque boot pve-main → NAS, rotation 3 images |
| Clé USB Clonezilla | Restauration non-interactive depuis NFS Synology |
| RTO | 4h services critiques, 8h complet |
| Commandes manuelles max | 5 |
État actuel : Pas encore implémenté. Prérequis : Phase 6 (DR) validée.
Cible : Mises à jour par vagues avec contrôle GPU, migration tampon NUC.
| Vague | Machines | Risque |
|---|---|---|
| 1 — Non-critiques | vm-csgo2, vm-admin, guillaume-trading | Faible |
| 2 — Services | home-assistant, vm-adguard, lxc-llm, wireguard | Moyen |
| 3 — Infra critique | nginx-proxy-manager, vm-hestiacp | Élevé |
| 4 — Pilotes | vm-rustdesk-n8n (N8N + orchestration) | Très élevé |
| 5 — Hyperviseur | pve-main (kernel + drivers NVIDIA) | Critique |
Snapshot VM → apt update && apt upgrade → Reboot si nécessaire
→ Vérifications post-update (spécifiques par machine)
→ OK : cleanup snapshot, passer à la suivante
→ FAIL : rollback snapshot, STOP cycle complet, alerte
| Machine | Checks |
|---|---|
| Toutes | ping + SSH |
| Docker hosts (.9) | docker ps (tous running 2 min) |
| lxc-llm (.14) | nvidia-smi + Ollama port 11434 |
| vm-hestiacp (.12) | SMTP 587 + IMAP 993 |
| NPM (.10) | test proxy host |
| vm-adguard (.24) | dig @192.168.202.24 google.com |
| wireguard (.26) | wg0 up + handshake récent |
| pve-main (.101) | nvidia module + nvidia-smi + GPU visible + LXC LLM démarre |
État actuel : pfSense API installée et active. AdGuard API accessible. Pas encore de workflow.
Cible : Système de codes par email pour ouvrir l'accès réseau temporairement.
Email reçu sur acces@scijoly.fr
→ N8N (IMAP check 2 min)
→ Parse code + vérifier expéditeur
→ SI VALIDE :
→ POST pfSense API /firewall/rule (ouvrir accès)
→ POST AdGuard API (exception DNS)
→ Timer (2h par défaut)
→ Email confirmation → admin
→ SI INVALIDE :
→ Ignorer + alerte admin
→ Timer expire :
→ DELETE pfSense rule
→ Réactiver blocage AdGuard
→ Email notification → admin
État actuel : HestiaCP fonctionnel sur .12, domaine scijoly.fr, SMTP/IMAP actifs, SPF/DKIM configurés.
Cible : Tri automatique emails + extraction factures PDF + registre.
| Adresse | Usage | Statut |
|---|---|---|
| alertes@scijoly.fr | Notifications infra | ✅ Actif |
| lpascalin@scijoly.fr | Personnel admin | ✅ Actif |
| acces@scijoly.fr | Contrôle parental | 🔲 À créer |
| rapports@scijoly.fr | Rapports automatiques | 🔲 À créer |
État actuel : GitHub privé (sylexozorus/infrastructure), commits manuels, git pull sur infra-mgmt.
Cible : GitLab self-hosted + CI/CD automatique + mirror GitHub.
PC local → git commit + push → GitHub
→ SSH sur infra-mgmt (.50)
→ cd /opt/ansible/repo && git pull
→ ansible-playbook playbooks/XX.yml
PC local → git push → GitLab local
→ Pipeline CI :
→ Stage validate : ansible-playbook --check
→ Stage deploy : ansible-playbook (auto ou manuel)
→ Push mirror → GitHub (backup cloud)
État actuel : Wiki.js sync Git. Pas de génération automatique.
Cible : LLM génère/met à jour les runbooks à chaque changement, rappels trimestriels, PDF "Guide de survie".
| Scénario | Comportement attendu |
|---|---|
| Machine injoignable par Ansible | 3 tentatives × 2 min, puis alerte + statut "en attente" |
| Prometheus target down | Alerte après 5 min, visible Grafana |
| N8N webhook non reçu | Uptime Kuma détecte → alerte directe SMTP |
| LLM local timeout | Escalade Claude API + alerte si les deux down |
| SMTP HestiaCP échoue | 3 retries 60s, bascule Gmail App Password |
| PBS plein | Alerte à 20% restant, rotation anciennes sauvegardes |
| pfSense API indisponible | File d'attente, retry 5 min, alerte admin |
| Code parental invalide | Ignorer, loguer, alerte si > 3 tentatives/h |
| pve-main détruit | Procédure DR : Proxmox → clone Git → Ansible → 4-8h |
| Update échoue (vague N) | Rollback snapshot, STOP cycle, alerte critique |
| Machine | CPU | RAM | Disque | Rôle |
|---|---|---|---|---|
| pve-main (.101) | AMD (ancien gamer) | ~64 Go | 200G NVMe + 1T SSD + 16T ZFS + 256G boot | Hyperviseur principal |
| pve-nuc (.249) | i5-6260U (4 threads) | 23 Go | 120G SSD + 1T HDD | PBS + tampon |
| pfSense | Atom C3558 (4 cores) | — | — | Routeur/Firewall |
| LXC | ID | CPU | RAM | Disque | Rôle |
|---|---|---|---|---|---|
| infra-mgmt | 120 | 2 vCPU | 4 Go | 30 Go | Ansible, Prometheus, Grafana, Wiki.js |
| uptime-monitor | 121 | 1 vCPU | 512 Mo | 8 Go | Uptime Kuma |
| netbox | 122 | 2 vCPU | 2 Go | 15 Go | NetBox (prévu) |
| LXC | CPU | RAM | Disque | Rôle |
|---|---|---|---|---|
| gitlab | 4 vCPU | 8 Go | 30 Go | GitLab self-hosted + CI/CD |
État actuel : Ollama fonctionne (.14:11434), N8N envoie des emails, pas de routage multi-LLM ni de validation par webhook.
Cible : Proxy LLM centralisé (LiteLLM) avec routage automatique local→gratuit→payant, suivi des coûts, et emails interactifs avec boutons d'action webhook.
┌─────────────────────────────────────────────────────────────────────┐
│ LiteLLM Proxy (infra-mgmt .50:4000) │
│ API compatible OpenAI — point d'entrée unique │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ NIVEAU 1 — LOCAL (hermétique, gratuit, aucune fuite) │ │
│ │ Backend : Ollama (192.168.202.14:11434) │ │
│ │ Modèle : qwen2.5:7b (4.7 Go, Q4_K_M) │ │
│ │ Usage : classification, extraction, résumé, tri mail │ │
│ │ Trigger : données sensibles (IPs, secrets, docs perso) │ │
│ │ Fallback : llama3:8b si qwen2.5 timeout │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ si échec ou token limit │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ NIVEAU 2 — GRATUIT CLOUD (limité, données non-sensibles) │ │
│ │ Backend A : Groq (Llama 3.3 70B) — 1000 req/jour, 30 RPM │ │
│ │ Backend B : Google AI Studio (Gemini 2.5 Flash) — variable │ │
│ │ Backend C : OpenRouter free models (200 req/jour) │ │
│ │ Usage : raisonnement multi-étapes, résumés longs, analyse │ │
│ │ Trigger : requête > 8K tokens OU local échoue │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ si rate limit ou qualité insuffisante │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ NIVEAU 3 — PAYANT (budget capé, meilleur raisonnement) │ │
│ │ Backend : OpenRouter → Claude Sonnet 4.6 ($3/$15 per M) │ │
│ │ Fallback : Gemini Pro ($2/$12 per M) │ │
│ │ Usage : architecture, debugging, génération longue/complexe │ │
│ │ Budget : max 20€/mois, alertes à 50%/80%/100% │ │
│ │ Circuit-breaker : max 50 req/jour vers payant │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ MÉTRIQUES → Prometheus (:4000/metrics) │
│ LOGS → fichier JSON + rotation 30j │
│ DASHBOARD → Grafana "AI Router" │
└─────────────────────────────────────────────────────────────────────┘
| Paramètre | Valeur |
|---|---|
| Hébergé sur | infra-mgmt (192.168.202.50) — Docker ou pip |
| Port | 4000 |
| Config | /opt/litellm/config.yaml |
| Clés API | Ansible Vault → variables d'environnement |
| Mode | Proxy avec base de données SQLite (logs) |
# /opt/litellm/config.yaml
model_list:
# Niveau 1 — Local
- model_name: "local/qwen2.5"
litellm_params:
model: "ollama/qwen2.5:7b"
api_base: "http://192.168.202.14:11434"
timeout: 60
- model_name: "local/llama3"
litellm_params:
model: "ollama/llama3:8b"
api_base: "http://192.168.202.14:11434"
timeout: 60
# Niveau 2 — Gratuit
- model_name: "free/groq-llama70b"
litellm_params:
model: "groq/llama-3.3-70b-versatile"
api_key: "os.environ/GROQ_API_KEY"
rpm: 30
tpm: 12000
- model_name: "free/gemini-flash"
litellm_params:
model: "gemini/gemini-2.5-flash"
api_key: "os.environ/GOOGLE_AI_KEY"
# Niveau 3 — Payant
- model_name: "paid/claude-sonnet"
litellm_params:
model: "openrouter/anthropic/claude-sonnet-4.6"
api_key: "os.environ/OPENROUTER_API_KEY"
- model_name: "paid/gemini-pro"
litellm_params:
model: "openrouter/google/gemini-2.5-pro"
api_key: "os.environ/OPENROUTER_API_KEY"
router_settings:
routing_strategy: "cost-based" # least-cost first
num_retries: 2
timeout: 90
allowed_fails: 3
fallbacks:
- "local/qwen2.5": ["local/llama3", "free/groq-llama70b"]
- "free/groq-llama70b": ["free/gemini-flash", "paid/claude-sonnet"]
general_settings:
master_key: "os.environ/LITELLM_MASTER_KEY"
database_url: "sqlite:///opt/litellm/litellm.db"
max_budget: 20.0 # €/mois
budget_duration: "1mo"
┌──────────────────────────────────────────────────────────────────┐
│ BOUCLE DE VALIDATION │
│ │
│ 1. Événement détecté (alerte, fin de batch, action proposée) │
│ │ │
│ v │
│ 2. N8N évalue la catégorie : │
│ ├── Informationnel → notification pure (pas de bouton) │
│ ├── Standard → email avec [APPROUVER] [REFUSER] [REPORTER] │
│ └── Critique → email avec [APPROUVER] [REFUSER] seulement │
│ │ │
│ v │
│ 3. Génération du token unique (UUID v4) + stockage en BDD N8N │
│ │ │
│ v │
│ 4. Email envoyé via HestiaCP SMTP (.12:587) │
│ │ │
│ │ ┌─────────────────────────────────────┐ │
│ │ │ EMAIL DE VALIDATION │ │
│ │ │ │ │
│ │ │ 🔔 Action proposée : │ │
│ │ │ "Redémarrer NPM (down depuis 5min)" │ │
│ │ │ │ │
│ │ │ Contexte : Uptime Kuma a détecté │ │
│ │ │ NPM down à 14:32. Dernière erreur : │ │
│ │ │ "502 Bad Gateway" │ │
│ │ │ │ │
│ │ │ Recommandation IA : APPROUVER │ │
│ │ │ (confiance 95% — redémarrage safe) │ │
│ │ │ │ │
│ │ │ [✅ APPROUVER] [❌ REFUSER] │ │
│ │ │ [⏰ Reporter 1h] [📋 Détails] │ │
│ │ │ │ │
│ │ │ ⏳ Expire dans 2h (16:32) │ │
│ │ └─────────────────────────────────────┘ │
│ │ │
│ v │
│ 5. Admin clique sur un bouton │
│ │ → GET https://n8n.dev13.fr/webhook/validate/{token} │
│ │ ?action=approve │
│ │ │
│ v │
│ 6. N8N reçoit le webhook : │
│ ├── Vérifie token valide + non expiré + non utilisé │
│ ├── Exécute l'action (SSH restart, etc.) │
│ ├── Marque le token comme consommé │
│ └── Envoie email de confirmation avec résultat │
│ │
│ 7. Si timeout (pas de clic avant expiration) : │
│ ├── Standard → action annulée + email rappel │
│ └── Critique → rappel à mi-parcours + annulation finale │
└──────────────────────────────────────────────────────────────────┘
Base URL : https://n8n.dev13.fr/webhook/ (via NPM reverse proxy)
ou http://192.168.202.9:5678/webhook/ (interne)
Endpoints :
GET /webhook/validate/{token}?action=approve → exécuter l'action
GET /webhook/validate/{token}?action=reject → annuler
GET /webhook/validate/{token}?action=postpone&delay=1h → reporter
GET /webhook/validate/{token}?action=details → renvoyer email détaillé
Sécurité :
- Token UUID v4, usage unique
- Expiration configurable (30min / 2h)
- Log IP + timestamp de chaque clic
- Réponse HTML simple ("✅ Action exécutée" ou "❌ Token expiré")
Tous les workflows N8N qui appellent Ollama directement vont migrer vers l'endpoint LiteLLM :
AVANT : HTTP Request → http://192.168.202.14:11434/api/generate
APRÈS : HTTP Request → http://192.168.202.50:4000/v1/chat/completions
Header: Authorization: Bearer ${LITELLM_KEY}
Body: {"model": "local/qwen2.5", "messages": [...]}
LiteLLM gère le routing, les retries et le fallback de manière transparente.
| Panel | Métrique |
|---|---|
| Requêtes/heure par niveau | litellm_requests_total |
| Coût cumulé du mois | litellm_cost_total_euros |
| Latence par backend | litellm_response_time_seconds |
| Taux d'erreur | litellm_errors_total / litellm_requests_total |
| Budget restant | 20 - litellm_cost_total_euros |
| Top 5 workflows consommateurs | litellm_requests_by_caller |
| Phase | Tests |
|---|---|
| Phase 3 | Alertmanager → email < 5 min, chemin secondaire NUC fonctionne |
| Phase 4 | Isolation inter-VLAN, API pfSense GET/POST OK |
| Phase 5 | Ollama < 60s, escalade Claude, facture extraite |
| Phase 6 | Restore VM depuis PBS sur NUC, watchdog détecte < 15 min |
| Phase 7 | Dry-run vagues, rollback fonctionne, blocage GPU si kernel incompatible |
| Phase 8 | Code parental → accès → expiration automatique |
| Phase 9 | Runbook auto-régénéré, pipeline CI/CD, mirror GitHub OK |