−25%

Windows 按年付费,截至 10 月 31 日。 查看套餐

EQVPS
开始使用

WebRTC TURN 服务器(coturn)VPS

在严格 NAT 后面失败的通话需要 TURN 中继。在带独立 IPv4 的服务器上运行 coturn:端口、有时效的凭据、经过实测的配置和合适的套餐。

你的 WebRTC 应用在自己的 Wi-Fi 下正常,偏偏在那位从酒店打来的客户那里失败。点对点媒体需要双方之间有一条路径;当双方都在严格 NAT 之后,或者防火墙丢弃 UDP,就没有路径,通话根本连不上。谁也看不到报错——只有一个没完没了的“正在连接…”。

TURN 服务器就是这类通话的后备方案:双方把媒体流发给它,由它转发。如果你在应用里加入视频或语音、为团队运行 Matrix 或 Jitsi,或者给游戏做语音聊天,你就需要一台。coturn 是标准的开源实现,而且很轻。

选哪个套餐

TURN 对 CPU 和内存的消耗很小。它消耗的是带宽:每条中继流进出各经过服务器一次,共两次。我们套餐的流量在 1 Gbit/s 下不限量,所以问题在于同时有多少通话在中继,而不是要付多少钱。

同时中继的通话数套餐
几通——一个团队、一个小应用Nano-IP:2 vCPU、1 GB、15 GB(每月 $8)
几十通Micro-IP:2 vCPU、2 GB、25 GB(每月 $10)
更多,或把 TURN 放在应用服务器旁边Small-IP:4 vCPU、4 GB、35 GB(每月 $16)

必须是带独立 IPv4 的套餐——TURN 的核心就是大量端口上的入站连接,而 NAT 套餐从外部只接受 SSH。在我们的独立 IP 套餐上,公网地址直接配置在服务器的网卡上,所以 coturn 不需要 external-ip 这一行就能找到它。把服务器放在离用户近的地方:德国或芬兰,下单时可看到是否有货。如果你在意,可以用加密货币付款,无需 KYC。

需要开放的端口:UDP 和 TCP 3478,中继媒体用的 UDP 49152–65535,如果加 TLS 再开 TCP 5349。

安装与配置

在 Ubuntu 24.04 或 Debian 12 上以 root 身份执行:

apt install -y coturn
ip -4 addr    # 网卡上应能看到你的公网 IP

用一个新的共享密钥写入配置:

SECRET=$(openssl rand -hex 32)
cat > /etc/turnserver.conf <<EOF
listening-port=3478
fingerprint
use-auth-secret
static-auth-secret=$SECRET
realm=turn.example.com
min-port=49152
max-port=65535
no-cli
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
EOF
echo "$SECRET"    # 后端需要这个值
systemctl restart coturn
ufw allow 22/tcp && ufw allow 3478 && ufw allow 49152:65535/udp && ufw enable

别省略 denied-peer-ip 这几行。没有它们,任何持有有效凭据的人都能借你的中继访问私有地址——包括服务器自身内部一侧的服务。我们验证过:有了这些行,到私有地址的中继会被以“403 Forbidden IP”拒绝。

会过期的凭据

使用 use-auth-secret 时,coturn 接受包含过期时间的用户名,以及作为该用户名 HMAC 的密码。它们由你的后端生成;密钥从不离开服务器端。用 Node.js:

const crypto = require("crypto");

// 临时 TURN 用户名/密码(coturn 通过 use-auth-secret 支持的 TURN REST API 方案)
function turnCredentials(secret, user, ttlSeconds = 3600) {
  const username = `${Math.floor(Date.now() / 1000) + ttlSeconds}:${user}`;
  const credential = crypto.createHmac("sha1", secret).update(username).digest("base64");
  return { username, credential };
}

console.log(JSON.stringify(turnCredentials(process.env.TURN_SECRET, "alice")));

在浏览器里向后端请求一组新凭据,交给 RTCPeerConnection:

const { username, credential } = await fetch("/api/turn-credentials").then((r) => r.json());

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: "stun:turn.example.com:3478" },
    {
      urls: ["turn:turn.example.com:3478?transport=udp", "turn:turn.example.com:3478?transport=tcp"],
      username,
      credential,
    },
  ],
});

测试

在服务器上,用 openssl 按同样的方案生成(得到的密码与 Node.js 代码完全一致——我们比对过):

U="$(( $(date +%s) + 3600 )):test"
P=$(echo -n "$U" | openssl dgst -binary -sha1 -hmac "$SECRET" | base64)
turnutils_uclient -y -n 10 -u "$U" -w "$P" YOUR_SERVER_IP

最后你希望看到“Total lost packets 0”。密码错误时,客户端会停在“Cannot complete Allocation”——这说明服务器在正常工作。

我们还用真实的 Chromium 验证过:设置 iceTransportPolicy: "relay" 并使用新凭据时,浏览器从服务器拿到了 relay 候选;密码错误时一个都没有。想用同样方法测试你的配置,可以临时打开这个选项——它会强制所有通话走 TURN。

需要时再加 TLS

有些网络会屏蔽 UDP,对普通 TCP 也很挑剔;基于 TLS 的 TURN 能穿过其中更多网络。为 turn.example.com 申请证书(certbot 就行)。证书文件只有 root 能读,而 coturn 以 turnserver 用户运行,所以要给 coturn 一份它自己的副本:

mkdir -p /etc/coturn
cp /etc/letsencrypt/live/turn.example.com/fullchain.pem /etc/letsencrypt/live/turn.example.com/privkey.pem /etc/coturn/
chown turnserver: /etc/coturn/*.pem && chmod 640 /etc/coturn/privkey.pem
cat >> /etc/turnserver.conf <<'EOF'
tls-listening-port=5349
cert=/etc/coturn/fullchain.pem
pkey=/etc/coturn/privkey.pem
EOF
systemctl restart coturn && ufw allow 5349/tcp

在 urls 列表中加入 turns:turn.example.com:5349?transport=tcp。证书有效期很短,所以请把复制步骤放进 certbot 的 deploy hook——否则 TLS 会在第一次续期时悄悄失效。

值得了解

  • TURN 不会把糟糕的连接变好。它多加了一跳,所以要把服务器放在需要它的人附近。
  • 如果你运行 SFU——比如 Jitsi 的 videobridge——那是另一个组件。TURN 帮助的是无法直接连到它的客户端。
  • 整个安全模型就是那个密钥。不要放进前端,一旦泄露就立即更换。

相关内容

准备好部署了?加密货币付款、无需KYC——约一分钟上线。

立即部署 →

常见问题

WebRTC 通话为什么需要 TURN 服务器?

WebRTC 会尝试直接连接通话双方,通常都能成功。当双方都位于对称型 NAT 或会丢弃 UDP 的防火墙之后——酒店 Wi-Fi、办公网络、部分移动运营商——就没有直连路径,通话会悄无声息地失败。TURN 服务器正是为这些通话中继媒体流;常被引用的比例大约是十通里有一通。

TURN 能跑在 NAT 套餐上吗?

不能。客户端要连接 UDP 和 TCP 3478 以及中继端口范围,而 NAT 套餐从外部只接受你的 SSH 端口。你需要带独立 IPv4 的套餐。

它用多少带宽?

每条中继流都要经过服务器两次——从一方进来,再出去到另一方。每个方向 1–2 Mbit/s 的一对一视频通话,在服务器上就是几 Mbit/s。CPU 和内存几乎感觉不到;真正的上限是 1 Gbit/s 端口。

为什么不直接在应用里写死用户名和密码?

因为浏览器 JavaScript 里的一切都是公开的,而密码已知的 TURN 服务器就是给整个互联网用的免费中继。使用 use-auth-secret,你的后端会发放会过期的凭据,用浏览器永远看不到的密钥签名。

5349 端口需要 TLS 吗?

在屏蔽 UDP、又对普通 TCP 很严格的网络里有帮助。等普通 TURN 跑通后再加:给 coturn 一张 TURN 主机名的证书,并开放 5349/tcp。我们也测试过通过 TLS 的中继。

评论

暂无评论。来做第一个吧。

发表评论

评论在显示前会经过审核。