feat(agent): v0.3.5 Windows inbound forwarding, UI actions, lifecycle
- 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.
This commit is contained in:
@@ -0,0 +1,549 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user