d090f67bff
- Use Windows named pipe \.\pipe\studioe5-tailscaled instead of Unix socket - Apply hideWindow to all child processes (tailscale, podman, docker, browser) - Redirect agent logs to <data-dir>/agent.log and tailscaled logs to tailscaled.log - Fix double tailscale/ tailscale dir path in startTailscaleAndReport - Remove --operator=root on Windows - Bump agent version to 0.3.1
356 lines
15 KiB
Markdown
356 lines
15 KiB
Markdown
# 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**
|
||
- L’agent ne démarre plus Tailscale au boot.
|
||
- Le VPN se lance automatiquement à la création/démarrage d’une 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 l’agent (modèle commercialisable)**
|
||
- L’agent démarre sans `headscale_url` ni `headscale_auth_key`.
|
||
- L’utilisateur entre seulement un code d’activation.
|
||
- Le serveur envoie la config Headscale, l’agent la sauvegarde et démarre le VPN automatiquement.
|
||
|
||
## ✅ Blocage levé
|
||
|
||
**Rate limit Let’s Encrypt pour `edudeploy.com` est levé.**
|
||
Le 2026-06-23 vers 09:35 UTC, Caddy a pu obtenir un certificat Let’s 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 d’instance.
|
||
|
||
## 🎯 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 Let’s 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://`.
|
||
|
||
## 📁 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 : `3151830` (lancé le 2026-06-23 09:36 UTC)
|
||
|
||
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 :
|
||
- L’agent 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).
|
||
- 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)
|
||
```
|
||
|
||
## 🛠️ Commandes utiles pour reprendre
|
||
|
||
### Voir l’agent de test
|
||
```bash
|
||
pgrep -a studioe5-agent
|
||
```
|
||
|
||
### Relancer l’agent 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 l’API web
|
||
|
||
Test réalisé le 2026-06-23 en utilisant le compte superadmin :
|
||
|
||
1. **Authentification NextAuth** sur `/api/auth/callback/credentials`.
|
||
2. **Création d’instance** via `POST /api/instances` :
|
||
```json
|
||
{
|
||
"nodeId": "vps-8fc665eb",
|
||
"templateId": "wordpress-wordpress-latest",
|
||
"port": 8002
|
||
}
|
||
```
|
||
→ Instance créée : `cmqqgrur20001lw67t2bdgzkg`.
|
||
3. Le serveur a automatiquement envoyé l’action `start` au node via WebSocket.
|
||
4. L’agent a démarré le VPN (si besoin), écrit le compose et a lancé les conteneurs WordPress.
|
||
5. Caddy a obtenu un certificat Let’s 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 l’agent
|
||
|
||
L’agent 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.1-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.1.exe`
|
||
- Nécessite d’avoir installé Tailscale Windows séparément ou d’avoir les binaires dans `tailscale-bin/windows/`.
|
||
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.1`
|
||
|
||
### 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 l’agent pour Windows et Linux (macOS nécessite CGO)
|
||
./build.sh
|
||
```
|
||
|
||
Le `build.sh` génère automatiquement `studioE5-agent-v0.3.0-windows.zip` et copie les binaires versionnés dans `server/public/`.
|
||
|
||
### Flow d’activation zéro-config (modèle commercialisable)
|
||
|
||
L’élève/employé n’a **aucune configuration technique** à saisir :
|
||
|
||
1. **Télécharger** l’agent Windows (`studioE5-agent-v0.3.0-windows.zip`).
|
||
2. **Extraire** et **lancer** `studioE5-agent.exe`.
|
||
3. **Entrer le code d’activation** à 6 caractères fourni par l’établissement (affiché dans l’UI locale `http://localhost:7070`).
|
||
4. L’agent contacte le serveur, le serveur vérifie le code et renvoie **automatiquement** :
|
||
- l’identité de l’élève (`studentName`)
|
||
- l’URL Headscale
|
||
- la clé pré-auth Headscale
|
||
5. L’agent sauvegarde ces informations localement et **démarre automatiquement le VPN**.
|
||
6. L’agent 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
|
||
```
|
||
|
||
## 📋 Prochaines étapes à faire
|
||
|
||
- [x] ~~Attendre la fin du rate limit Let’s Encrypt~~ (levé le 2026-06-23).
|
||
- [x] ~~Relancer un test HTTPS sur `https://test-wp-001.studioe5.edudeploy.com/`~~ → **OK** (HTTP/2 200).
|
||
- [x] ~~Créer une branche dédiée et commiter les modifications VPN on-demand~~ → branche `feat/studioe5-vpn-ondemand`, commit `124543d`. Push vers Gitea à faire dès que le remote sera accessible (actuellement `localhost:3001` et `gitea.alfrednobel.edudeploy.com` injoignables).
|
||
- [x] ~~Tester le flux complet depuis l’interface web~~ → **OK** via l’API authentifiée (`POST /api/instances`), instance `cmqqgrur20001lw67t2bdgzkg` accessible en HTTPS public.
|
||
- [ ] **Obtenir un certificat wildcard** pour `*.studioe5.edudeploy.com` (voir étude ci-dessous).
|
||
- [ ] **Nettoyer les instances/agent de test** une fois le wildcard en place et le push effectué.
|
||
- [x] ~~Packager les binaires Tailscale pour Windows~~ → **OK**, `download-tailscale-bins.sh` + `studioE5-agent-v0.3.0-windows.zip` prêt.
|
||
- [ ] **Nettoyer les anciens nodes/volumes Headscale** créés pendant les tests.
|
||
- [ ] **Documenter la procédure de mise en production** pour le client A (config agent, clés Headscale, ports, etc.).
|
||
|
||
## 💡 Améliorations UI envisagées
|
||
|
||
### Console / log intégré dans l’agent
|
||
|
||
Plutôt que de laisser Windows ouvrir une fenêtre noire à chaque commande `podman`/`docker`/`tailscale`, rediriger le `Stdout`/`Stderr` de chaque commande vers l’UI locale de l’agent (`http://localhost:7070`).
|
||
|
||
Bénéfices :
|
||
- Expérience utilisateur plus propre et commercialisable.
|
||
- Diagnostic facilité : l’utilisateur voit exactement ce qui se passe (téléchargement d’image, démarrage, installation PrestaShop, etc.).
|
||
|
||
Implémentation :
|
||
1. Remplacer `cmd.Stdout = os.Stdout` par un `io.Pipe()` ou `bytes.Buffer` dans `docker.go`, `tailscale.go`, etc.
|
||
2. Envoyer les lignes de log au frontend via le WebSocket existant (`agent/ui/websocket`).
|
||
3. Afficher les logs dans un panneau dédié du HTML.
|
||
|
||
### Barre de progression
|
||
|
||
Associer des étapes connues à une barre de progression dans l’UI :
|
||
|
||
| Étape | Poids |
|
||
|-------|-------|
|
||
| Connexion au serveur | 10 % |
|
||
| Démarrage du VPN | 25 % |
|
||
| Téléchargement de l’image Docker | 50 % |
|
||
| Création de la base de données | 70 % |
|
||
| Installation de PrestaShop/WordPress | 90 % |
|
||
| Instance prête | 100 % |
|
||
|
||
L’agent envoie des messages `progress` au frontend à chaque étape franchie.
|
||
|
||
## 🔒 Étude certificat wildcard `*.studioe5.edudeploy.com`
|
||
|
||
### Pourquoi passer en wildcard ?
|
||
|
||
Avec `tls { on_demand }`, Caddy émet **un certificat Let’s Encrypt par sous-domaine d’instance**. 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 d’instances 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.
|
||
- L’agent 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.
|