33800 Docs

← Retour

Fix : Partage du tunnel VPN entre instances utilisant le même profil

Status : EN ATTENTE

Problème

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 :

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.

Cause racine

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).

Solution : Nommer le tunnel par profil VPN + refcount

Principe

Modifications

1. 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);

2. 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)
}

3. 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)}`;

Fichiers modifiés

Fichier Changement
connectors-api/src/services/vpn-profiles.ts Interface nommée par profil + refcount

Pas de modification du sidecar

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.

Cas couverts

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

Risque

Déploiement

  1. Modifier src/services/vpn-profiles.ts
  2. git add + commit + push → CI/CD auto

Vérification

  1. Tester SSH sur Test Rhinov → succès
  2. Tester SSH sur Proxmox Rhinov → succès (même tunnel réutilisé)
  3. Tester SSH sur Proxy Begle → succès (même tunnel, mais attention auth SSH : le mdp a peut-être été perdu par le bug précédent → re-saisir si besoin)