diff --git a/Backend/.env.example b/Backend/.env.example index 6cb171d..0d2a629 100644 --- a/Backend/.env.example +++ b/Backend/.env.example @@ -17,7 +17,7 @@ MEDIA_PROVIDER=mock TURN_URLS= TURN_USERNAME= TURN_CREDENTIAL= -MEDIA_WEBRTC_LISTEN_IP=0.0.0.0 +MEDIA_WEBRTC_LISTEN_IP=127.0.0.1 MEDIA_WEBRTC_ANNOUNCED_IP= MEDIA_RTC_MIN_PORT=40000 MEDIA_RTC_MAX_PORT=49999 diff --git a/Backend/README.md b/Backend/README.md index fce9118..2784edd 100644 --- a/Backend/README.md +++ b/Backend/README.md @@ -38,7 +38,7 @@ Required env vars: | `MEDIA_SFU_ENGINE` | SFU engine for `single_server_sfu` mode (`mediasoup` default, `noop` fallback) | | `MEDIA_PROVIDER` | Media backend provider (`mock` by default) | | `TURN_URLS` / `TURN_USERNAME` / `TURN_CREDENTIAL` | TURN/STUN configuration used by single-server SFU mode | -| `MEDIA_WEBRTC_LISTEN_IP` / `MEDIA_WEBRTC_ANNOUNCED_IP` | WebRTC transport bind/announce IPs for mediasoup SFU | +| `MEDIA_WEBRTC_LISTEN_IP` / `MEDIA_WEBRTC_ANNOUNCED_IP` | WebRTC transport bind/announce IPs for mediasoup SFU. Do not leave bind IP as `0.0.0.0` without an announced IP in non-local environments. | | `MEDIA_RTC_MIN_PORT` / `MEDIA_RTC_MAX_PORT` | UDP/TCP RTP port range for mediasoup worker | | `MEDIA_RECORDINGS_DIR` | Local output directory for server-side recording workers (planned in SFU mode) | | `MEDIA_MAX_PUBLISHERS` / `MEDIA_MAX_SUBSCRIBERS_PER_ROOM` | Soft concurrency limits for single-server media mode (planned) | diff --git a/Backend/media/sfu/mediasoup.ts b/Backend/media/sfu/mediasoup.ts index bbc78a4..5f3d502 100644 --- a/Backend/media/sfu/mediasoup.ts +++ b/Backend/media/sfu/mediasoup.ts @@ -1,4 +1,5 @@ import { randomUUID } from 'crypto'; +import { networkInterfaces } from 'os'; import { mediaConfig } from '../config'; import type { @@ -39,6 +40,45 @@ const parsePort = (value: string | undefined, fallback: number): number => { return parsed; }; +const pickHostIpv4 = (): string | null => { + const interfaces = networkInterfaces(); + for (const addresses of Object.values(interfaces)) { + if (!addresses) continue; + for (const address of addresses) { + if (address.family === 'IPv4' && !address.internal) { + return address.address; + } + } + } + return null; +}; + +const resolveListenAddress = (): { ip: string; announcedAddress?: string } => { + const configuredListenIp = (process.env.MEDIA_WEBRTC_LISTEN_IP ?? '').trim(); + const configuredAnnounced = process.env.MEDIA_WEBRTC_ANNOUNCED_IP?.trim(); + + if (configuredListenIp && configuredListenIp !== '0.0.0.0') { + return { + ip: configuredListenIp, + ...(configuredAnnounced ? { announcedAddress: configuredAnnounced } : {}), + }; + } + + const discoveredIp = pickHostIpv4(); + if (!configuredAnnounced && configuredListenIp === '0.0.0.0') { + console.warn( + `[sfu] MEDIA_WEBRTC_LISTEN_IP is 0.0.0.0 without MEDIA_WEBRTC_ANNOUNCED_IP. ` + + `Using ${discoveredIp ?? '127.0.0.1'} for ICE candidates. Configure both env vars for production.`, + ); + } + + const ip = discoveredIp ?? '127.0.0.1'; + return { + ip, + ...(configuredAnnounced ? { announcedAddress: configuredAnnounced } : {}), + }; +}; + const toTransportOptions = (transport: any): SfuTransportOptions => ({ id: transport.id, iceParameters: transport.iceParameters ?? {}, @@ -105,19 +145,18 @@ export class MediasoupSfuService implements SfuService { } private async createWebRtcTransport(router: any): Promise { - const listenIp = process.env.MEDIA_WEBRTC_LISTEN_IP ?? '0.0.0.0'; - const announcedAddress = process.env.MEDIA_WEBRTC_ANNOUNCED_IP?.trim(); + const listenAddress = resolveListenAddress(); return await router.createWebRtcTransport({ listenInfos: [ { protocol: 'udp', - ip: listenIp, - ...(announcedAddress ? { announcedAddress } : {}), + ip: listenAddress.ip, + ...(listenAddress.announcedAddress ? { announcedAddress: listenAddress.announcedAddress } : {}), }, { protocol: 'tcp', - ip: listenIp, - ...(announcedAddress ? { announcedAddress } : {}), + ip: listenAddress.ip, + ...(listenAddress.announcedAddress ? { announcedAddress: listenAddress.announcedAddress } : {}), }, ], enableUdp: true, @@ -364,4 +403,3 @@ export class MediasoupSfuService implements SfuService { return consumerDescriptor; } } -