Files
edubox/SUIVI_VPN_ONDEMAND.md
T
EduBox Dev 33d89c66c0 fix(agent): v0.3.10 cleanup orphan instance dirs on startup
- Add cleanupOrphanInstanceDirs() to remove leftover instance directories
  after failed deletes (common on Windows when compose.log is locked)
- Log RemoveAll errors in dockerComposeRm for better visibility
- Bump version to 0.3.10 and rebuild binaries
2026-06-27 21:36:02 +00:00

962 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Suivi VPN on-demand studioE5 (client A)
## ✅ Ce qui fonctionne
1. **Agent standalone (mode console / systray)**
- Exécutable : `agent/studioE5-agent`
- Config lu depuis `<data-dir>/studioE5-config.json`
- Mode console : `-no-tray`
2. **VPN on-demand dans l'agent**
- Lagent ne démarre plus Tailscale au boot.
- Le VPN se lance automatiquement à la création/démarrage dune instance, ou sur commande serveur.
- Implémentation basée sur les binaires `tailscaled` + `tailscale up` (pas `tsnet`, car `tsnet` ne loguait pas automatiquement avec une authkey sur un state vierge).
3. **Commandes serveur → agent**
- Endpoint de test : `POST /api/internal/send-to-node`
- Actions supportées : `start_vpn`, `stop_vpn`, `start`, `stop`, `reset`, `delete`.
4. **Resolver/serveur dans le tailnet studioe5**
- Service `resolver-vpn` (conteneur Tailscale) partage le netns du `resolver`.
- Le resolver peut joindre les IPs Tailscale des nodes (`ping 100.64.0.x` OK).
5. **Instance WordPress démarrée avec succès**
- Le resolver a renvoyé une 302 WordPress via `http://resolver:2020/`.
6. **Activation zéro-config de lagent (modèle commercialisable)**
- Lagent démarre sans `headscale_url` ni `headscale_auth_key`.
- Lutilisateur entre seulement un code dactivation.
- Le serveur envoie la config Headscale, lagent la sauvegarde et démarre le VPN automatiquement.
## ✅ Blocage levé
**Rate limit Lets Encrypt pour `edudeploy.com` est levé.**
Le 2026-06-23 vers 09:35 UTC, Caddy a pu obtenir un certificat Lets Encrypt pour `test-wp-001.studioe5.edudeploy.com` :
```
tls.obtain: certificate obtained successfully identifier=test-wp-001.studioe5.edudeploy.com issuer=acme-v02.api.letsencrypt.org-directory
```
Le flux complet HTTPS public est désormais validé :
```bash
curl -sS -I -L https://test-wp-001.studioe5.edudeploy.com/
# => HTTP/2 302 -> HTTP/2 200 (WordPress install.php)
```
Le DNS wildcard `*.studioe5.edudeploy.com` est en place. Caddy utilise toujours `tls { on_demand }` et émet un certificat par sous-domaine dinstance.
## 🎯 Validation du flux HTTPS public
Le 2026-06-23 09:39 UTC, le flux complet a été validé :
```text
Client (HTTPS) → Caddy (:443) → resolver (:2020) → Tailnet (100.64.0.8) → agent → WordPress (:8001)
```
Résultat :
```bash
$ curl -sS -I -L https://test-wp-001.studioe5.edudeploy.com/
HTTP/2 302
location: https://test-wp-001.studioe5.edudeploy.com/wp-admin/install.php
...
HTTP/2 200
```
- Certificat Lets Encrypt obtenu automatiquement par Caddy (`tls { on_demand }`).
- Le resolver réécrit les en-têtes `Location` et le contenu HTML pour passer de `http://` à `https://`.
## 📝 Template WordPress prêt à lemploi
Un nouveau template `wordpress-ready-wordpress-latest` a été créé et validé le 2026-06-26. Il fournit un WordPress déjà initialisé en français, prêt à lusage en classe ou en examen.
### Contenu du template
| Élément | Valeur / État |
|---|---|
| Langue | **Français** (`fr_FR`) |
| Titre du site | **Mon site wordpress** |
| Compte administrateur | **admin / admin** |
| Thème actif | **Astra** |
| Spectra | installé et **actif** |
| Yoast SEO | installé mais **inactif** |
| Mises à jour automatiques | **désactivées** (core, plugins, thèmes) |
| DNS conteneur | `8.8.8.8` + `1.1.1.1` pour permettre laccès à `api.wordpress.org` |
### Architecture technique
- Le modèle `Template` de Prisma dispose dun nouveau champ `initScript` (`TEXT?`).
- Le seed génère le template avec :
- une section `dns` dans le service `app` du `docker-compose.yml` ;
- un service sidecar `wp-init` (image `wordpress:cli`) exécutant le script dinitialisation.
- Lagent écrit le script `wp-init.sh` dans le dossier de linstance au démarrage.
- Le conteneur `wp-init` attend que WordPress soit prêt, puis exécute WP-CLI en tant que `www-data`.
- Un fichier flag `.studioe5-init-done` évite de réinitialiser linstance à chaque redémarrage.
### Fichiers modifiés / ajoutés
- `server/prisma/schema.prisma` champ `initScript` sur `Template`.
- `server/prisma/seed.ts` génération du template `wordpress-ready-wordpress-latest`.
- `server/templates/wordpress-ready/wp-init.sh` script dinitialisation WP-CLI.
- `server/app/api/instances/route.ts` envoi de `initScript` à lagent avec remplacement des placeholders.
- `agent/websocket.go` réception et transmission de `InitScript`.
- `agent/docker.go` écriture du script dans le dossier instance (`writeInitScript`).
### Validation
Instance de test créée via lAPI (`cmqv03a6v0001vg8zrpe8zqfy`) :
```bash
$ curl -sS -I -L https://cmqv03a6v0001vg8zrpe8zqfy.studioe5.edudeploy.com/
HTTP/2 200
```
- Page daccueil en français, titre **« Mon site wordpress »**.
- Connexion admin `/wp-login.php` avec **admin / admin** fonctionnelle.
- Tableau de bord en français.
- Plugins : Spectra actif, Yoast SEO inactif.
- `wp-config.php` contient les constantes de désactivation des mises à jour automatiques.
Les instances de test ont été nettoyées après validation.
### Template versionné WordPress 7.0.0
Un second template `wordpress-ready-wordpress-7.0.0-php8.3` a été ajouté pour figer WordPress sur la version **7.0.0** (PHP 8.3) au lieu de `latest`. Cela garantit que tous les postes déploient exactement la même version, sans dépendre du cache local de `wordpress:latest`.
| Template | Image Docker |
|---|---|
| `wordpress-ready-wordpress-latest` | `wordpress:latest` |
| `wordpress-ready-wordpress-7.0.0-php8.3` | `wordpress:7.0.0-php8.3` |
## 📁 Fichiers modifiés (non exhaustif)
- `agent/tailscale.go` lancement `tailscaled` + `tailscale up`, gestion start/stop/status.
- `agent/websocket.go` handlers `start_vpn` / `stop_vpn`, `ensureTailscale()`.
- `agent/docker.go` remplacement des placeholders `{PORT}` et `{INSTANCE_ID}` dans les compose.
- `docker-compose.yml` ajout du sidecar `resolver-vpn`, suppression des `cap_add`/`ip route` obsolètes sur `server`/`resolver`.
- `Caddyfile` configuration on-demand TLS pour les instances.
- `.env` clé pré-auth Headscale mise à jour (clé réutilisable).
## 🧪 Tests / environnement de test actuel
Agent de test lancé en arrière-plan :
- data-dir : `/tmp/studioe5-test-clienta`
- node-id : `vps-8fc665eb`
- tailnet IP actuelle : `100.64.0.8`
- PID : voir `/tmp/studioe5-test-clienta/agent.pid` (relancé le 2026-06-26 11:53 UTC avec lagent v0.3.5 corrigé)
Instance de test créée :
- ID : `test-wp-001`
- Node : `vps-8fc665eb`
- Port : `8001`
- Template : `wordpress-wordpress-latest`
- État : WordPress répond sur `http://127.0.0.1:8001` **et en HTTPS public sur `https://test-wp-001.studioe5.edudeploy.com/`**.
## 🪟 Fix agent Windows v0.3.1
Problème rencontré sur le PC de test (`OMEGA-GAMER-dc166b1a`) :
- Le nœud apparaissait `online` dans le dashboard mais sans IP Tailscale.
- `tailscale.exe ip -4` retournait une erreur de connexion au socket local.
Cause racine :
- Lagent lançait `tailscaled` avec `--socket=<fichier>.sock`, mais **Tailscale sur Windows utilise des named pipes** (`\\.\pipe\...`), pas des sockets Unix.
- De plus, les commandes `podman`/`docker`/`tailscale` ouvraient une fenêtre console à chaque exécution.
Corrections apportées (`agent/tailscale.go`, `agent/docker.go`, `agent/instance.go`, `agent/systray.go`, `agent/ui.go`, `agent/main.go`) :
- Sur Windows, utilisation de la named pipe `\\.\pipe\studioe5-tailscaled`.
- Application de `hideWindow` à tous les processus enfants (Tailscale, Podman, Docker, ouverture navigateur, redémarrage agent).
- Redirection des logs agent vers `<data-dir>/agent.log` et des logs `tailscaled` vers `<data-dir>/tailscale/tailscaled.log`.
- Suppression de `--operator=root` sur Windows (non pertinent).
- Ajout de `--unattended` au `tailscale up` sur Windows pour que le daemon reste connecté après la déconnexion du client CLI.
- Correction du chemin `dataDir` passé à `startTailscale` (évitait un double dossier `tailscale/tailscale`).
Validation manuelle sur Windows :
```powershell
.\tailscaled.exe --state="C:\...\data\tailscale.state" --socket="\\.\pipe\studioe5-tailscaled" --tun=userspace-networking
.\tailscale.exe --socket="\\.\pipe\studioe5-tailscaled" status # => Logged out (NeedsLogin)
```
## 🪟 Agent v0.3.5 forwarding entrant Windows + UI locale + cycle de vie
### Problème
Sur Windows, Tailscale en `userspace-networking` ne forwarde pas automatiquement les connexions entrantes du Tailnet vers `localhost`. Résultat : les URLs publiques retournaient une erreur 502/timeout, bien que lagent soit `online`.
Logs caractéristiques :
```text
client -> backend close connection: close tcp 100.64.0.12:8080->100.64.0.11:xxxxx: endpoint not connected
```
### Solution : `tailscale serve` automatique
Lagent configure automatiquement un proxy TCP pour chaque instance démarrée :
```powershell
tailscale serve --bg --tcp=<port> tcp://localhost:<port>
```
| Action agent | Commande Tailscale |
|--------------|--------------------|
| Démarrage dinstance | `serve --bg --tcp=<port> tcp://localhost:<port>` |
| Arrêt dinstance | `serve --bg --tcp=<port> off` |
| Suppression dinstance | `serve --bg --tcp=<port> off` |
| Redémarrage de lagent | reconfiguration pour les instances déjà `running` |
Fichiers modifiés : `agent/tailscale.go`, `agent/websocket.go`, `agent/main.go`, `agent/ui.go`.
### UI locale modernisée
- Tableau de bord avec indicateurs de service.
- Liste des applications avec badges de statut.
- Boutons daction par instance : **Démarrer**, **Arrêter**, **Redémarrer**, **Supprimer**.
- Panneau de logs et diagnostic intégré.
- Panneau de configuration (URL serveur, Headscale, node ID).
### Cycle de vie des instances
- **Arrêter** → `docker compose stop` (volumes conservés).
- **Démarrer** → `docker compose start` (ou `up -d` la première fois).
- **Redémarrer** → `docker compose down -v` + recréation (données remises à zéro).
- **Supprimer** → `docker compose down -v` + suppression des fichiers.
- À la fermeture de lagent, les instances en cours sont arrêtées proprement (`stop`) et le serveur est notifié (`instance_stopped`).
### Démarrage du VPN après activation
Lagent redémarre `tailscaled` automatiquement au lancement, même si la clé pré-auth a déjà été utilisée. Il se base sur l’état persistant `tailscaled.state` (`tailscale up` sans `--authkey`).
### Téléchargement
- **Windows (archive)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.5-windows.zip`
- **Windows (exe)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.5.exe`
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.5`
## 🪟 Agent v0.3.6 recover() dans les goroutines de démarrage dinstance
### Problème
Lors de la création dune instance depuis le dashboard vers certains agents (notamment Windows), lagent sarrêtait brutalement. Le `recover()` présent dans `handleMessage` ne capturait pas le panic car celui-ci survenait dans les goroutines lancées par `go handleStartInstance(...)`.
### Corrections apportées
- Ajout dun `defer recover()` dans `handleStartInstance` ; en cas de panic, linstance passe en statut `error` et un message `instance_error` est envoyé au serveur.
- Ajout dun `defer recover()` dans toutes les goroutines critiques du WebSocket :
- `start_vpn`
- `stop_vpn`
- `start`
- `reset`
- `startTailscaleAndReport`
- cleanup au shutdown
- Ajout de logs de traçage au début de `handleStartInstance` (`instance`, `type`, `port`, `dataDir`, `initScriptLen`).
### Téléchargement
- **Windows (archive)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.6-windows.zip`
- **Windows (exe)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.6.exe`
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.6`
### Redeploiement
- Agent rebuildé en v0.3.6 pour Windows et Linux.
- Binaires versionnés copiés dans `server/public/`.
- Page `/dashboard/download` mise à jour vers la v0.3.6.
- Serveur rebuildé et redémarré.
## 🪟 Agent v0.3.7 recover() dans les notifications UI
### Problème
Lagent continuait de sarrêter brutalement lors de la création dune instance depuis le dashboard. Le crash survenait juste après les logs `Start instance ...` et `notifyUI: broadcasting to ...`, sans laisser de trace de panic. Cela pointait vers une panique dans les goroutines de notification UI ou dans l’écriture des logs vers les clients UI locaux.
### Corrections apportées
- Ajout dun `defer recover()` dans `notifyUI` pour chaque goroutine de notification.
- Ajout dun `defer recover()` dans `sendUILog` (logs diffusés aux clients UI).
- Ajout dun `defer recover()` dans `broadcastUI` (messages diffusés aux clients UI).
### Téléchargement
- **Windows (archive)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.7-windows.zip`
- **Windows (exe)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.7.exe`
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.7`
## 🪟 Agent v0.3.8 DNS automatique pour Podman machine (Windows/macOS)
### Problème
Après correction du crash, lagent Windows avec Podman échouait au `docker compose up` avec :
```text
lookup registry-1.docker.io: Temporary failure in name resolution
```
La VM Podman machine navait pas de DNS fonctionnel, ce qui empêchait le téléchargement des images Docker. Le DNS des conteneurs (`dns: 8.8.8.8` dans le compose) résout le problème à lintérieur des conteneurs, mais pas pour le pull dimages par Podman machine.
### Solution
Lagent configure automatiquement le DNS des machines Podman en cours dexécution au démarrage :
- Détection de Podman sur Windows/macOS.
- Liste des machines Podman (`podman machine list --format json`).
- Pour chaque machine `running`, exécution de :
```bash
podman machine ssh <name> sudo sh -c 'echo nameserver 8.8.8.8 > /etc/resolv.conf && echo nameserver 1.1.1.1 >> /etc/resolv.conf'
```
Fichier ajouté : `agent/podman.go`. Appel depuis `agent/main.go` au démarrage.
### Téléchargement
- **Windows (archive)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.8-windows.zip`
- **Windows (exe)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.8.exe`
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.8`
## 🐛 Fix synchronisation agent / dashboard
### Problème
Le statut affiché dans le dashboard pouvait diverger de l’état réel de lagent :
- Après un **Arrêter** lancé depuis le dashboard, linstance restait affichée comme elle l’était avant, ou disparaissait avec perte des données.
- Après une **Suppression**, linstance n’était pas retirée de la liste.
### Causes racines
1. **Action `stop` du dashboard envoyée comme `delete` à lagent** (`server/app/api/instances/route.ts`).
Lagent exécutait alors `docker compose down -v` + suppression des fichiers, cest-à-dire une suppression réelle, tout en marquant linstance `stopped` en base.
2. **Lagent ne confirmait pas les actions serveur** (`agent/websocket.go`).
Les handlers `stop` et `delete` ne renvoyaient jamais les messages `instance_stopped` / `instance_deleted` au serveur ; seule lUI locale le faisait.
3. **Le handler `stop` de lagent utilisait `dockerComposeDown`** au lieu de `dockerComposeStop`, ne respectant pas le cycle de vie documenté (arrêt = conteneurs et volumes conservés).
### Corrections apportées
| Fichier | Changement |
|---------|------------|
| `server/app/api/instances/route.ts` | Laction dashboard `stop` envoie désormais `action: "stop"` à lagent (et non plus `"delete"`). |
| `agent/websocket.go` | Le cas `stop` utilise `dockerComposeStop`, puis envoie `instance_stopped` au serveur. Le cas `delete` envoie `instance_deleted` au serveur. |
| `server/lib/websocket.ts` | Utilisation de `updateMany`/`deleteMany` pour ignorer silencieusement les messages dinstances déjà absentes/supprimées (évite les erreurs Prisma en double suppression). |
### Résultat
Le dashboard reflète désormais l’état réel après une action serveur-initiée, dès le rechargement de la page. Le cycle de vie respecte la sémantique attendue :
- **Arrêter** : `docker compose stop` → statut `stopped`.
- **Démarrer** : `docker compose up -d` → statut `running`.
- **Redémarrer** : `docker compose down -v` + recréation.
- **Supprimer** : `docker compose down -v` + suppression fichiers.
### Redeploiement effectué le 2026-06-26
- **Agent rebuildé** en v0.3.5 (`agent/studioE5-agent`, `.exe`, `.zip` et `server/public/` mis à jour).
- **Serveur rebuildé et redémarré** (`docker compose up -d --build server`) pour intégrer les corrections TypeScript.
- **Page `/dashboard/download` mise à jour** : passage à la version 0.3.5 et ajout des liens Windows (.exe, .zip) et Linux.
- **Corrections défensives agent** après signalement darrêt brutal lors dactions dashboard :
- `sendMessage` exécuté de manière asynchrone (`go`) dans les handlers `stop`, `delete`, `stop_vpn` et cleanup, pour ne pas bloquer la boucle de lecture WebSocket.
- Ajout dun `recover` dans `handleMessage` pour capturer d’éventuels panics sans tuer lagent.
- Correction du cleanup `main.go` : modification de `inst[id].Status` (et non de la copie locale `info`).
- **Agent de test Linux relancé** (PID dans `/tmp/studioe5-test-clienta/agent.pid`).
- **Agents clients** : il faut redémarrer lagent sur chaque poste, ou télécharger à nouveau le binaire v0.3.5 depuis le dashboard pour Windows.
## 🛠️ Commandes utiles pour reprendre
### Voir lagent de test
```bash
pgrep -a studioe5-agent
```
### Relancer lagent de test (si besoin)
```bash
mkdir -p /tmp/studioe5-test-clienta
cat > /tmp/studioe5-test-clienta/studioE5-config.json <<EOF
{
"server": "wss://studioe5.edudeploy.com/api/websocket",
"headscale_url": "https://headscale.studioe5.edudeploy.com",
"headscale_auth_key": "$(grep HEADSCALE_AUTH_KEY /opt/studioe5-client-a/.env | cut -d= -f2)",
"node_id": "vps-8fc665eb",
"data_dir": "/tmp/studioe5-test-clienta"
}
EOF
cd /opt/studioe5-client-a/agent
./studioE5-agent -no-tray -data-dir /tmp/studioe5-test-clienta
```
### Démarrer le VPN manuellement
```bash
curl -sS -X POST https://studioe5.edudeploy.com/api/internal/send-to-node \
-H "Content-Type: application/json" \
-d '{"nodeId":"vps-8fc665eb","message":{"action":"start_vpn"}}'
```
### Voir les nodes Headscale
```bash
cd /opt/studioe5-client-a
docker compose exec -T headscale headscale nodes list studioe5
```
### Tester le resolver (depuis Caddy)
```bash
cd /opt/studioe5-client-a
docker exec studioe5-caddy curl -sS -I -H "Host: test-wp-001.studioe5.edudeploy.com" http://resolver:2020/
```
### Tester en HTTPS public (dès que la limite sera levée)
```bash
curl -sS -I -L https://test-wp-001.studioe5.edudeploy.com/
```
## 🌐 Flux complet testé via lAPI web
Test réalisé le 2026-06-23 en utilisant le compte superadmin :
1. **Authentification NextAuth** sur `/api/auth/callback/credentials`.
2. **Création dinstance** via `POST /api/instances` :
```json
{
"nodeId": "vps-8fc665eb",
"templateId": "wordpress-wordpress-latest",
"port": 8002
}
```
→ Instance créée : `cmqqgrur20001lw67t2bdgzkg`.
3. Le serveur a automatiquement envoyé laction `start` au node via WebSocket.
4. Lagent a démarré le VPN (si besoin), écrit le compose et a lancé les conteneurs WordPress.
5. Caddy a obtenu un certificat Lets Encrypt pour `cmqqgrur20001lw67t2bdgzkg.studioe5.edudeploy.com`.
6. **Validation HTTPS** :
```bash
curl -sS -I -L https://cmqqgrur20001lw67t2bdgzkg.studioe5.edudeploy.com/
# => HTTP/2 302 -> HTTP/2 200 (WordPress install.php)
```
Le flux `UI → API → WebSocket → agent → Docker → VPN → Caddy → HTTPS public` est fonctionnel.
## 💻 Téléchargement de lagent
Lagent est servi par Caddy depuis le dossier `agent/` monté dans le conteneur Caddy (`./agent:/usr/share/caddy/agent`).
### Binaires disponibles
- **Windows (archive complète)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.10-windows.zip`
- Contient `studioE5-agent.exe` + `tailscale-bin/windows/` (`tailscale.exe`, `tailscaled.exe`, `wintun.dll`) + `README-Windows.txt`.
- **Windows (exécutable seul)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.10.exe`
- Nécessite davoir installé Tailscale Windows séparément ou davoir les binaires dans `tailscale-bin/windows/`.
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.10`
### Builder / préparer les binaires
```bash
cd /opt/studioe5-client-a/agent
# 1. Télécharger les binaires Tailscale Windows (nécessite msitools)
./download-tailscale-bins.sh 1.98.4
# 2. Builder lagent pour Windows et Linux (macOS nécessite CGO)
./build.sh
```
Le `build.sh` génère automatiquement `studioE5-agent-v0.3.10-windows.zip` et copie les binaires versionnés dans `server/public/`.
### Flow dactivation zéro-config (modèle commercialisable)
L’élève/employé na **aucune configuration technique** à saisir :
1. **Télécharger** lagent Windows (`studioE5-agent-v0.3.10-windows.zip`).
2. **Extraire** et **lancer** `studioE5-agent.exe`.
3. **Entrer le code dactivation** à 6 caractères fourni par l’établissement (affiché dans lUI locale `http://localhost:7070`).
4. Lagent contacte le serveur, le serveur vérifie le code et renvoie **automatiquement** :
- lidentité de l’élève (`studentName`)
- lURL Headscale
- la clé pré-auth Headscale
5. Lagent sauvegarde ces informations localement et **démarre automatiquement le VPN**.
6. Lagent est alors visible dans le dashboard et peut recevoir des instances.
### Configuration manuelle (mode debug / admin)
Si besoin, on peut toujours forcer une config via `data/studioE5-config.json` :
```json
{
"server": "wss://studioe5.edudeploy.com/api/websocket",
"headscale_url": "https://headscale.studioe5.edudeploy.com",
"headscale_auth_key": "CLE_PREAUTH_ICI",
"node_id": "IDENTIFIANT_DU_POSTE",
"data_dir": "C:\\studioE5-agent\\data"
}
```
> ⚠️ `headscale_auth_key` doit être une clé pré-auth réutilisable valide pour le tailnet studioe5. Ne jamais commiter cette clé.
Lancement :
```powershell
.\studioE5-agent.exe -no-tray -data-dir C:\studioE5-agent\data
```
## 🔒 Durcissement du code dactivation
### Génération
- Les codes sont générés avec `crypto.randomBytes` (au lieu de `Math.random`).
- Longueur conservée à 6 caractères, alphabet sans ambiguïté (`ABCDEFGHJKLMNPQRSTUVWXYZ23456789`).
- Un champ `activationCodeExpiresAt` a été ajouté au modèle `Student` ; les codes expirent après **60 minutes**.
### Rate-limiting
- Maximum de **5 tentatives dactivation par code** sur une fenêtre de **15 minutes**.
- Maximum de **5 tentatives par `nodeId`** sur la même fenêtre.
- Au-delà, le serveur répond `activation_failed` avec `Too many attempts`.
### Cycle de vie
- Le code est **invalide après une activation réussie** (`activationCode` et `activationCodeExpiresAt` mis à `null`).
- Un code expiré renvoie `Code expired`.
- Un code déjà utilisé renvoie `Invalid code`.
### Tests validés
- Activation valide → `activated` + token node reçu.
- Code expiré → `Code expired`.
- Code déjà utilisé → `Invalid code`.
- 5+ tentatives invalides → `Too many attempts`.
## 🔒 ACL Headscale (isolation du tailnet)
### Objectif
Par défaut, tous les nœuds du tailnet peuvent communiquer entre eux. Les ACL restreignent la connectivité au strict nécessaire :
- les agents élèves ne peuvent pas se parler entre eux ;
- le resolver peut atteindre les agents sur leurs ports dinstance ;
- les agents peuvent joindre le resolver sur son port HTTP interne.
### Mise en œuvre
- Fichier de politique : `headscale/acl_policy.hujson`.
- `headscale/config.yaml` pointe vers ce fichier via `policy.path`.
- Le resolver a été déplacé dans un utilisateur Headscale dédié `resolver` (clé `HEADSCALE_RESOLVER_AUTH_KEY`).
- Les agents utilisent lutilisateur `studioe5` et sont tagués `tag:student-agent`.
- Les nouveaux agents recevront automatiquement le tag via la nouvelle clé pré-auth `HEADSCALE_AUTH_KEY` (créée avec `--tags tag:student-agent`).
### Contenu de la politique
```json
{
"groups": {
"group:agents": ["studioe5@studioe5.local"],
"group:resolvers": ["resolver@studioe5.local"]
},
"tagOwners": {
"tag:student-agent": ["studioe5@studioe5.local"],
"tag:resolver": ["resolver@studioe5.local"]
},
"acls": [
{ "action": "accept", "src": ["tag:resolver"], "dst": ["tag:student-agent:*"] },
{ "action": "accept", "src": ["tag:student-agent"], "dst": ["tag:resolver:2020"] }
]
}
```
### Tests validés
| Test | Résultat |
|------|----------|
| `resolver` ping agent | ✅ OK |
| Agent → agent (port instance) | ❌ bloqué (timeout) |
| Agent → resolver:2020 | ✅ OK |
| Flux HTTPS public | ✅ HTTP 200 |
## 🔒 Authentification du canal serveur → agent
### Token dauthentification par nœud
- Le modèle `Node` dispose dun champ `token` unique.
- Lagent envoie son token dans len-tête `Authorization: Bearer <token>` lors de la connexion WebSocket.
- Le serveur rejette toute connexion/register dont le token ne correspond pas au `nodeId` (fermeture `1008`).
- Lors de lactivation, le serveur génère un token aléatoire (32 octets hex) et le renvoie dans le message `activated` ; lagent le sauvegarde dans `<data-dir>/node.token` (permissions `0600`).
- Pour les nœuds existants sans token, le serveur en génère un à la première connexion et lenvoie via `set_token`.
### Endpoint `/api/internal/send-to-node`
- Protégé par la variable denvironnement `INTERNAL_API_KEY`.
- Requiert len-tête `Authorization: Bearer <INTERNAL_API_KEY>`.
- Appel sans clé → `401 Unauthorized`.
### Routes API métier
- Les routes de gestion des instances (`/api/instances`) requièrent une session NextAuth valide.
- Un administrateur ne peut agir que sur les ressources de son établissement ; le `superadmin` peut tout voir/tout faire.
### Endpoint `/api/resolve`
- Protégé par la même clé `INTERNAL_API_KEY`.
- Requiert len-tête `Authorization: Bearer <INTERNAL_API_KEY>`.
- Le resolver (`resolver:2020`) ne lutilise pas ; il interroge directement PostgreSQL. Cette route est donc réservée aux outils/scripts internes authentifiés.
### Exemples de commandes avec la clé interne
```bash
KEY=$(grep INTERNAL_API_KEY /opt/studioe5-client-a/.env | cut -d= -f2)
curl -sS -X POST https://studioe5.edudeploy.com/api/internal/send-to-node \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $KEY" \
-d '{"nodeId":"vps-8fc665eb","message":{"action":"start_vpn"}}'
curl -sS -H "Authorization: Bearer $KEY" \
"https://studioe5.edudeploy.com/api/resolve?subdomain=test-wp-001"
```
## 🔒 Clés pré-auth Headscale éphémères
### Principe
À lactivation zero-config, le serveur génère désormais une **clé pré-auth unique et à usage unique** pour chaque agent, au lieu denvoyer la clé réutilisable `HEADSCALE_AUTH_KEY`.
Avantages :
- une clé compromise ne permet pas denregistrer dautres nœuds ;
- traçabilité directe entre une activation et une clé Headscale ;
- expiration courte (15 min) ;
- la clé nest **pas persistée** dans `studioE5-config.json` côté agent.
### Implémentation
| Composant | Changement |
|-----------|------------|
| `server/lib/headscale.ts` | Nouveau helper : `getHeadscaleUserId()` + `createEphemeralPreAuthKey()` appelant `POST /api/v1/preauthkey`. |
| `server/lib/websocket.ts` | Sur `activate`, génère une clé éphémère taguée `tag:student-agent` pour lutilisateur `studioe5`. Fallback sur `HEADSCALE_AUTH_KEY` si `HEADSCALE_API_KEY` nest pas configurée. |
| `agent/websocket.go` | La clé reçue est utilisée immédiatement mais **nest plus écrite** dans `studioE5-config.json`. |
| `agent/tailscale.go` | `tailscale up` fonctionne sans `--authkey` quand le state Tailscale existe déjà (reconnexion). |
| `.env.example` / `docker-compose.yml` | Ajout de `HEADSCALE_API_KEY` pour le service `server`. |
### Configuration requise
Générer une clé API Headscale (depuis le conteneur ou la CLI) :
```bash
cd /opt/studioe5-client-a
# Clé valable 10 ans (87600h) pour éviter un renouvellement fréquent.
docker compose exec headscale headscale apikeys create -e 87600h
```
Puis lajouter dans `.env` :
```bash
HEADSCALE_API_KEY=hskey-api-...
```
> ⚠️ La clé API Headscale a une expiration par défaut de 90 jours. La clé de production a été créée avec une expiration de **10 ans** (`-e 87600h`). Les anciennes clés ont été révoquées.
### Rotation / renouvellement
Si la clé doit être changée :
1. Créer une nouvelle clé API :
```bash
docker compose exec headscale headscale apikeys create -e 87600h
```
2. Mettre à jour `.env` :
```bash
HEADSCALE_API_KEY=<nouvelle_clé>
```
3. Redémarrer le serveur :
```bash
docker compose up -d server
```
4. Révoquer lancienne clé :
```bash
docker compose exec headscale headscale apikeys expire --id <id_ancienne>
```
### Déploiement effectué
- Clé API créée et ajoutée au `.env` de production.
- Image serveur rebuildée et redémarrée.
- Agents Linux/Windows rebuildés en v0.3.4 et copiés dans `server/public/`.
## 🔒 Sécurité — points restants à traiter
> Le certificat wildcard `*.studioe5.edudeploy.com` est désormais du ressort du **deployeur** (voir `docs/ONBOARDING_CLIENT.md`). Les points ci-dessous concernent lapplication studioE5 proprement dite.
### Gestion et rotation des secrets
| Secret | Où ? | Action |
|--------|------|--------|
| `INTERNAL_API_KEY` | `.env` serveur | Prévoir une procédure de rotation régulière. |
| `HEADSCALE_API_KEY` | `.env` serveur | Rotation tous les 10 ans max, stockage sécurisé. |
| `NEXTAUTH_SECRET` | `.env` serveur | Génération robuste, rotation si suspicion de fuite. |
| `DATABASE_URL` | `.env` serveur | Utilisateur DB dédié, mot de passe fort. |
| `node.token` | `<data-dir>/node.token` | Vérifier permissions `0600` sur tous les OS. |
### Durcissement des conteneurs
- Limiter les `cap_add` au strict minimum.
- Faire tourner les services avec un utilisateur non-root quand possible.
- Mettre à jour régulièrement les images de base (Caddy, Node, Postgres, Headscale).
- Scanner les images Docker pour les CVE.
### Mises à jour de sécurité
- Mise à jour des binaires Tailscale (Windows et Linux).
- Mise à jour des images Docker (`server`, `resolver`, `caddy`, `postgres`, `headscale`).
- Mise à jour de lOS des VPS et des postes agents.
- Mécanisme de mise à jour automatique ou notification de lagent.
### Logs daudit
- Tracer la création / suppression dinstances.
- Tracer la génération et lusage des codes dactivation.
- Tracer les actions admin (connexion, création d’élève, démarrage/arrêt VPN).
- Conservation et consultation des logs daudit.
### Backups et reprise dactivité
- Backup régulier de la base PostgreSQL.
- Backup du state Headscale.
- Backup des states Tailscale côté agents.
- Procédure de restauration documentée et testée.
### Sécurité du build et distribution de lagent
- Vérifier lintégrité des binaires Tailscale téléchargés (checksum / signature).
- Signer lexécutable Windows `studioE5-agent.exe` pour éviter les alertes Defender.
- Fournir un hash SHA256 des archives dagent.
### RGPD et données personnelles
- Justifier la conservation des noms/prénoms des élèves.
- Gérer les droits daccès, la suppression de compte et lexport de données.
- Définir la durée de conservation des logs et historiques.
### Sécurité réseau complémentaire
- Restreindre laccès à `/api/internal/send-to-node` par IP source si possible.
- Vérifier lexposition publique du dashboard Headscale et la durcir si nécessaire.
- Évaluer si `headscale.studioe5.edudeploy.com` doit rester public.
### Rate limiting et quotas
- Rate-limiting global sur les routes publiques (`/api/auth/*`, activation, création dinstance).
- Limitation du nombre dinstances par élève et par établissement.
- Protection contre les abus sur la génération de codes dactivation.
### Tests de sécurité
- Tests dintrusion légers (agent → agent, accès aux endpoints internes sans clé, accès à une instance dun autre élève).
- Tests automatisés du flux complet avant chaque release.
---
## 🖥️ Installateur agent professionnel
### Objectif
Créer un package dinstallation unique et professionnel par OS, incluant lagent studioE5, Tailscale et **Podman CLI**, afin de ne plus dépendre dinstallations manuelles préalables par lutilisateur.
### Choix des outils
| OS | Outil | Format | Justification |
|---|---|---|---|
| **Windows** | **Inno Setup** | `.exe` | Gratuit, open source, très répandu, personnalisable, exécution de scripts PowerShell/silencieux. |
| **macOS** | **`pkgbuild`** | `.pkg` | Outil natif Apple, gratuit, format professionnel pour la distribution macOS. |
| **Linux** | **Script shell** (+ `.deb`/`.rpm` optionnels) | `.sh` | Universel, détecte le package manager, simple à maintenir. |
### Contenu du package par OS
- **Windows** (Inno Setup) :
- Installer lagent dans `C:\Program Files\studioE5-agent\`.
- Extraire Tailscale dans `C:\Program Files\studioE5-agent\tailscale-bin\windows\`.
- Installer Podman CLI via le MSI officiel en mode silencieux.
- Exécuter `podman machine init` puis `podman machine start`.
- Créer un raccourci de démarrage et/ou un service Windows.
- **macOS** (`pkgbuild`) :
- Installer lagent dans `/Applications/studioE5-agent/`.
- Installer Podman CLI.
- Exécuter `podman machine init` puis `podman machine start`.
- Optionnellement créer un LaunchAgent pour démarrer lagent au login.
- **Linux** (script shell) :
- Détecter le package manager (`apt`, `dnf`, `pacman`, etc.).
- Installer Podman et Podman Compose.
- Copier lagent dans `/opt/studioe5-agent/`.
- Créer le service systemd `studioe5-agent.service`.
- Activer et démarrer le service.
### Adaptations nécessaires dans lagent
- Détecter si Podman est utilisé et si une machine est requise (Windows/macOS).
- Vérifier au démarrage que la machine Podman est démarrée, et lancer `podman machine start` si besoin.
- Gérer proprement larrêt de la machine à la fermeture de lagent (optionnel).
### Mise à jour de lagent vs dépendances système
- **Lagent peut se mettre à jour lui-même** (binaire + fichiers) depuis la v0.3.10.
- **Podman / Docker / Tailscale restent gérés par linstallateur** : lagent vérifie leur présence et alertera lutilisateur si une dépendance est manquante ou trop ancienne, mais ne les met pas à jour automatiquement (droits élevés, risque de casser les machines Podman, etc.).
---
## 📋 Prochaines étapes à faire
### ✅ Terminé
- [x] Rate limit Lets Encrypt levé.
- [x] Flux HTTPS public validé (`test-wp-001.studioe5.edudeploy.com`).
- [x] Branche `feat/studioe5-vpn-ondemand` créée, commit `124543d`.
- [x] **Pousser la branche** vers Gitea : la branche est synchronisée sur `origin/feat/studioe5-vpn-ondemand` (commit `cf8b663`).
- [x] Flux complet UI → API → WebSocket → agent → Docker → VPN → Caddy validé.
- [x] Packager les binaires Tailscale pour Windows (`studioE5-agent-v0.3.5-windows.zip`).
- [x] **Sécurité authentification du canal serveur → agent** (token par nœud, clé API interne, sessions NextAuth sur les routes API).
- [x] **Sécurité durcissement du code dactivation** (`crypto.randomBytes`, expiration 60 min, rate-limiting, invalidation après usage).
- [x] **Sécurité ACL Headscale** (isolation agent ↔ agent, resolver → agent autorisé).
- [x] **Sécurité clés pré-auth Headscale éphémères** (génération côté serveur via `HEADSCALE_API_KEY`, non persistées côté agent).
- [x] **Agent v0.3.5 forwarding entrant Windows** (`tailscale serve` automatique au démarrage de chaque instance).
- [x] **Agent v0.3.5 UI locale moderne** (dashboard, logs, progression, actions dinstance).
- [x] **Agent v0.3.5 cycle de vie des instances** (`stop`/`start` préservent les volumes, `reset`/`delete` effacent).
- [x] **Agent v0.3.5 cleanup au shutdown** (arrêt propre de Tailscale et des instances, notification serveur).
- [x] **Synchronisation dashboard** (messages `instance_stopped` / `instance_deleted` traités côté serveur, et agent renvoyant correctement ces messages après un ordre serveur `stop`/`delete`).
- [x] **Template WordPress prêt à lemploi** (`wordpress-ready-wordpress-latest`) : WordPress en français, titre « Mon site wordpress », compte admin/admin, thème Astra, Spectra actif, Yoast SEO inactif, mises à jour automatiques désactivées, DNS `8.8.8.8`/`1.1.1.1` pour `api.wordpress.org`.
- [x] **Nettoyer les instances/agent de test** (2026-06-27) : agent de test arrêté (`vps-8fc665eb`), `tailscaled` associé arrêté, data-dir `/tmp/studioe5-test-clienta` supprimé ; **13 instances de test supprimées de la base PostgreSQL** (`vps-8fc665eb` + `OMEGA-GAMER-60d7f87c`).
- [x] **Nettoyer les anciens nodes/volumes Headscale de test** (2026-06-27) : nœuds `edubox`, `prof`, `invalid-*`, anciens `vps-8fc665eb`, anciens `studioe5-resolver` et `test-node-b` supprimés ; volume Docker anonyme orphelin supprimé.
- [x] **Centralisation de la version agent** : fichier unique `agent/VERSION`, API `GET /api/agent/version`, dashboard et route `/api/download` alignés.
- [x] **Agent v0.3.10 synchronisation agent ↔ serveur au démarrage** : protocole `sync` / `sync_response`, suppression/lancement automatique des instances décalées pendant un offline.
- [x] **Agent v0.3.10 détails techniques dans lUI locale** : version de lagent, nodeId, version attendue par le serveur, notification de mise à jour.
- [x] **Agent v0.3.10 mise à jour automatique de lagent** : détection de nouvelle version, téléchargement, remplacement du binaire via script helper et redémarrage.
- [x] **Agent v0.3.10 handlers asynchrones** : `start`, `stop`, `delete`, `reset` exécutés dans des goroutines pour ne plus bloquer la boucle WebSocket.
- [x] **Agent v0.3.10 nettoyage des dossiers instances orphelins au démarrage** : supprime les répertoires résiduels laissés par des `delete` incomplets (souvent `compose.log` verrouillé sous Windows).
### ⏳ Reste à faire
- [ ] **Certificat wildcard** : transféré au deployeur (`docs/ONBOARDING_CLIENT.md`). L’étude technique reste disponible ci-dessous pour référence.
- [ ] **Documenter la procédure de mise en production** pour le client A (config agent, clés Headscale, ports, ACL, etc.).
- [ ] **Installateur agent professionnel (Windows / macOS / Linux)** : créer un package dinstallation unique incluant lagent studioE5, Tailscale et **Podman CLI**. Voir la section « 🖥️ Installateur agent professionnel » ci-dessous pour le détail des outils (Inno Setup, pkgbuild, script shell) et du contenu par OS.
- [ ] **Template WordPress prêt à lemploi (usage examen/classe)** :
- Forcer le DNS (`8.8.8.8`, `1.1.1.1`) dans le `docker-compose.yml` pour permettre laccès à la bibliothèque de plugins/mises à jour depuis le conteneur.
- Pré-installer WordPress en **français** via WP-CLI avec le titre **“Mon site wordpress”** et le compte **admin / admin**.
- Désactiver les **mises à jour automatiques** (core, plugins, thèmes) pour figer lenvironnement.
- Installer et activer le **thème Astra**.
- Installer **Yoast SEO** (inactif) et **Spectra** (actif).
- [ ] **Barre de progression basée sur les logs dinstallation** : enrichir la barre de progression agent/dashboard en lisant les logs Podman/Docker (`podman logs -f`) pendant le premier démarrage dune instance. Définir des patterns de logs par template (ex. `Installation successful` pour PrestaShop) et relayer les étapes réelles au dashboard via WebSocket.
- [ ] **Étude interface de déploiement multi-clients** : outil de provisionning dun nouveau serveur client + agent générique (option A : URL serveur déterminée à lactivation).
- [ ] **Sécurité gestion et rotation des secrets** (`INTERNAL_API_KEY`, `HEADSCALE_API_KEY`, `NEXTAUTH_SECRET`, `DATABASE_URL`).
- [ ] **Sécurité durcissement des conteneurs** (`cap_add`, utilisateurs non-root, scans CVE).
- [ ] **Sécurité mises à jour de sécurité** (Tailscale, images Docker, OS agents).
- [ ] **Sécurité logs daudit** (instances, codes dactivation, actions admin).
- [ ] **Sécurité backups et reprise dactivité** (DB, state Headscale, states agents).
- [ ] **Sécurité intégrité et signature de lagent** (checksum Tailscale, signature Windows, hash SHA256).
- [ ] **Sécurité conformité RGPD** (données élèves, suppression de compte, export).
- [ ] **Sécurité restriction réseau** (endpoint interne, dashboard Headscale).
- [ ] **Sécurité rate limiting et quotas** (routes publiques, instances par élève/établissement).
- [ ] **Sécurité tests de sécurité** (intrusion légère, tests automatisés avant release).
## 💡 Améliorations UI
### ✅ Console / log intégrée dans lagent (v0.3.5)
Les logs de lagent sont redirigés vers `<data-dir>/agent.log` et diffusés en temps réel dans lUI locale (`http://localhost:7070`) via le WebSocket existant.
### ✅ Barre de progression (v0.3.5)
Lagent envoie des messages `progress` au frontend pendant le démarrage dune instance :
| Étape | Poids |
|-------|-------|
| Préparation de lapplication | 10 % |
| Configuration de lapplication | 30 % |
| Application en cours de démarrage | 60 % |
| Connexion sécurisée active | 80 % |
| Finalisation de linstallation | 90 % |
| Application prête | 100 % |
### Boutons daction par instance (v0.3.5)
LUI locale affiche désormais des boutons **Démarrer**, **Arrêter**, **Redémarrer** et **Supprimer** pour chaque instance.
## 🚀 Scalabilité commerciale — déploiement multi-clients
### Objectif
Permettre de déployer facilement une stack studioE5 complète pour un nouvel établissement/client sur un VPS dédié, sans intervention technique lourde.
### Architecture cible
- **Un serveur = un client** : chaque établissement a sa propre stack Docker Compose, sa base PostgreSQL, son Headscale et son Caddy.
- **Agent générique (option A)** : un seul binaire agent pour tous les clients. LURL du serveur cible est déterminée au moment de lactivation, pas hardcodée dans lagent.
- Pistes : code dactivation résolu par un hub central, code structuré contenant lidentifiant du serveur, ou champ URL serveur saisi dans lUI locale.
- **Interface de déploiement** : dashboard superadmin (hub) permettant de créer un client, provisionner le VPS, générer les secrets et retourner les informations de connexion.
### Prérequis techniques à préparer
Avant de pouvoir déployer un nouveau client en quelques clics, il faut encore préparer les éléments suivants :
| # | Élément | État | Détail |
|---|---------|------|--------|
| 1 | **Agent générique** | ⏳ À faire | `defaultServerURL` est hardcodé (`wss://studioe5.edudeploy.com/api/websocket`). Lagent doit pouvoir déterminer lURL serveur cible à lactivation (option A : champ URL, hub de résolution, ou code structuré). |
| 2 | **Script de provisionning** | ⏳ À faire | Aucun outil automatisé pour provisionner un VPS vierge : installation Docker, déploiement de la stack, génération des secrets, création des clés Headscale, configuration DNS wildcard. |
| 3 | **Registry dimages** | ⏳ À faire | Les images `server` et `resolver` sont buildées sur le serveur cible. Il faut un registry privé pour builder une fois et déployer partout. |
| 4 | **Hub central** | ⏳ À faire | Dashboard superadmin listant les clients, état des serveurs, versions déployées, logs distants et mises à jour. |
| 5 | **Mises à jour à distance** | ⏳ À faire | Mécanisme pour pousser une nouvelle version du serveur et de lagent sur tous les déploiements clients. |
| 6 | **Monitoring / support** | ⏳ À faire | Collecte centralisée de logs, alertes (serveur down, certificat expiré, agent hors ligne). |
| 7 | **Branding / personnalisation** | ⏳ À faire | Logo, nom de l’établissement, couleurs configurables par client. |
| 8 | **Tests automatisés** | ⏳ À faire | Tests du flux activation → VPN → instance → HTTPS public pour valider chaque nouveau déploiement. |
| 9 | **Documentation procédure prod** | ⏳ À faire | Procédure complète de mise en production pour un nouveau client. |
### Statut
- ⏳ À étudier et planifier plus tard. Larchitecture actuelle (un serveur par client + agent zero-config) est déjà compatible avec cette vision, mais le code nest pas encore industrialisé pour un déploiement à grande échelle.
## 🔒 Étude certificat wildcard `*.studioe5.edudeploy.com`
### Pourquoi passer en wildcard ?
Avec `tls { on_demand }`, Caddy émet **un certificat Lets Encrypt par sous-domaine dinstance**. Cela expose au rate limit de 50 certificats par domaine principal (`edudeploy.com`) sur 7 jours. Un certificat wildcard unique (`*.studioe5.edudeploy.com`) couvre tous les sous-domaines dinstances et évite ce problème.
### Contrainte technique
Un certificat wildcard nécessite le **challenge DNS-01** (le challenge HTTP-01 ne permet pas de valider `*.domain.tld`). Caddy doit donc pouvoir créer un enregistrement TXT automatiquement chez le registrar DNS.
### Infomaniak (registrar actuel)
Le DNS de `edudeploy.com` est chez **Infomaniak** :
```bash
dig NS edudeploy.com +short
# nsany1.infomaniak.com.
# nsany2.infomaniak.com.
```
Il existe un module Caddy DNS pour Infomaniak :
- Repository : `github.com/caddy-dns/infomaniak`
- Nécessite un **token API Infomaniak** avec droits DNS.
### Implémentation à envisager
1. **Générer un token API Infomaniak** (compte client A ou compte dédié avec accès au domaine).
2. **Builder une image Caddy custom** avec le module :
```dockerfile
FROM caddy:2-builder AS builder
RUN xcaddy build --with github.com/caddy-dns/infomaniak
FROM caddy:2-alpine
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
```
3. **Modifier le `Caddyfile`** pour gérer le wildcard :
```caddy
*.studioe5.edudeploy.com {
tls {
dns infomaniak {env.INFOMANIAK_API_TOKEN}
}
reverse_proxy resolver:2020 {
header_up Host {host}
}
}
```
4. **Ajouter le token dans `.env`** et le passer au conteneur Caddy.
5. Supprimer ou ajuster le bloc `:443` actuel qui utilise `on_demand` pour les instances.
### Alternative sans module DNS
Obtenir le certificat wildcard manuellement (Certbot DNS-01, acheté, etc.) et le charger dans Caddy :
```caddy
*.studioe5.edudeploy.com {
tls /data/certs/wildcard.crt /data/certs/wildcard.key
reverse_proxy resolver:2020 {
header_up Host {host}
}
}
```
Inconvénient : renouvellement manuel.
## 🔧 Notes techniques
- Le conteneur `resolver-vpn` utilise `network_mode: service:resolver` pour partager le netns avec le resolver.
- Lagent utilise `tailscaled --tun=userspace-networking` ; le resolver-vpn utilise un vrai TUN (`tailscale0`).
- Le `Caddyfile` actuel utilise `tls { on_demand }` pour les instances. En cas de nouvelle rate limit, on peut temporairement remettre `tls internal` dans le bloc `:443` pour valider le flux sans certificat public.