Status : EN ATTENTE
Quand plusieurs instances SSH utilisent le même profil VPN (ex: 3 instances Rhinov avec le profil feb5990f), chaque instance crée son propre tunnel WireGuard :
wg-67beec52 (Test Rhinov)wg-ddd7e99a (Proxmox Rhinov)wg-95c357ba (Proxy Begle)Mais ils partagent la même config VPN : même IP (10.9.0.9/32), mêmes routes (192.168.10.0/24, 192.168.11.0/24). Le 1er tunnel réussit, les suivants échouent :
ip -4 route add 192.168.11.0/24 dev wg-ddd7e99a
RTNETLINK answers: File exists
→ wg-quick échoue → tunnel détruit → SSH échoue.
vpn-profiles.ts ligne 507 :
const interfaceName = `wg-${instanceId.substring(0, 8)}`;
L'interface est nommée par instance, alors qu'elle devrait être nommée par profil VPN (car c'est le même tunnel réseau).
src/services/vpn-profiles.ts — connectForInstance()Ligne 507, remplacer :
const interfaceName = `wg-${instanceId.substring(0, 8)}`;
Par :
const interfaceName = `wg-${vpnProfileId.substring(0, 8)}`;
Et ajouter un refcount après connexion réussie (ligne ~544) :
// Track reference count for shared tunnels
if (!this._tunnelRefCounts) this._tunnelRefCounts = new Map<string, Set<string>>();
if (!this._tunnelRefCounts.has(interfaceName)) this._tunnelRefCounts.set(interfaceName, new Set());
this._tunnelRefCounts.get(interfaceName)!.add(instanceId);
src/services/vpn-profiles.ts — disconnectForInstance()Lignes 553-590, remplacer la génération du nom d'interface ET ajouter le refcount :
async disconnectForInstance(instanceId: string, userId: string) {
// Find which profile this instance uses
const instanceResult = await supabase.select('user_connectors', {
eq: { id: instanceId, user_id: userId },
single: true
});
const vpnProfileId = instanceResult.data?.token_metadata?.vpn_profile_id;
if (!vpnProfileId) return { success: true };
const interfaceName = `wg-${vpnProfileId.substring(0, 8)}`;
// Remove this instance from refcount
if (this._tunnelRefCounts?.has(interfaceName)) {
this._tunnelRefCounts.get(interfaceName)!.delete(instanceId);
// Don't disconnect if other instances still use this tunnel
if (this._tunnelRefCounts.get(interfaceName)!.size > 0) {
return { success: true };
}
this._tunnelRefCounts.delete(interfaceName);
}
// No more users → disconnect tunnel
// ... (reste du code existant avec le bon interfaceName)
}
src/services/vpn-profiles.ts — getStatusForInstance()Ligne ~597, même changement de nommage :
// Avant:
const interfaceName = `wg-${instanceId.substring(0, 8)}`;
// Après: récupérer le vpnProfileId de l'instance, puis:
const interfaceName = `wg-${vpnProfileId.substring(0, 8)}`;
| Fichier | Changement |
|---|---|
connectors-api/src/services/vpn-profiles.ts |
Interface nommée par profil + refcount |
Le sidecar gère déjà le cas "Already connected" (app.py ligne ~220) :
if interface_name in active_tunnels:
return {"success": True, "interface_name": interface_name, "message": "Already connected"}
Donc aucune modif nécessaire côté sidecar.
| Scénario | Comportement |
|---|---|
| 1ère instance se connecte | Crée tunnel wg-feb5990f → succès |
| 2ème instance se connecte | Tunnel existe → "Already connected" → succès |
| 3ème instance se connecte | Idem → succès |
| Disconnect 1 instance | Refcount > 0 → tunnel maintenu |
| Disconnect dernière instance | Refcount = 0 → tunnel fermé |
| Sidecar restart | Refcount reset, prochain connect recrée le tunnel |
wg-{instanceId} seront simplement ignorés/nettoyés au prochain restart du sidecar.src/services/vpn-profiles.tsgit add + commit + push → CI/CD auto