element-calling: fix Wi-Fi calls and tighten media/TURN ports
Root cause of 'calls fail on Wi-Fi but work on mobile data': LiveKit only advertised the public/WAN IP (rtc.node_ip), so LAN clients had to hairpin through the router for media. Fixes and cleanup: - rtc.advertise_internal_ip: true — also advertise the primary interface's LAN host candidate, so Wi-Fi callers connect directly (no hairpin). - Drop rtc.port_range_start/end (30000-40000) and keep the single UDP mux (udp_port: 7882). In LiveKit 1.13.x the range takes precedence over udp_port, so media was actually spread over 10000 ports. - Drop turn.tls_port: 5349 — LiveKit advertises turns:<domain>:443 to clients regardless of tls_port, so a 5349 TURN/TLS listener was unreachable dead config (and needless attack surface). - Pin TURN relay allocation to 40000-40099 (disjoint from the media mux) and open/forward that range; the old default overlapped RTC media. - turn.allow_restricted_peer_cidrs with the LAN subnet derived from the primary interface: without it the relay refuses to deliver to the private LAN host candidate and its final hop would fall back to WAN hairpin. - Update Hub port guidance (server.py) to the new list.
This commit is contained in:
@@ -291,11 +291,10 @@ FEATURE_REGISTRY = [
|
|||||||
"port_requirements": [
|
"port_requirements": [
|
||||||
{"port": "80", "protocol": "TCP", "description": "HTTP (redirect to HTTPS)"},
|
{"port": "80", "protocol": "TCP", "description": "HTTP (redirect to HTTPS)"},
|
||||||
{"port": "443", "protocol": "TCP", "description": "HTTPS (domain)"},
|
{"port": "443", "protocol": "TCP", "description": "HTTPS (domain)"},
|
||||||
{"port": "7881", "protocol": "TCP", "description": "LiveKit WebRTC signalling"},
|
{"port": "7881", "protocol": "TCP", "description": "WebRTC media (TCP fallback)"},
|
||||||
{"port": "7882", "protocol": "UDP", "description": "LiveKit media (UDP mux)"},
|
{"port": "7882", "protocol": "UDP", "description": "WebRTC media (UDP)"},
|
||||||
{"port": "5349", "protocol": "TCP", "description": "TURN over TLS"},
|
{"port": "3478", "protocol": "UDP", "description": "TURN relay + STUN"},
|
||||||
{"port": "3478", "protocol": "UDP", "description": "TURN (STUN/relay)"},
|
{"port": "40000-40099", "protocol": "UDP", "description": "TURN relay (WebRTC media)"},
|
||||||
{"port": "30000-40000", "protocol": "TCP/UDP", "description": "TURN relay (WebRTC)"},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -395,11 +394,10 @@ FEATURE_SERVICE_MAP = {
|
|||||||
|
|
||||||
# Port requirements for service tiles (keyed by unit name or icon)
|
# Port requirements for service tiles (keyed by unit name or icon)
|
||||||
_PORTS_ELEMENT_CALLING = [
|
_PORTS_ELEMENT_CALLING = [
|
||||||
{"port": "7881", "protocol": "TCP", "description": "LiveKit WebRTC signalling"},
|
{"port": "7881", "protocol": "TCP", "description": "WebRTC media (TCP fallback)"},
|
||||||
{"port": "7882", "protocol": "UDP", "description": "LiveKit media (UDP mux)"},
|
{"port": "7882", "protocol": "UDP", "description": "WebRTC media (UDP)"},
|
||||||
{"port": "5349", "protocol": "TCP", "description": "TURN over TLS"},
|
{"port": "3478", "protocol": "UDP", "description": "TURN relay + STUN"},
|
||||||
{"port": "3478", "protocol": "UDP", "description": "TURN (STUN/relay)"},
|
{"port": "40000-40099", "protocol": "UDP", "description": "TURN relay (WebRTC media)"},
|
||||||
{"port": "30000-40000", "protocol": "TCP/UDP", "description": "TURN relay (WebRTC)"},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Units whose port requirements exist purely so the user can forward them in
|
# Units whose port requirements exist purely so the user can forward them in
|
||||||
|
|||||||
@@ -185,6 +185,14 @@ EOF
|
|||||||
fi
|
fi
|
||||||
echo "Detected primary network interface: $IFACE"
|
echo "Detected primary network interface: $IFACE"
|
||||||
|
|
||||||
|
# Derive the LAN subnet this box sits on so the embedded TURN relay
|
||||||
|
# is allowed to hand media to LiveKit's LAN host candidate (see the
|
||||||
|
# allow_restricted_peer_cidrs block below). Computed from the primary
|
||||||
|
# interface's own address, so it always matches the subnet the LAN
|
||||||
|
# clients (phones on Wi-Fi) actually live on.
|
||||||
|
LAN_CIDR=$(ip -4 -o addr show dev "$IFACE" | awk '{print $4}' | grep -vE '^(127\.|169\.254\.)' | head -n1 | python3 -c 'import sys, ipaddress; s = sys.stdin.read().strip(); print(str(ipaddress.ip_network(s, strict=False)) if s else "")' 2>/dev/null)
|
||||||
|
echo "Derived LAN CIDR for TURN relay: ${LAN_CIDR:-<none>}"
|
||||||
|
|
||||||
# Generate the full LiveKit config the daemon will load. turn.domain and
|
# Generate the full LiveKit config the daemon will load. turn.domain and
|
||||||
# rtc.interfaces.includes are only known at runtime, so they are
|
# rtc.interfaces.includes are only known at runtime, so they are
|
||||||
# substituted here. The cert/key paths point at the LoadCredential-staged
|
# substituted here. The cert/key paths point at the LoadCredential-staged
|
||||||
@@ -229,10 +237,9 @@ port: 7880
|
|||||||
rtc:
|
rtc:
|
||||||
use_external_ip: false
|
use_external_ip: false
|
||||||
node_ip: $PUBLIC_IP
|
node_ip: $PUBLIC_IP
|
||||||
|
advertise_internal_ip: true
|
||||||
tcp_port: 7881
|
tcp_port: 7881
|
||||||
udp_port: 7882
|
udp_port: 7882
|
||||||
port_range_start: 30000
|
|
||||||
port_range_end: 40000
|
|
||||||
interfaces:
|
interfaces:
|
||||||
includes:
|
includes:
|
||||||
- $IFACE
|
- $IFACE
|
||||||
@@ -244,10 +251,9 @@ port: 7880
|
|||||||
rtc:
|
rtc:
|
||||||
use_external_ip: true
|
use_external_ip: true
|
||||||
skip_external_ip_validation: true
|
skip_external_ip_validation: true
|
||||||
|
advertise_internal_ip: true
|
||||||
tcp_port: 7881
|
tcp_port: 7881
|
||||||
udp_port: 7882
|
udp_port: 7882
|
||||||
port_range_start: 30000
|
|
||||||
port_range_end: 40000
|
|
||||||
interfaces:
|
interfaces:
|
||||||
includes:
|
includes:
|
||||||
- $IFACE
|
- $IFACE
|
||||||
@@ -264,16 +270,37 @@ EOF
|
|||||||
# vhost (/livekit/jwt/sfu_webhook → 8073).
|
# vhost (/livekit/jwt/sfu_webhook → 8073).
|
||||||
LK_KEY=$(cut -d: -f1 < ${livekitKeyFile} | tr -d '[:space:]')
|
LK_KEY=$(cut -d: -f1 < ${livekitKeyFile} | tr -d '[:space:]')
|
||||||
|
|
||||||
|
# TURN/TLS is intentionally not configured (no tls_port): LiveKit
|
||||||
|
# advertises turns:<domain>:443 to clients regardless of tls_port, so
|
||||||
|
# a 5349 TURN/TLS listener would be unreachable and only adds attack
|
||||||
|
# surface. The staged cert/key stay for a future TURN/TLS-on-443
|
||||||
|
# (Caddy layer4 SNI) setup.
|
||||||
cat >> /run/livekit/livekit.yaml <<EOF
|
cat >> /run/livekit/livekit.yaml <<EOF
|
||||||
room:
|
room:
|
||||||
auto_create: false
|
auto_create: false
|
||||||
turn:
|
turn:
|
||||||
enabled: true
|
enabled: true
|
||||||
domain: $MATRIX
|
domain: $MATRIX
|
||||||
tls_port: 5349
|
|
||||||
udp_port: 3478
|
udp_port: 3478
|
||||||
|
relay_range_start: 40000
|
||||||
|
relay_range_end: 40099
|
||||||
cert_file: /run/credentials/livekit.service/turn-cert
|
cert_file: /run/credentials/livekit.service/turn-cert
|
||||||
key_file: /run/credentials/livekit.service/turn-key
|
key_file: /run/credentials/livekit.service/turn-key
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# By default the embedded TURN relay refuses to send media to
|
||||||
|
# private/loopback peers. That would force its final hop to the
|
||||||
|
# public/WAN IP (hairpin NAT) — exactly what breaks calls on routers
|
||||||
|
# without NAT loopback. Allow the LAN subnet so the relay can deliver
|
||||||
|
# directly to LiveKit's LAN host candidate instead.
|
||||||
|
if [ -n "$LAN_CIDR" ]; then
|
||||||
|
cat >> /run/livekit/livekit.yaml <<EOF
|
||||||
|
allow_restricted_peer_cidrs:
|
||||||
|
- $LAN_CIDR
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >> /run/livekit/livekit.yaml <<EOF
|
||||||
webhook:
|
webhook:
|
||||||
api_key: $LK_KEY
|
api_key: $LK_KEY
|
||||||
urls:
|
urls:
|
||||||
@@ -315,10 +342,14 @@ EOF
|
|||||||
"turn-key:/var/lib/livekit/turn.key"
|
"turn-key:/var/lib/livekit/turn.key"
|
||||||
];
|
];
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 5349 7881 ];
|
# 5349/TCP (TURN/TLS) is deliberately absent — see livekit-turn-setup. RTC
|
||||||
|
# media uses the single UDP mux (7882); the 30000-40000 range is gone so
|
||||||
|
# media is no longer spread across 10000 ports. The TURN relay allocation
|
||||||
|
# range (40000-40099) is kept separate from the media mux.
|
||||||
|
networking.firewall.allowedTCPPorts = [ 7881 ];
|
||||||
networking.firewall.allowedUDPPorts = [ 3478 7882 ];
|
networking.firewall.allowedUDPPorts = [ 3478 7882 ];
|
||||||
networking.firewall.allowedUDPPortRanges = [
|
networking.firewall.allowedUDPPortRanges = [
|
||||||
{ from = 30000; to = 40000; } # LiveKit internal TURN relay range
|
{ from = 40000; to = 40099; } # LiveKit embedded TURN relay allocation range
|
||||||
];
|
];
|
||||||
|
|
||||||
####### JWT SERVICE RUNTIME CONFIG #######
|
####### JWT SERVICE RUNTIME CONFIG #######
|
||||||
|
|||||||
Reference in New Issue
Block a user