karvan: quitar el STUN de Google y servir la configuracion ICE del propio nodo

El cliente tenia stun.l.google.com fijo en el codigo. Un servidor STUN aprende la
IP publica y el momento de cada consulta, asi que tal cual estaba cada llamada le
avisaba a Google de que ese usuario esta llamando. En un proyecto con esta postura
sobre privacidad no se sostiene.

Por defecto ya no hay ningun servidor ICE: solo candidatos host, que funcionan en
la misma red y con NAT amable. Es mas honesto degradar a nada que degradar a un
tercero.

Se anade turnCredentials.js, siguiendo el estilo de los helpers de src/backend/:
genera credenciales efimeras con el mecanismo REST de coturn (use-auth-secret),
username = caducidad:identificador y credential = HMAC-SHA1 en base64. El feed id
hace de identificador, que es lo que distingue a quien llama en Oasis. Como coturn
valida el HMAC, las credenciales se emiten sin hablar con el servidor y caducan
solas.

La ruta GET /karvan/ice las sirve solo a loopback —la credencial va firmada con el
secreto del nodo y no debe salir de la maquina— y el cliente la pide ANTES de crear
ninguna RTCPeerConnection, porque la configuracion ICE de una conexion ya creada no
se puede cambiar de forma fiable. Con dos segundos de espera maxima y respaldo a
candidatos host.

La configuracion vive en oasis-config.json bajo rtc, vacia de fabrica, y admite tres
formas: STUN propio, TURN con secreto compartido, o credenciales estaticas. Con
relayOnly se fuerza iceTransportPolicy relay para que ningun par vea la IP del otro.

Verificado: sin configuracion devuelve iceServers vacio; con secreto emite credencial
reproducible y con caducidad; cero referencias a Google en el codigo.
This commit is contained in:
SITO 2026-08-19 00:16:50 +02:00
parent c3a1c841e6
commit 841acf2a7a
4 changed files with 92 additions and 6 deletions

View file

@ -110,7 +110,10 @@
// --- WebRTC (data-channel, perfect negotiation) ---
var RTC = window.RTCPeerConnection || window.webkitRTCPeerConnection;
var rtcConfig = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
// Sin servidores ICE por defecto: solo candidatos host (LAN y NAT amable).
// Un STUN de terceros aprende la IP publica y el momento de cada llamada, asi que
// no se pone ninguno de fabrica. Si el nodo ofrece TURN propio, /karvan/ice lo da.
var rtcConfig = { iceServers: [] };
function setLive(on) {
if (!liveTxt) return;
@ -291,9 +294,23 @@
setInterval(pollMsgs, 2000);
if (RTC) {
setLive(false);
announce();
setInterval(announce, 8000);
pollSignals();
setInterval(pollSignals, 2000);
// La configuracion ICE tiene que estar resuelta antes de la primera
// RTCPeerConnection: no se puede cambiar de forma fiable una ya creada.
var iceReady = fetch("/karvan/ice", { headers: { accept: "application/json" } })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (d) {
if (d && Array.isArray(d.iceServers) && d.iceServers.length) {
rtcConfig = { iceServers: d.iceServers };
if (d.relayOnly) rtcConfig.iceTransportPolicy = "relay";
}
})
.catch(function () {});
var iceTimeout = new Promise(function (res) { setTimeout(res, 2000); });
Promise.race([iceReady, iceTimeout]).then(function () {
announce();
setInterval(announce, 8000);
pollSignals();
setInterval(pollSignals, 2000);
});
}
})();