-
Notifications
You must be signed in to change notification settings - Fork 122
Description
Summary
The MeshCentral agent does not use STUN/TURN servers for WebRTC ICE candidate gathering, which causes WebRTC connections to fail when the browser and agent are on different networks (even when both are behind the same NAT but on different subnets).
Problem Description
When initiating a remote desktop session, MeshCentral attempts to establish a WebRTC connection between the browser and the agent. The browser correctly receives the webrtcconfig from the server and generates:
- Host candidates (local IPs)
- Server reflexive (srflx) candidates (public IP via STUN)
- Relay candidates (via TURN servers)
However, the agent only generates host candidates. It never contacts STUN servers to discover its public IP, nor does it connect to TURN servers to allocate relay addresses.
Evidence from WebRTC Internals
Browser's local candidates:
candidate:xxx 1 udp 2113937151 [local-ip] 55598 typ host
candidate:xxx 1 udp 1677729535 [public-ip] 55598 typ srflx
candidate:xxx 1 udp 33563135 [turn-server-ip] 53157 typ relay
Agent's candidates (from SDP answer):
a=candidate:0 1 UDP 2128609535 [agent-local-ip-1] 56514 typ host
a=candidate:1 1 UDP 2128609534 [agent-local-ip-2] 56514 typ host
a=candidate:2 1 UDP 2128609533 [agent-local-ip-3] 56514 typ host
a=candidate:3 1 UDP 2128609532 [agent-local-ip-4] 56514 typ host
Note: All agent candidates are typ host - no srflx or relay candidates.
Result
ICE connectivity checks fail because:
- Browser's host/srflx candidates cannot reach agent's private IPs across networks
- Browser's relay candidates (via TURN) cannot reach agent's private IPs (TURN servers cannot route to private addresses)
- For TURN relay to work, both endpoints must connect to the TURN server
ICE connection state: "checking" → "disconnected"
Connection state: "connecting" → "failed"
Technical Analysis
Browser Side (Working Correctly)
The server sends webrtcconfig to the browser via the serverinfo message:
// In meshcentral.js or webserver.js
serverinfo.webrtcconfig = { iceServers: [...] };The browser uses this configuration when creating the RTCPeerConnection:
// In default.handlebars
var configuration = webrtcconfiguration;
webRtcDesktop.webrtc = new RTCPeerConnection(configuration);Agent Side (Issue Location)
In meshcore.js, the WebRTC connection is created without any configuration:
// Around line 3888 in meshcore.js
ws.webrtc = rtc.createConnection();The rtc module is require('ILibWebRTC'), which is the native C WebRTC implementation. The createConnection() call does not accept or use any STUN/TURN configuration.
C Code Analysis
In microscript/ILibDuktape_WebRTC.c, there is a SetTurn function:
duk_ret_t ILibWebRTC_Duktape_ConnectionFactory_SetTurn(duk_context *ctx)
{
char *host = Duktape_GetStringPropertyValue(ctx, 0, "Host", NULL);
int port = Duktape_GetIntPropertyValue(ctx, 0, "Port", 3478);
char *username = Duktape_GetStringPropertyValueEx(ctx, 0, "Username", NULL, &usernameLen);
char *password = Duktape_GetStringPropertyValueEx(ctx, 0, "Password", "", &passwordLen);
// ...
ILibWrapper_WebRTC_ConnectionFactory_SetTurnServer(factory, server, username, ...);
}This indicates that the underlying C WebRTC library (ILibWebRTC) does support TURN, but:
- The
setTurnmethod is not exposed to JavaScript viaILibDuktape_CreateInstanceMethod - There is no mechanism for the server to send STUN/TURN configuration to the agent
meshcore.jsnever calls any STUN/TURN setup methods
Proposed Solution
1. Expose setTurn to JavaScript
In ILibDuktape_WebRTC.c, add the method binding:
ILibDuktape_CreateInstanceMethod(ctx, "setTurn", ILibWebRTC_Duktape_ConnectionFactory_SetTurn, 1);Also add a setStun method for STUN-only configurations.
2. Server Sends WebRTC Config to Agent
When initiating a WebRTC session, the server should include the webrtcconfig in the tunnel request:
// Server side - when sending WebRTC offer to agent
tunnelMessage.webrtcconfig = domain.webrtcconfig || obj.args.webrtcconfig;3. Agent Uses WebRTC Config
In meshcore.js, before creating the WebRTC connection:
// Parse webrtcconfig from tunnel message
if (ws.httprequest.webrtcconfig && ws.httprequest.webrtcconfig.iceServers) {
for (var i = 0; i < ws.httprequest.webrtcconfig.iceServers.length; i++) {
var server = ws.httprequest.webrtcconfig.iceServers[i];
if (server.urls) {
for (var j = 0; j < server.urls.length; j++) {
var url = server.urls[j];
if (url.startsWith('turn:')) {
rtc.setTurn({
Host: url.replace('turn:', '').split(':')[0],
Port: parseInt(url.split(':')[2]) || 3478,
Username: server.username,
Password: server.credential
});
}
}
}
}
}
ws.webrtc = rtc.createConnection();Current Workaround
WebRTC only works when:
- Browser and agent are on the same subnet (host-to-host connectivity)
- There is direct UDP routing between browser and agent networks
When clients are on different networks (common in enterprise environments with VLANs, or remote access scenarios), WebRTC fails and falls back to WebSocket relay through the server, resulting in:
- Higher latency (~200ms+ vs ~5-10ms for direct)
- Additional server bandwidth usage
- No end-to-end encryption (server decrypts/re-encrypts)
Environment
- MeshCentral Server Version: 1.1.x+
- Agent Version: Windows/Linux (both affected)
- Browser: Chrome 120+ (tested)
- WebRTC enabled in config.json:
"webrtc": true - STUN/TURN servers: Configured and working (verified with browser-only tests)
Related Documentation
From MeshCentral2DesignArchitecture.pdf, Section 9:
"Browsers typically use STUN and TURN servers to help establish WebRTC connections..."
"When a web application makes use of WebRTC, the browser and MeshAgent will attempt to negotiate a direct connection."
The documentation describes the intended behavior but does not mention that the agent lacks STUN/TURN support.
Impact
This affects all MeshCentral deployments where:
- Users access agents from different networks
- Enterprise networks with VLAN segmentation
- Remote access over the internet
- Symmetric NAT environments
Requested Changes
- Expose STUN/TURN configuration methods in the agent's JavaScript runtime
- Add server-to-agent WebRTC config transmission
- Implement STUN/TURN usage in
meshcore.js - Update documentation to reflect STUN/TURN requirements for cross-network WebRTC
Thank you for considering this enhancement. WebRTC with STUN/TURN support would significantly improve performance for cross-network remote desktop sessions.