Documentation J+SERVICES Guides Référence API

Contrat API — camouflage MAC (application mobile)

Révision : 2026-09-17 Statut : générée et injectée par l’application mobile ; validée et réservée par le backend uniquement en mode Cloud.

Mode Standalone — aucune dépendance RADIUS/NAS

Le camouflage manuel est une opération locale. Si le routeur n’a pas de nas_id, ou si le gérant n’a pas activé la gestion Cloud, l’application doit pouvoir le réaliser depuis le LAN :

Le backend reste silencieux et ne crée aucun état dans ce mode. L’écran doit indiquer « Mode local — non synchronisé au Cloud ». Les protections centralisées (réservation globale de MAC, audit Cloud, statut distant et relance serveur) ne sont alors pas disponibles.

Les routes décrites ci-dessous concernent exclusivement le mode Cloud optionnel, activé par le gérant sur un NAS déjà existant. Elles ne sont pas un prérequis au camouflage.

Contrat

POST /api/v1/radius/starlink/mac/{nasId} accepte ce corps de déclaration :

{
  "router_serial": "HH40AF7YVNP",
  "resultat": "POSEE",
  "interface": "ether1",
  "mac_origine": "48:A9:8A:11:22:33",
  "camouflage_mac": "02:AB:CD:EF:01:02",
  "internet_confirme": true
}

camouflage_mac est requis, y compris avec resultat: "ECHEC", afin que la tentative reste traçable et que la colonne d’identité réseau ne soit jamais vide. Le backend accepte uniquement une MAC normalisable à six octets, unicast (bit 0 du premier octet à 0), localement administrée (bit 1 à 1) et différente de mac_origine. Il l’enregistre en forme majuscule avec : comme séparateur.

Si une autre ligne la possède déjà, le serveur répond :

{
  "success": false,
  "error": "MAC_COLLISION",
  "message": "L'adresse MAC générée est déjà utilisée par un autre équipement."
}

avec HTTP 409. L’application doit générer une nouvelle valeur et rejouer la même demande ; elle ne doit pas réessayer une valeur refusée.

Algorithme historique transmis au mobile

Le générateur backend historique est conservé dans src/modules/vpn-management/utils/macVirtuelle.js. Son mode sûr par défaut était déterministe : SHA-256 de ${sel}|${identifiant}, premier octet forcé à 0x02, puis les cinq octets suivants du hash. 0x02 garantit unicast et localement administrée. Pour la nouvelle stratégie, l’application doit plutôt tirer cinq octets cryptographiquement aléatoires et préfixer 02 :

function generateCamouflageMac(randomBytes: Uint8Array): string {
  if (randomBytes.length < 5) throw new Error('five random bytes required');
  return [0x02, ...randomBytes.slice(0, 5)]
    .map((byte) => byte.toString(16).toUpperCase().padStart(2, '0'))
    .join(':');
}

Ne pas utiliser les OUIs constructeur optionnels de l’ancien générateur : ils ne sont ni localement administrés ni nécessaires à ce contrat. Utiliser une source cryptographique du système mobile pour randomBytes.

Déviations par rapport à l’ancien contrat

Lecture déléguée par un compte assistant

GET /api/v1/radius/starlink/mac/{nasId}/statut et GET /api/v1/radius/starlink/mac/{nasId}/consigne sont accessibles à tout compte mobile authentifié et lié, y compris s’il n’est pas propriétaire du routeur. Cette exception sert les interventions physiques et l’assistance multi-compte. Les routes de déclaration et relance conservent le contrôle d’appartenance.

Chaque lecture tierce est journalisée ; le propriétaire reçoit un push et un e-mail, dédoublonnés par quart d’heure et par type de lecture pour ne pas alerter à chaque cycle de polling.

Obtenir les commandes de discrétion (Consigne)

Lors de l’appel à GET /api/v1/radius/starlink/mac/{nasId}/consigne, l’application mobile doit obligatoirement fournir l’inventaire des interfaces du routeur dans les paramètres d’URL (query parameters) afin que le backend puisse générer les commandes de discrétion (masquage MNDP/Winbox).

Paramètres requis :

Exemple d’appel complet : GET /api/v1/radius/starlink/mac/{nasId}/consigne?router_serial=HH40AF7YVNP&interface=ether1&interfaces_locales=bridge,ether2,ether3

Comportement de sécurité : Si interfaces_locales est vide ou manquant, le backend refusera de générer les commandes pour éviter de bloquer l’accès local. Dans ce cas, la réponse contiendra "discretion": null et "discretion_motif": "INVENTAIRE_INTERFACES_MANQUANT". L’application mobile ne doit pas considérer cela comme un crash, mais corriger sa requête d’inventaire.