a414f03a59
- Configure tailscale serve automatically for each instance on Windows userspace networking. - Add local UI buttons: start/stop/reset/delete instances (stop/start preserve volumes). - Clean shutdown: stop tailscaled and instances, notify server with instance_stopped. - Restart tailscaled on agent boot using persisted state when pre-auth key is absent. - Sync instance stopped/deleted status to dashboard (server/lib/websocket.ts). - Security: include prior authz/scoping changes across API routes, ephemeral pre-auth keys, ACL policy, internal API key. - Update SUIVI_VPN_ONDEMAND.md and docs/ONBOARDING_CLIENT.md. - Bump agent version to 0.3.5.
550 lines
16 KiB
Markdown
550 lines
16 KiB
Markdown
# Deployeur studioE5 — Onboarding d’un nouvel établissement
|
||
|
||
## Objectif
|
||
|
||
Ce document décrit le fonctionnement du **deployeur studioE5**, c’est-à-dire l’application / l’outil qui provisionne et configure un nouvel environnement client (un établissement) prêt à accueillir l’application studioE5.
|
||
|
||
L’application studioE5 elle-même (agent, VPN on-demand, resolver, Caddy, Headscale, etc.) est documentée dans `SUIVI_VPN_ONDEMAND.md`. Le deployeur est l’outil qui **déploie** cette application sur un VPS dédié au client.
|
||
|
||
---
|
||
|
||
## Public cible
|
||
|
||
- Équipe produit / développement du deployeur
|
||
- Équipe ops / déploiement
|
||
- Référents techniques du client A
|
||
|
||
---
|
||
|
||
## Glossaire
|
||
|
||
| Terme | Définition |
|
||
|-------|------------|
|
||
| **Deployeur** | Application qui provisionne un VPS, déploie la stack studioE5 et configure le DNS/certificats pour un nouvel établissement. |
|
||
| **Hub central** | Dashboard superadmin studioE5 qui orchestre les déploiements et la gestion multi-clients. |
|
||
| **Établissement** | Entité client (école, lycée, université, entreprise). |
|
||
| **Tag établissement** | Slug unique et court identifiant l’établissement dans les URLs et le DNS. |
|
||
| **Domaine géré** | Sous-domaine fourni par studioE5 : `*.tag.edudeploy.com`. |
|
||
| **Domaine propre** | Domaine détenu par l’établissement : `*.tag.monetablissement.fr`. |
|
||
| **Application studioE5** | Stack complète déployée sur le VPS : `server`, `resolver`, `resolver-vpn`, `caddy`, `headscale`, `postgres`, etc. |
|
||
| **Agent générique** | Binaire agent unique, capable de se connecter à n’importe quel serveur studioE5 via résolution d’URL à l’activation. |
|
||
|
||
---
|
||
|
||
## Architecture : deployeur vs application studioE5
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Hub central studioE5 │
|
||
│ (superadmin, gestion des établissements, monitoring) │
|
||
└───────────────────────┬─────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Deployeur studioE5 │
|
||
│ (provisionning VPS, DNS, certificats, déploiement stack) │
|
||
└───────────────────────┬─────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Application studioE5 (un par client) │
|
||
│ Caddy — Resolver — Resolver-VPN — Headscale — Server — DB │
|
||
│ ▲ │
|
||
│ │ WebSocket / VPN on-demand │
|
||
│ ▼ │
|
||
│ Agent élève (Windows/Linux) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Flux d’onboarding par le deployeur (vue d’ensemble)
|
||
|
||
```
|
||
Création de l’établissement dans le hub
|
||
↓
|
||
Choix du domaine (géré ou propre)
|
||
↓
|
||
Génération du tag établissement
|
||
↓
|
||
Provisionning du VPS
|
||
↓
|
||
Configuration DNS wildcard
|
||
↓
|
||
Génération du certificat wildcard
|
||
↓
|
||
Déploiement de la stack studioE5 (Docker Compose)
|
||
↓
|
||
Initialisation de Headscale et création des clés
|
||
↓
|
||
Création du compte administrateur de l’établissement
|
||
↓
|
||
Génération des codes d’activation
|
||
↓
|
||
Build et mise à disposition de l’agent dédié
|
||
↓
|
||
Activation de l’agent par un élève
|
||
↓
|
||
Création d’une première instance (validation du déploiement)
|
||
```
|
||
|
||
---
|
||
|
||
## 1. Création de l’établissement dans le hub
|
||
|
||
Le superadmin crée un nouvel établissement dans le hub central.
|
||
|
||
Données minimales :
|
||
|
||
- Nom officiel
|
||
- Type d’établissement (école, lycée, université, entreprise)
|
||
- Pays / fuseau horaire
|
||
- Contact administrateur
|
||
- Choix du mode de domaine (`managed` ou `custom`)
|
||
|
||
---
|
||
|
||
## 2. Choix du domaine
|
||
|
||
### Option A — Domaine géré par studioE5 (MVP)
|
||
|
||
Le deployeur crée automatiquement un sous-domaine du domaine maître :
|
||
|
||
```
|
||
*.tag.edudeploy.com
|
||
```
|
||
|
||
Le hub gère le DNS chez le registrar studioE5 (actuellement Infomaniak).
|
||
|
||
### Option B — Domaine propre de l’établissement (évolution)
|
||
|
||
L’établissement fournit son propre domaine :
|
||
|
||
```
|
||
*.tag.monetablissement.fr
|
||
```
|
||
|
||
Prérequis :
|
||
|
||
- Le client pointe son DNS wildcard vers l’IP du VPS provisionné.
|
||
- Le deployeur dispose d’un token API du registrar du client pour le challenge DNS-01.
|
||
|
||
---
|
||
|
||
## 3. Génération du tag établissement
|
||
|
||
Le tag est un slug court, unique au niveau du hub, utilisé dans les URLs et le DNS.
|
||
|
||
### Règles
|
||
|
||
- Uniquement `[a-z0-9-]`
|
||
- Pas de tiret au début ni à la fin
|
||
- Longueur conseillée : 2 à 20 caractères
|
||
- Vérification d’unicité en base
|
||
|
||
### Exemples
|
||
|
||
| Nom d’établissement | Tag |
|
||
|---------------------|-----|
|
||
| Lycée Jules Ferry | `ljf` |
|
||
| Institut Supérieur du Digital | `isd` |
|
||
| École Notre-Dame | `end` |
|
||
|
||
### Gestion des collisions
|
||
|
||
- `ljf` → `ljf-2`, `ljf-3`, etc.
|
||
|
||
---
|
||
|
||
## 4. Provisionning du VPS
|
||
|
||
Le deployeur provisionne un VPS dédié pour l’établissement.
|
||
|
||
### Prérequis sur le VPS vierge
|
||
|
||
- OS Linux (Ubuntu LTS recommandé)
|
||
- Docker + Docker Compose installés
|
||
- Accès SSH avec clé
|
||
- Ports ouverts : 22, 80, 443
|
||
|
||
### Actions automatisées par le deployeur
|
||
|
||
1. Installation de Docker et Docker Compose si absent.
|
||
2. Création de la structure de dossiers (`/opt/studioe5-<tag>/`).
|
||
3. Génération des secrets (`.env`) :
|
||
- `INTERNAL_API_KEY`
|
||
- `HEADSCALE_API_KEY`
|
||
- `HEADSCALE_AUTH_KEY` (réutilisable, taguée `tag:student-agent`)
|
||
- `HEADSCALE_RESOLVER_AUTH_KEY`
|
||
- `INFOMANIAK_API_TOKEN` (si domaine géré)
|
||
- `NEXTAUTH_SECRET`, `DATABASE_URL`, etc.
|
||
4. Récupération des images Docker depuis le registry privé (ou build sur place en attendant).
|
||
5. Génération des fichiers de configuration (`Caddyfile`, `docker-compose.yml`, `headscale/config.yaml`, `headscale/acl_policy.hujson`).
|
||
|
||
---
|
||
|
||
## 5. Configuration DNS wildcard
|
||
|
||
### Domaine géré
|
||
|
||
Le deployeur appelle l’API du registrar pour créer :
|
||
|
||
```dns
|
||
*.tag.edudeploy.com A <IP_DU_VPS>
|
||
```
|
||
|
||
### Domaine propre
|
||
|
||
Le deployeur vérifie que l’enregistrement existe :
|
||
|
||
```dns
|
||
*.tag.monetablissement.fr A <IP_DU_VPS>
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Certificat wildcard
|
||
|
||
### Principe
|
||
|
||
Un seul certificat wildcard couvre toutes les instances futures de l’établissement.
|
||
|
||
### Mise en œuvre avec Caddy
|
||
|
||
Le deployeur génère le `Caddyfile` :
|
||
|
||
```caddy
|
||
*.tag.edudeploy.com {
|
||
tls {
|
||
dns infomaniak {env.INFOMANIAK_API_TOKEN}
|
||
}
|
||
|
||
reverse_proxy resolver:2020 {
|
||
header_up Host {host}
|
||
}
|
||
}
|
||
```
|
||
|
||
Pour un domaine propre, le provider DNS est celui du client.
|
||
|
||
### Renouvellement
|
||
|
||
Géré automatiquement par Caddy.
|
||
|
||
---
|
||
|
||
## 7. Déploiement de la stack studioE5
|
||
|
||
Le deployeur lance la stack Docker Compose complète :
|
||
|
||
```bash
|
||
cd /opt/studioe5-<tag>
|
||
docker compose up -d
|
||
```
|
||
|
||
Services déployés :
|
||
|
||
- `server` : API + WebSocket + UI Next.js
|
||
- `resolver` : reverse proxy interne vers les instances
|
||
- `resolver-vpn` : sidecar Tailscale dans le netns du resolver
|
||
- `caddy` : reverse proxy public + TLS
|
||
- `headscale` : contrôleur Tailscale
|
||
- `postgres` : base de données
|
||
|
||
---
|
||
|
||
## 8. Initialisation de Headscale
|
||
|
||
Le deployeur initialise Headscale et crée les clés nécessaires :
|
||
|
||
```bash
|
||
# Création de l’utilisateur dédié au resolver
|
||
docker compose exec headscale headscale users create resolver
|
||
|
||
# Création de la clé pré-auth réutilisable pour les agents
|
||
docker compose exec headscale headscale preauthkeys create \
|
||
--user studioe5 \
|
||
--reusable \
|
||
--tags tag:student-agent \
|
||
-e 87600h
|
||
|
||
# Création de la clé pré-auth pour le resolver
|
||
docker compose exec headscale headscale preauthkeys create \
|
||
--user resolver \
|
||
--tags tag:resolver \
|
||
-e 87600h
|
||
|
||
# Création d’une clé API Headscale valable 10 ans
|
||
docker compose exec headscale headscale apikeys create -e 87600h
|
||
```
|
||
|
||
Ces secrets sont stockés dans le `.env` du serveur.
|
||
|
||
---
|
||
|
||
## 9. Création du compte administrateur de l’établissement
|
||
|
||
Une fois la stack déployée, le deployeur (ou le hub) crée le premier compte administrateur de l’établissement via l’API du serveur nouvellement déployé.
|
||
|
||
Rôles :
|
||
|
||
- `admin` : gestion des élèves, instances, agents.
|
||
- `teacher` : gestion limitée à certaines classes/groupes.
|
||
- `superadmin` (studioE5) : accès transverse.
|
||
|
||
L’administrateur reçoit un lien d’activation sécurisé.
|
||
|
||
---
|
||
|
||
## 10. Génération des codes d’activation
|
||
|
||
Le deployeur configure le serveur pour permettre la génération de codes d’activation.
|
||
|
||
### Règles de sécurité (implémentées côté application studioE5)
|
||
|
||
- Génération avec `crypto.randomBytes`
|
||
- Alphabet sans ambiguïté : `ABCDEFGHJKLMNPQRSTUVWXYZ23456789`
|
||
- 6 caractères
|
||
- Expiration après 60 minutes
|
||
- Invalidation après usage
|
||
- Rate-limiting : 5 tentatives par code / 5 tentatives par `nodeId` sur 15 minutes
|
||
|
||
### Flux
|
||
|
||
1. L’administrateur génère un code pour un élève.
|
||
2. L’élève saisit le code dans l’agent.
|
||
3. Le serveur valide et renvoie :
|
||
- l’identité de l’élève
|
||
- l’URL Headscale
|
||
- une clé pré-auth Headscale éphémère
|
||
4. L’agent démarre automatiquement le VPN.
|
||
|
||
---
|
||
|
||
## 11. Build et mise à disposition de l’agent
|
||
|
||
### Principe
|
||
|
||
L’agent est un binaire générique, mais il doit être capable de se connecter au bon serveur. Le deployeur génère un agent pré-configuré ou un installeur qui embarque l’URL du serveur de l’établissement.
|
||
|
||
### Build
|
||
|
||
```bash
|
||
cd /opt/studioe5-<tag>/agent
|
||
./download-tailscale-bins.sh 1.98.4
|
||
./build.sh
|
||
```
|
||
|
||
Artifacts générés :
|
||
|
||
- `studioE5-agent-vX.Y.Z-windows.zip`
|
||
- `studioE5-agent-vX.Y.Z.exe`
|
||
- `studioE5-agent-vX.Y.Z` (Linux)
|
||
|
||
### Mise à disposition
|
||
|
||
Les fichiers sont servis par Caddy depuis `server/public/agent/` :
|
||
|
||
```
|
||
https://tag.edudeploy.com/studioE5-agent-vX.Y.Z-windows.zip
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Activation de l’agent
|
||
|
||
### Activation zéro-config
|
||
|
||
1. L’élève télécharge l’agent depuis l’URL de l’établissement.
|
||
2. Il extrait l’archive et lance `studioE5-agent.exe`.
|
||
3. Il ouvre `http://localhost:7070`.
|
||
4. Il saisit le code d’activation à 6 caractères.
|
||
5. L’agent contacte le serveur, récupère la configuration et démarre le VPN.
|
||
|
||
> Les détails techniques du VPN on-demand (named pipes Windows, logs, ACL, tokens, clés éphémères) sont documentés dans `SUIVI_VPN_ONDEMAND.md`.
|
||
|
||
---
|
||
|
||
## 13. Création d’une instance et construction de l’URL (validation)
|
||
|
||
Le deployeur ou l’administrateur crée une première instance pour valider le déploiement.
|
||
|
||
### Format d’URL
|
||
|
||
```
|
||
<appli>-<initiales><id-court>.<tag>.<domaine>
|
||
```
|
||
|
||
Exemple :
|
||
|
||
```
|
||
wp-jd47.ljf.edudeploy.com
|
||
```
|
||
|
||
Avec :
|
||
|
||
- `wp` : type d’application
|
||
- `jd` : initiales de l’élève
|
||
- `47` : identifiant court unique
|
||
- `ljf` : tag de l’établissement
|
||
- `edudeploy.com` : domaine de base
|
||
|
||
### Mapping type d’application → préfixe
|
||
|
||
| Application | Préfixe |
|
||
|-------------|---------|
|
||
| WordPress | `wp` |
|
||
| PrestaShop | `ps` |
|
||
| Moodle | `mdl` |
|
||
| Nextcloud | `nc` |
|
||
|
||
### Protection de l’identité
|
||
|
||
- L’URL ne contient pas le nom complet de l’élève.
|
||
- Seules les initiales + un identifiant court opaque sont exposées.
|
||
|
||
---
|
||
|
||
## 14. Modèles de données du deployeur
|
||
|
||
### Table / modèle `Organization` (établissement dans le hub)
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"name": "Lycée Jules Ferry",
|
||
"tag": "ljf",
|
||
"domainMode": "managed",
|
||
"baseDomain": "edudeploy.com",
|
||
"adminEmail": "admin@ljf.fr",
|
||
"status": "active",
|
||
"createdAt": "2026-06-25T17:28:07Z"
|
||
}
|
||
```
|
||
|
||
### Table / modèle `Deployment` (déploiement sur un VPS)
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"organizationId": "uuid",
|
||
"serverIp": "203.0.113.10",
|
||
"serverHostname": "ljf.studioe5.edudeploy.com",
|
||
"wildcardDnsConfigured": true,
|
||
"wildcardCertificateReady": true,
|
||
"dnsProvider": "infomaniak",
|
||
"dnsProviderTokenRef": "env:INFOMANIAK_TOKEN_LJF",
|
||
"headscaleApiKeyRef": "env:HEADSCALE_API_KEY_LJF",
|
||
"status": "ready",
|
||
"deployedAt": "2026-06-25T17:28:07Z"
|
||
}
|
||
```
|
||
|
||
### Table / modèle `Student` (dans l’application studioE5 déployée)
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"organizationId": "uuid",
|
||
"firstName": "Jean",
|
||
"lastName": "Dupont",
|
||
"initials": "jd",
|
||
"activationCode": "AB3D9F",
|
||
"activationCodeExpiresAt": "2026-06-25T18:28:07Z",
|
||
"nodeId": "vps-8fc665eb",
|
||
"nodeToken": "..."
|
||
}
|
||
```
|
||
|
||
### Table / modèle `Instance` (dans l’application studioE5 déployée)
|
||
|
||
```json
|
||
{
|
||
"id": "cmqqgrur20001lw67t2bdgzkg",
|
||
"organizationId": "uuid",
|
||
"studentId": "uuid",
|
||
"nodeId": "vps-8fc665eb",
|
||
"templateId": "wordpress-wordpress-latest",
|
||
"applicationPrefix": "wp",
|
||
"shortId": "47",
|
||
"subdomain": "wp-jd47",
|
||
"fqdn": "wp-jd47.ljf.edudeploy.com",
|
||
"port": 8001,
|
||
"status": "running"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 15. Sécurité et RGPD
|
||
|
||
### Protection de l’identité de l’élève
|
||
|
||
- L’URL publique ne contient pas le nom complet de l’élève.
|
||
- Seules les initiales + un identifiant court opaque sont exposées.
|
||
|
||
### Isolation réseau
|
||
|
||
- Les agents élèves ne peuvent pas communiquer entre eux (ACL Headscale).
|
||
- Le resolver est le seul service autorisé à joindre les agents sur leurs ports d’instance.
|
||
|
||
### Authentification
|
||
|
||
- Token unique par agent (`node.token`).
|
||
- Clé API interne pour les endpoints serveur → agent.
|
||
- Sessions NextAuth sur les routes API métier.
|
||
|
||
### Clés pré-auth Headscale
|
||
|
||
- Éphémères, à usage unique, 15 minutes d’expiration.
|
||
- Non persistées côté agent.
|
||
|
||
---
|
||
|
||
## 16. Checklist de validation du deployeur
|
||
|
||
À l’issue d’un onboarding, les points suivants doivent être validés :
|
||
|
||
- [ ] L’établissement est créé dans le hub avec un tag unique.
|
||
- [ ] Le VPS est provisionné et accessible en SSH.
|
||
- [ ] Docker et Docker Compose sont installés.
|
||
- [ ] Le DNS wildcard est résolu (`*.tag.edudeploy.com` → IP du VPS).
|
||
- [ ] Le certificat wildcard est obtenu et valide.
|
||
- [ ] La stack studioE5 est démarrée (`docker compose ps`).
|
||
- [ ] Headscale est initialisé avec les utilisateurs et clés nécessaires.
|
||
- [ ] Le compte administrateur de l’établissement est créé.
|
||
- [ ] Un code d’activation peut être généré pour un élève.
|
||
- [ ] L’agent est buildé et téléchargeable depuis le serveur de l’établissement.
|
||
- [ ] L’agent s’active avec le code zéro-config.
|
||
- [ ] Une instance peut être créée et son URL est accessible en HTTPS.
|
||
- [ ] Deux instances différentes reçoivent des URL uniques.
|
||
- [ ] Le flux HTTPS complet retourne bien HTTP 200.
|
||
|
||
---
|
||
|
||
## 17. Roadmap du deployeur
|
||
|
||
### Court terme (MVP)
|
||
|
||
- Déploiement manuel ou semi-automatisé d’un nouvel établissement sur un VPS.
|
||
- Domaine géré par studioE5 uniquement.
|
||
- Build des images sur le VPS cible.
|
||
- Agent avec URL serveur hardcodée ou fournie à l’activation.
|
||
|
||
### Moyen terme
|
||
|
||
- **Agent générique** : déterminer l’URL serveur cible à l’activation (code structuré, hub de résolution, ou champ URL).
|
||
- **Script de provisionning** : installation Docker, déploiement stack, génération secrets, DNS wildcard.
|
||
- **Registry d’images privé** : builder une fois, déployer partout.
|
||
- Support de domaines propres à l’établissement.
|
||
- Support multi-registrar DNS.
|
||
|
||
### Long terme
|
||
|
||
- **Hub central multi-clients** : dashboard superadmin, gestion des versions, logs distants.
|
||
- **Mises à jour à distance** : pousser une nouvelle version du serveur et de l’agent sur tous les déploiements.
|
||
- **Monitoring / support** : alertes serveur down, certificat expiré, agent hors ligne.
|
||
- **Tests automatisés** : validation du flux activation → VPN → instance → HTTPS public à chaque déploiement.
|
||
- **Console/log intégré et barre de progression** dans l’agent.
|
||
- Génération automatique de codes d’activation par import CSV.
|