🛰️

STUN / TURN 自建部署调研

← 返回调研中心

NAT 穿透基础设施:STUN 几乎免费,TURN 才是要算账的那个

本篇聚焦自建部署:STUN/TURN/ICE 到底各自解决什么问题、哪些环节真的需要 TURN、单机能撑多少路、带宽怎么算钱、coturn 生产配置与安全加固怎么写、443 端口怎么和 Web 服务共存、多区域高可用怎么做,以及自建与托管的成本临界点在哪。

一句话结论:STUN 只在建连阶段交换几个包,自建成本近似为零,应当自建(不要把 Google 公共 STUN 当生产依赖);TURN 是媒体中继,成本 = 出网流量,必须先算清"中继占比 × 码率"。对我们的业务有个关键边界:PSTN/SIP 外呼链路根本不需要 TURN(服务器对服务器,双方公网可达);TURN 只服务于浏览器网页座席、移动 App 客户端与真 P2P 回呼,而且当座席连的是自家公网媒体网关/SFU(ICE-Lite)时,多数连接会直连,只有 UDP 被企业防火墙封死时才回落 TURN/TCP-443。纯音频场景的中继流量通常只有每月几十 GB 量级——此时 Cloudflare TURN($0.05/GB,前 1 TB 免费、Anycast 就近)比自建更划算;自建的真正理由是视频/大流量(TB 级)、固定出口 IP 白名单、数据不出境与区域覆盖(新加坡/圣保罗托管商单价高达 $0.60–0.80/GB)。自建就用 coturn 4.15+(2026 年两个月连发 5 个版本、密集修 CVE,务必追新),照 EnableSecurity 加固模板配 ACL 与限额,并牢记"网卡带宽先于 CPU 触顶"。

编制日期 2026-07-30 | 三轮迭代(原理与方案 → CR 补盲区:容量/成本/安全/云原生 → 深化:厂商数字自校验 + 配置样例 + 决策表)| 信源:coturn 官方 Release/Wiki(GitHub API 实时核对)、EnableSecurity 加固指南、Cloudflare/Twilio/Metered/Xirsys 官方定价、ICEPerf 独立测速、l7mp/LiveKit/Jitsi/mediasoup 官方文档、webrtcHacks、APNIC/Google IPv6 统计,关键数据 2+ 信源交叉验证

▦一、先把四个词分清:STUN / TURN / TURNS / ICE

名词解决什么流量特征成本量级现行 RFC
STUN我在 NAT 后的公网映射地址是什么(srflx 候选)+ ICE 连通性检查只有信令级小包(几百字节 × 几次),媒体不经过它近似为零RFC 8489(原 5389)
TURN打洞失败时由服务器中继全部媒体(relay 候选)全部媒体流经服务器,进出双向= 出网流量费,主成本RFC 8656(原 5766);IPv6 扩展 6156;TCP relay 6062
TURNSTURN over TLS/DTLS,穿透深度包检测防火墙(常挂 443)同 TURN,加 TLS 握手与加密开销同 TURN同 8656
ICE把 host / srflx / relay 候选排优先级并竞速选路——RFC 8445(原 5245)

NAT 类型决定要不要中继

NAT / 网络类型行为直连可能性
Full-cone / Restricted cone映射对外稳定STUN 打洞基本成功
Port-restricted cone需先向对端确切 IP:端口发过包看对端类型,多数可成
Symmetric NAT(含多数 CGNAT)每个远端目标一个新映射,端口不可预测双方都是对称型时必须中继
企业防火墙 / 酒店网络UDP 全禁,只放 TCP 443必须 TURN over TLS 443

移动网络的运营商级 NAT(CGNAT)是打洞失败的主因,跨双层 CGNAT 时几乎必然回落中继(APNIC)。反向的好消息是 IPv6 双端可用即可绕过 NAT 直连:全球 IPv6 采用率已约 49.6%(Google,2026-07-26),越南约 65%+、墨西哥约 57.7%,而印尼仅约 26%——同样的产品,在印尼要按更高的中继占比做预算,在越南/墨西哥可以更乐观。

◆二、谁真的需要 TURN:先砍掉不需要的部分

链路是否需要 TURN原因预算口径
AI 外呼 / 呼入:FreeSWITCH ↔ 线路商 SBC(SIP/RTP)不需要服务器对服务器,双方公网 IP 固定,不存在 NAT 穿透问题;被叫是普通电话,媒体走 PSTN0
浏览器网页座席 → 自家媒体网关/SFU(公网 IP,ICE-Lite)兜底需要服务器侧公网可达,多数直连;只有企业防火墙封 UDP 时回落 TURN/TCP-443按座席通话时长 × 10–20% 中继占比
移动 App 客户端 → 自家媒体服务器兜底需要CGNAT + 封 UDP 的移动网络需中继同上,CGNAT 重的市场取上限
真 P2P(网页回呼、双端都在 NAT 后的视频客服)必备两端都可能是对称 NAT按 20% 中继率(行业均值)预算,企业网络场景可达 40–60%

中继占比:别照抄"20%"

  • 行业常引区间:0–50%,均值约 20%(bloggeek);消费级网络 15–20%,企业网络 40–60%(厂商经验值,未见公开可复现压测)。
  • 这个数字强依赖架构:P2P 产品才有 20%+;客服/外呼这种"客户端 ↔ 公网 SFU"的星型架构,中继占比通常远低于 P2P 产品,因为服务器侧永远是公网可达的一半。
  • 正确做法:上线第一周就采集 RTCPeerConnection 的选中候选类型(relay / srflx / host)并按国家、网络类型分维度统计——用自己的 relay 占比替换所有二手数字,这是唯一能决定容量与成本的输入。

Σ三、容量与带宽数学(含对厂商数字的自校验)

1)一路中继到底吃多少带宽

  • TURN 服务器对一路中继流是收进来再转出去:网卡吞吐 ≈ 2 × 单向码率。若一通双向通话的两个方向都经同一台 TURN,网卡吞吐 ≈ 4 × 单向码率。
  • 计费口径不同:托管商(Cloudflare/Twilio)通常只按出网(egress)计费,入网免费;云主机自建同理只收出网。所以"计费 GB ≈ 单向码率 × 时长 × 走中继的方向数"。
  • 音频:Opus 宽带净荷 16–24 kbps,加 RTP/UDP/IP/SRTP 头后线路约 30–50 kbps/方向,工程按 50 kbps 估;每方向每小时 ≈ 22 MB,双向 ≈ 45 MB/小时。
  • 视频 720p(含音频)按 1.5 Mbps:每方向每小时 ≈ 675 MB,双向 ≈ 1.35 GB/小时——视频比音频贵 30 倍,是自建与否的真正分水岭。
# 月度中继流量速算公式(出网口径)
月 GB = 单向码率(kbps) ÷ 8 ÷ 1024 × 3600 ÷ 1024 × 通话小时 × 中继占比 × 中继方向数

# 例 A:50 席网页座席,人均 6 通话小时/天 × 22 天 = 6,600 小时/月,音频 50kbps,中继占比 20%,双向
6600 × 0.2 × 0.045 GB/h ≈ 59 GB/月   → Cloudflare 免费额度(1 TB)内,$0
# 例 B:10 万分钟/月 PSTN 外呼(SIP 中继)
0 GB  ← 不经 TURN
# 例 C:同样 6,600 小时但是 720p 视频客服,中继占比 20%,双向
6600 × 0.2 × 1.35 GB/h ≈ 1,782 GB/月 → 托管 Twilio 新加坡 ≈ $1,069/Cloudflare ≈ $39/自建 VPS ≈ $20–40

2)单机并发:厂商给的 5,000–10,000 路,自己算一遍就漏气了

常见口径(CelloIP 生产指南):4 vCPU / 8 GB / 1 Gbps 网卡可撑 5,000–10,000 路并发音频中继或 500–1,000 路 720p 视频。用上面的双向公式核一下:

场景厂商上限反推所需网卡吞吐1 Gbps 网卡实际可承载(留 40% 余量)
音频 50 kbps 单向中继10,000 路10,000 × 50k × 2 = 1.0 Gbps(刚好打满)约 6,000 路
音频,双向都经同一 TURN10,000 路2.0 Gbps(超)约 3,000 通
720p 视频 1.5 Mbps1,000 路1,000 × 1.5M × 2 = 3.0 Gbps(远超)约 200–300 路

结论:厂商上限本质是"网卡刚好打满"的理论值,不是可运营容量。TURN 容量规划应当先算网卡,再谈 CPU——coturn 官方 Wiki 也把 NIC/RSS、UDP 哈希表、文件描述符列为主要瓶颈,CPU 反而不是第一约束。

3)容易被忽略的三个硬上限

  • relay 端口数:默认 min-port=49152 / max-port=65535 = 16,384 个端口,每个 allocation 至少占 1 个 → 单实例硬上限约 1.6 万 allocation。coturn 4.13.0 起的 --multiplex-peer 用"每线程共享中继 socket"取代"每 allocation 一个端口",官方称对不依赖唯一中继端口的系统能显著提升容量(官方未给倍数,需自测)。
  • 文件描述符:默认 1024,约 1,000 路后就"Too many open files"。必须 LimitNOFILE=1048576 + fs.file-max。
  • 内核 UDP 处理:大量长连 UDP socket 下收包路径是瓶颈;对策是 NIC 的 RSS / 软件 RPS、加大 rmem_max/wmem_max、netdev_max_backlog。coturn 4.13.0 已在 Linux 上默认开启 --udp-recvmmsg(批量收包),4.14.0 重写了 Prometheus 支持并暴露 recvmmsg/sendmmsg 批量指标。

¥四、自建 vs 托管:临界点由托管商单价决定,差 12 倍

↔ 表格可横向滑动 | 单价为 2026-07-30 官方牌价口径

方案单价免费额度区域/接入要点
Cloudflare Realtime TURN$0.05/GB(仅出网计费)1,000 GB/月;与其 SFU 搭配时 TURN 免费;STUN 免费无限Anycast 全球就近,无区域差价性价比与延迟双优;不支持 TCP relay(RFC 6062),凭据最长 48 小时,需支持 ICE restart
Twilio NTS美西/欧洲 $0.40;新加坡/孟买/东京 $0.60;悉尼/圣保罗 $0.80STUN 免费五大洲固定 PoP恰好在我们的两个重点区域(东南亚、拉美)最贵;ICEPerf 实测其 TURN 候选延迟明显落后
Metered.ca套餐制 $99/150 GB、$199/500 GB、$499/2 TB(折合 $0.25–0.66/GB)有免费档(口径不一)31+ 区域,含新加坡、圣保罗区域覆盖好,单价中等
Xirsys$39/50 GB、$99/150 GB、$199/500 GB、$499/2 TB(超量 $0.09–0.50/GB)免费档 500 MBPoP 以美欧为主亚太/拉美覆盖弱
腾讯云 / 阿里云无独立 TURN/STUN 计费产品——官方路径是整套 RTC(TRTC / 阿里云 RTC,按分钟计费);腾讯 TRTC 文档里 coturn 只作为"企业内网代理"出现——即需要独立 TURN 时官方也让你自建
自建 coturn(大云)出网 $0.08–0.12/GB(AWS/GCP/Azure/阿里国际)→ $80–120/TBAWS 100 GB/月、GCP 200 GB/月区域最全大云出网单价比 Cloudflare TURN 贵一倍以上——在大云上自建 TURN 省不了钱
自建 coturn(含流量套餐 VPS)Hetzner 含 20 TB/月、超量约 €1/TB;DO/Vultr 含 1–4 TB、超量 $0.01/GB—Hetzner 无拉美机房;Vultr 有新加坡/圣保罗$20–60/月即可覆盖 TB 级中继,自建省钱的前提是选对流量套餐主机
≈1.4–2.2 TB/月
对 Cloudflare 的自建打平点
按自建总成本 $20–60/月 + 前 1 TB 免费推算,低于此值自建不划算
≈35–100 GB/月
对 Twilio($0.60/GB)的打平点
同样的自建成本,面对高价托管商时打平点低 12–40 倍
17.9 ms vs 139.6 ms
ICEPerf TURN 候选延迟
Cloudflare(Anycast)vs Twilio,差 680%;Metered 约 23 ms
10–100×
云出网单价极差
$1–10/TB(套餐 VPS)↔ $80–120/TB(大云)

结论:自建的理由通常不是"省钱"

  • 纯音频客服/外呼场景,托管更划算:几十 GB/月落在 Cloudflare 免费额度内,还白拿 Anycast 就近与零运维。
  • 自建的四个真实理由:① 视频/大流量把账单推到 TB 级;② 需要固定出口 IP 供客户白名单;③ 数据主权/合规要求媒体不经第三方;④ 需要托管商没有的区域或需与自家 SFU 同机房降 RTT。
  • 反直觉:在 AWS/GCP/阿里国际站上"自建省钱"是伪命题——大云出网 $80–120/TB 比 Cloudflare TURN 的 $50/TB 还贵,自建只剩控制权收益。

◉五、开源实现选型:coturn 仍是默认答案,但要看清它的脾气

实现定位活跃度(2026-07)适用短板
coturn 推荐C 语言,功能最全的 STUN/TURN 守护进程;多数据库后端、OAuth、CLI/Web 管理面、Prometheus极活跃:源码 4.15.0(2026-07-22)、官方镜像 4.16.0-r0(2026-07-30);两个月连发 5 个版本;14.2k starsVM/裸机、协议完备性要求高、要 TCP/TLS/DTLS 全支持老 C 代码库安全面大(2026 年连续修 CVE);配置项极多易踩坑;官方明确不提供 Helm chart
eturnalErlang/OTP(ProcessOne,源自 ejabberd 模块),YAML 配置,内置 TURN REST 临时凭据仓库有维护(近期还在升 OTP),但发布节奏约一年一版(1.12.2 / 2025-05)XMPP 生态、想要低运维单体、高并发连接社区规模远小;官网只声明支持 RFC 5389/5766/6156(未宣称 8656)
pion/turn v4Go 库,不是开箱服务:自己写壳、自定义认证与配额活跃(pion 生态底座;STUNner 数据面即基于它)Go 服务内嵌 TURN、认证要与业务系统深度耦合无用户库/管理面/运维工具,全部自建
STUNner(l7mp)Kubernetes 的 WebRTC 媒体网关:用 Gateway API(Gateway/UDPRoute CRD)声明式管 TURN 入口活跃,有官方 Helm chart 与 managed dataplane媒体服务器跑在 K8s 私有 Pod 网络、要弹性伸缩且不想用 hostNetwork与 K8s 强绑定;背后是小公司;协议特性不如 coturn 全
restundC,模块化插件(现挂 baresip 组织),历史最大用户是 Wire低Wire 存量生态新项目不建议
turn-rs / violetRust / 轻量 C++ 实现小众简单场景替代 coturn、嵌入式生产案例少

coturn 项目健康度:2022 年的"濒死"叙事已过时

  • 历史:创始人离开 → 2021 秋停更 → 社区发帖问 "is this project dead?" → 2022 年 Pavel Punsky(@eakraly)等接手救活(webrtcHacks 专访)。
  • 现状:2026 年 6–7 月连发 4.12.0 → 4.16.0,内容涵盖性能(--udp-recvmmsg Linux 默认开启、--multiplex-peer)、可观测(Prometheus 重写、支持 HTTPS 指标端点)、协议(RFC 8016 mobility 平滑切换)与大量安全修复。
  • 代价:密集的安全修复本身印证了老 C 代码库的固有风险。EnableSecurity 的判断很直白——"coturn 是部署最广的 TURN 服务器,也是我们发现漏洞最多的那个"。运维纪律:把 coturn 当作需要主动追版本的组件,而不是装完就忘的基础设施。
  • 扩展哲学:维护者明确解释了为什么没有 Helm chart——TURN 是全球性系统(DNS 就近 + 区域内轮询 + 共享用户库),无法用一条 helm install 表达。

如果你已经在用 LiveKit / Jitsi / mediasoup

  • LiveKit 自带 TURN,且与信令认证打通(只有已建 signal 连接的客户端能用其 TURN,不需要独立凭据体系)。注意:不挂 LB 时 turn.tls_port 必须是 443(这是对客户端广告的端口);挂 L4 LB 时 443→5349 并设 external_tls: true;云上还要 rtc.use_external_ip: true。
  • Jitsi Meet Debian 安装默认带 coturn(3478/UDP + 5349/TLS),凭据由 Prosody 走 XEP-0215 下发临时凭据;官方手册给的 443 复用做法就是 nginx stream + ssl_preread 按 SNI 分流。
  • mediasoup / Janus 服务端通常不需要 TURN:mediasoup 实现 ICE-Lite、永远做 ICE server,浏览器与媒体服务器直连;TURN 只作为客户端侧在封 UDP 网络下的中继。这条恰好解释了为什么客服星型架构的 TURN 用量远低于 P2P 产品。
  • K8s 环境:coturn 的 relay 端口区间与 K8s Service 模型天然冲突(Service 无法声明端口区间),因此大家被迫 hostNetwork: true——这是官方文档明示的反模式(每节点只能一个媒体 Pod、公网 IP 上全 UDP 敞开),且 GKE Autopilot 直接禁用 hostNetwork。要云原生就上 STUNner,或干脆把 TURN 放回 VM。

⚙六、生产级 coturn:一份可抄的配置与部署清单

部署七步按顺序做

  1. 主机与网络:静态公网 IP + DNS A/AAAA 记录(TLS 必需);起步规格 4 vCPU / 8 GB;优先选含大流量套餐的主机(见成本章)。
  2. 装最新版:发行版仓库常年落后,优先官方 Docker 镜像 coturn/coturn(因 relay 端口范围大,容器官方建议 --network=host)。装完先 turnserver --version 确认 ≥ 4.15。
  3. 证书:Let's Encrypt + certbot;用 systemd .path 监听证书文件变化自动 reload coturn(续期后不 reload 会静默失效)。
  4. 认证:只用 use-auth-secret 时限凭据(HMAC-SHA1,后端签发),绝不把静态用户名密码或 secret 下发到浏览器。
  5. ACL 加固:按 EnableSecurity 模板封掉全部私网/回环/链路本地/IPv4-mapped IPv6 网段(下方配置)。
  6. 限额与内核调优:user-quota / total-quota / max-bps / bps-capacity + fd 与 UDP buffer。
  7. 可观测:开 Prometheus 指标(4.14 起重写、支持 HTTPS 端点)+ node-exporter,对分配速率、带宽、认证失败、ACL 拒绝告警。

turnserver.conf 关键段(合并官方文档 + EnableSecurity 加固模板,已剔除过期参数)

# ── 监听与中继 ─────────────────────────────
listening-port=3478
tls-listening-port=5349
alt-tls-listening-port=443          # 企业防火墙"核选项";与 Web 共 IP 时见第七章
listening-ip=0.0.0.0
external-ip=<公网IP>                 # 云上 1:1 NAT 必填,可写 公网IP/内网IP
relay-ip=<公网IP>
min-port=49152
max-port=65535                      # 可收窄;收窄即降低单机 allocation 上限

# ── 认证:时限凭据 ──────────────────────────
lt-cred-mech
use-auth-secret
static-auth-secret=<openssl rand -hex 32>
realm=t                              # 短 realm 可减小 401 响应体积(降低放大倍数)
stale-nonce=600

# ── TLS ───────────────────────────────────
cert=/etc/letsencrypt/live/turn.example.com/fullchain.pem
pkey=/etc/letsencrypt/live/turn.example.com/privkey.pem
cipher-list="HIGH"
# 注意:no-tlsv1 / no-tlsv1_1 在 4.7.0 已被移除,写了会报 Bad configuration format

# ── 访问控制(防中继滥用,第一优先级)────────
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=172.16.0.0-172.31.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255   # 云元数据服务 169.254.169.254
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=224.0.0.0-239.255.255.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255  # 关键:堵 IPv4-mapped 绕过
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
# 若媒体只发往自家 SFU,改用白名单最稳(Zoom 的做法):
# allowed-peer-ip=<SFU公网IP> + denied-peer-ip=0.0.0.0-255.255.255.255

# ── 协议瘦身 ───────────────────────────────
no-tcp-relay                          # WebRTC 只要 UDP 中继,关掉 RFC6062
# no-rfc5780 / no-software-attribute 在 4.7.0+ 已是默认,无需再写
unauthorized-ratelimit                # 4.14.0+,限制 UDP 401 反射(实验性,先灰度)
unauthorized-ratelimit-rps=10

# ── 限额(按实际峰值给余量,别照抄)─────────
user-quota=100
total-quota=10000                     # 全服务器上限:设太小 = 自制 DoS(错误 486)
max-allocate-lifetime=3600
max-bps=<单会话上限>
bps-capacity=<整机带宽上限>

# ── 运维面与进程 ───────────────────────────
no-cli                                # 管理面是历史 CVE 高发区,一律关
# web-admin 不要开
prometheus
proc-user=turnserver
proc-group=turnserver
log-file=/var/log/coturn/turnserver.log
syslog

防火墙与内核(最常见的"能连上但没声音"就出在这)

# 端口:信令口 + 中继口,云安全组与主机防火墙两层都要开
ufw allow 3478/udp; ufw allow 3478/tcp
ufw allow 5349/udp; ufw allow 5349/tcp
ufw allow 443/tcp
ufw allow 49152:65535/udp        # ← 忘了这条:allocate 成功、媒体不通

# /etc/sysctl.d/99-coturn.conf
net.core.rmem_max=26214400
net.core.wmem_max=26214400
net.core.netdev_max_backlog=65536
net.ipv4.udp_rmem_min=8192
net.ipv4.udp_wmem_min=8192
fs.file-max=2097152

# /etc/systemd/system/coturn.service.d/limits.conf
[Service]
LimitNOFILE=1048576

后端签发时限凭据(TURN REST API 事实标准)

// username = 过期时间戳[:业务ID],credential = HMAC-SHA1(username, secret) 的 base64
function turnCredentials(secret, ttl = 12 * 3600) {
  const username = `${Math.floor(Date.now()/1000) + ttl}:agent-42`;
  const credential = crypto.createHmac('sha1', secret).update(username).digest('base64');
  return { username, credential };
}
// 下发多个 URL 让 ICE 自行竞速:UDP 优先,TCP/TLS 443 兜底,再挂一个托管商做异地冗余
iceServers: [
  { urls: 'stun:turn-sg.example.com:3478' },
  { urls: ['turn:turn-sg.example.com:3478?transport=udp',
           'turns:turn-sg.example.com:443?transport=tcp'], username, credential },
  { urls: 'turn:turn.cloudflare.com:3478', username: cfUser, credential: cfCred }  // 兜底
]

注意:draft-uberti-behave-turn-rest-00 早已过期但仍是事实标准,coturn / eturnal / Cloudflare 都兼容。TTL 别给太长(Cloudflare 侧上限 48 小时),且必须服务端签发。

⇄七、443 端口:企业环境的"核选项"与共存做法

为什么必须有 443

  • 企业防火墙、酒店/机场网络、部分运营商会直接封 UDP 3478/5349;TURN over TLS on TCP 443 长得像普通 HTTPS,是穿透率最高的兜底。厂商经验:不配 443 的话,企业环境会有 15–20% 的通话失败。
  • 代价必须认清:TCP 中继在丢包网络上会因重传与队头阻塞抬高延迟与抖动——它是兜底,不是主路径。ICE 的候选优先级本身会让 UDP 优先,只要 UDP 通就不会走它。

同一 IP 上同时跑 Web 和 TURNS:nginx stream + ssl_preread 按 SNI 分流

# 需要 nginx ≥1.11.5 且编入 stream_ssl_preread 模块(Jitsi 官方手册同款方案)
stream {
  map $ssl_preread_server_name $backend {
    turn.example.com  127.0.0.1:5349;   # TURNS → coturn
    default           127.0.0.1:8443;   # 其它 → Web
  }
  server {
    listen 443;
    ssl_preread on;
    proxy_pass $backend;
    proxy_timeout 3600s;                # 长通话别被默认超时切断
  }
}

nginx 只读 ClientHello 的 SNI、不解密,因此不影响 DTLS/SRTP 端到端加密。若 TURN 独占一个 IP,直接 alt-tls-listening-port=443 更简单——但注意 443/UDP 也建议放开(可蹭 QUIC 放行策略)。

◈八、多区域与高可用:TURN 的"集群"和你想的不一样

先破一个普遍误解:Redis ≠ 会话共享

大量教程写"配 Redis 实现 coturn 集群"。实际上 coturn 的 Redis/MySQL/PostgreSQL 后端存的是用户库与状态上报,allocation(中继会话)本身不能跨节点漂移——媒体流的五元组绑在具体那台机器的那个端口上。所以:

  • 共享数据库解决的是"多节点共用同一套凭据/配额",不是"节点挂了会话不断"。TURN 的故障恢复靠客户端 ICE restart 重新分配,不靠服务端漂移。
  • 若用 L4 负载均衡,必须保证同一客户端 IP 粘同一节点(RTP/RTCP 对、TCP relay 需要会话间交互);用到 mobility(RFC 8016)时更不能放 LB 后面。
  • coturn 内置的 ALTERNATE-SERVER(300 重定向)做负载均衡,浏览器并不支持,别指望它。

可落地的三层扩展法(也是 coturn 维护者的官方立场)

  1. 客户端多 URL 竞速(最稳、最省事):后端在 iceServers 里同时下发 2–3 个区域的 TURN URL,让 ICE 自己按 RTT 选路。天然容灾,不需要任何 LB。
  2. DNS 就近 + 区域内轮询:GeoDNS / 延迟路由(如 Route 53 latency-based)解析到最近区域,区域内多台做 round-robin DNS;配 _turn._udp / _turns._tcp SRV 记录。
  3. 共享用户库:各区域 coturn 共用同一 static-auth-secret(时限凭据机制下最省事)或共享数据库,保证凭据全局可用。

为什么必须区域化:TURN 在媒体路径上,选错区域等于媒体绕地球。Anycast(Cloudflare)由 BGP 自动就近,自建做不到——ICEPerf 实测 Anycast 的 TURN 候选延迟 17.9 ms vs 固定 PoP 厂商 139.6 ms,就是这个差距的量化。自建的替代方案只能是"多区域 + 就近解析"。

区域选点(东南亚 + 拉美)

  • 东南亚:新加坡(AWS/GCP/Azure/阿里/腾讯/DO/Vultr 全有)为主枢纽;雅加达(AWS ap-southeast-3、GCP、阿里云)覆盖印尼本地;曼谷(AWS 泰国区 ap-southeast-7,2025-01 GA)。
  • 拉美:圣保罗(AWS sa-east-1、GCP、Azure、Vultr)覆盖南美锥体;墨西哥中部(AWS Querétaro 2025-01 GA、GCP、Azure)覆盖墨西哥+中美。
  • 选点纪律:TURN 应与用户就近,而不是与自家机房就近;同时尽量与媒体网关/SFU 同区域,避免"用户→TURN→跨洲 SFU"两跳绕行。各国到区域的具体 RTT 需自测(云商延迟矩阵/wondernetwork),不要照抄经验值。

⚠九、安全:TURN 是"自带认证的开放代理"

攻击面后果对策
中继滥用 / 内网穿透(#1 威胁)拿到凭据者可用你的 TURN 访问内网服务、localhost、云元数据端点 169.254.169.254,或把你的服务器当 C2 跳板(Slack、Zoom 都出过真实案例)全私网/回环/链路本地 + ::ffff: 映射段 denied-peer-ip;能白名单就白名单(只允许自家 SFU IP)
放大 / 反射 DDoS伪造源 IP 发未认证请求,401 响应(含 REALM/NONCE)比请求大约 4 倍,反射打受害者短 realm;4.14.0+ 开 unauthorized-ratelimit;极端场景 no-udp 只留 TCP/TLS(代价是延迟);网络层 nftables 限速 + BCP38
凭据泄露长期静态凭据一旦泄露即被白用流量只用时限凭据(HMAC),TTL 数小时级;凭据 API 自身要限流鉴权
管理面漏洞(CVE 高发区)Web 管理面出过认证绕过(任意错误密码可登录,2019→4.9.0 修)、存储型 XSS、SQL 注入(PostgreSQL 后端可达 OS 命令执行);CLI 出过任意文件写no-cli + 不开 web-admin;必须开就绑管理网
配额耗尽(自伤)server 级 total-quota 设太小 → 一个人就能把全局分配打满,其他人全 486per-user 与 server 级配合;配额不能替代源 IP 级网络限速

coturn CVE 时间线(为什么"追版本"是第一安全控制)

版本修复内容
4.15.0(2026-07-22)GHSA-5538-7cxj-5jcc:MESSAGE-INTEGRITY 之后的 STUN 属性未按 RFC 8489 §9 忽略;mobility 会话恢复绑定原属主;多处溢出/截断修复
4.13.x(2026-06)CVE-2026-53450(CVSS 7.4,::ffff:127.0.0.1 绕过默认回环防护)、CVE-2026-53449(CLI psd 任意文件写);4.13.1 起默认拒绝 link-local/ULA 中继并统一规范化 IPv4-in-IPv6 编码
4.12.0 / 4.11.0CVE-2026-53448(管理面 SQL 注入,CVSS 7.2)/CVE-2026-43915(管理面存储型 XSS)
4.10.0CVE-2026-43994(CVSS 8.1,OAuth 令牌解码认证前栈溢出,仅 --oauth 模式)
4.9.0CVE-2026-27624:::ffff:x.x.x.x 绕过全部 IPv4 denied-peer-ip(对 CVE-2020-26262 修复的绕过);管理面反向密码校验

显式写死 denied-peer-ip 范围的部署没被 CVE-2026-53450 影响——"不依赖默认值、显式声明 ACL"这一条纪律,实际挡掉了一个 CVSS 7.4。

✗十、教程陷阱与反直觉(照抄博客最容易死在这十条)

抄配置前先看这几处

  1. total-quota=100:某高排名多区域教程里就这么写——这是全服务器只允许 100 路中继,第 101 个用户直接 486,等于自制 DoS。
  2. no-tlsv1 / no-tlsv1_1:4.7.0 起已移除(TLS 1.0/1.1 默认关),继续写会报 Bad configuration format;同理 no-rfc5780 / no-software-attribute 在 4.7.0+ 已是默认。
  3. denied-peer-ip=::0 绝不能写:::0 被当通配符匹配所有对端(含 IPv4),会把全部中继一刀砍死,所有版本通病。
  4. no-stun 的含义常被写错:它是"关闭 STUN Binding 服务",不是"要求 TURN 认证";关了之后这台机器就不能兼当 STUN 用。想反过来只做 STUN 用 stun-only。
  5. 忘了 external-ip:云上 1:1 NAT 环境不设,XOR-RELAYED-ADDRESS 会返回私网地址,客户端拿到无用候选——K8s/云上最典型的失败。
  6. 只开信令口不开中继口:49152-65535/udp 必须在云安全组和主机防火墙两层都放行,否则"分配成功但没声音"。
  7. Redis 不等于会话高可用(见第八章),别把它当集群方案卖给自己。
  8. 只设 IPv4 relay-ip:IPv6 对端将无法被中继。安全上是收益、功能上是限制,要明确取舍。
  9. 把 Google 公共 STUN 当生产依赖:无 SLA、国内可达性不稳。STUN 自建成本几乎为零(只有信令级流量),生产应自建 + 可用 stun.cloudflare.com(免费无限)做冗余。
  10. 托管商能力不等价:Cloudflare TURN 不支持 TCP relay(RFC 6062)、凭据最长 48 小时、allocation 可能因维护/路由变化中断(官方要求客户端支持 ICE restart)。选托管前要按自己的客户端能力核对这些细则。

三条反直觉总结

  • 自建不一定省钱:在大云上自建(出网 $80–120/TB)比 Cloudflare TURN($50/TB,前 1 TB 免费)更贵;省钱的前提是选含流量套餐的主机。
  • 厂商标称容量是网卡打满值:1 Gbps 机器"10,000 路音频"反推正好 1.0 Gbps,实际可运营量要打 4–6 折;视频标称 1,000 路反推 3 Gbps,根本不成立。
  • 外呼业务的 TURN 账单可能接近 0:PSTN 链路不经 TURN,星型架构又让多数客户端直连——先量出自己的 relay 占比,再决定要不要自建这套东西。

✓十一、验证与压测:上线前必须跑通的六件事

验证清单

  1. 监听确认:ss -tulpn | grep turnserver,3478/5349/443 齐全。
  2. 协议连通:turnutils_uclient -t -T -v -u USER -w PASS turn.example.com(TCP/TLS 分别跑);openssl s_client -connect turn.example.com:443 -servername turn.example.com 验 SNI 分流与证书链。
  3. 强制走中继:浏览器侧把 iceTransportPolicy 设成 'relay',只有这样才真的在测 TURN(默认 'all' 时通常直连,测不到)。
  4. 候选类型统计:Trickle-ICE 页面 + chrome://webrtc-internals 看选中候选是 relay / srflx / host,并把这个指标接进生产埋点。
  5. 指标与告警:coturn Prometheus 端点(4.14+ 支持 HTTPS)+ node-exporter;告警项:分配速率突增、带宽尖峰、认证失败率、ACL 拒绝次数、证书到期。
  6. 横向对标:ICEPerf.com 提供各托管 TURN 的 STUN/TURN 候选延迟与建连时间公开数据,可作为"自建是否值得"的外部参照。

▶十二、落地路线与决策速查表

P0(1–2 周):先量数据,别先买机器

  • 客户端埋点采集选中候选类型 + 按国家/网络分布,量出真实 relay 占比与每通中继时长。
  • 同时接入 Cloudflare TURN(免费额度 1 TB/月,STUN 免费无限)作为第一版可用能力——一天内可上线,不需要任何运维。
  • 并行搭一台自建 coturn 4.15+(Docker + host 网络 + 加固模板 + 时限凭据 + 443),作为第二 URL 与压测靶子。

P0 的产出是一个数字:月中继 GB。它决定后面所有事。

P1(1 个月):按数据决定自建规模

  • < 1 TB/月且无固定 IP/合规要求 → 停在托管,自建那台留作备用与内网调试。
  • > 1–2 TB/月或有白名单/数据不出境要求 → 在新加坡 + 圣保罗各起一台含流量套餐主机,客户端下发多区域 URL 竞速 + GeoDNS。
  • 把限额(user-quota/total-quota/max-bps/bps-capacity)按实测峰值 × 1.5 配置;评估 --multiplex-peer 与端口范围收窄。

P2(持续):运维纪律

  • 订阅 coturn Release/安全公告,按月追版本(2026 年节奏是月度级安全修复)。
  • 证书自动 reload、配额与带宽告警、ACL 拒绝日志巡检;每季度按 EnableSecurity 模板复核一次配置漂移。
  • 若媒体栈整体迁入 K8s,再评估 STUNner 替换 coturn;不迁就别为了"云原生"给自己找 hostNetwork 的麻烦。
你的情况选择理由
纯 PSTN/SIP 外呼,没有 WebRTC 客户端不需要 TURN服务器对服务器,先别建
音频网页座席,月中继 < 1 TBCloudflare TURN(Anycast,1 TB 免费)免费 + 延迟最优 + 零运维
视频客服 / 月中继 TB 级自建 coturn(含流量套餐主机,新加坡/圣保罗双区)成本从千元级降到几十美元级
客户要求固定出口 IP 白名单 / 数据不出境必须自建托管商 IP 不固定、媒体经第三方
媒体服务器已在 K8s 私有网络STUNner(Gateway API)避开 hostNetwork 反模式
已用 LiveKit / Jitsi 全家桶先用其内置 TURNLiveKit 记得 443 与 use_external_ip;Jitsi 用 ssl_preread 复用 443
Go 团队要深度定制认证/配额pion/turn v4 自建服务逻辑完全可编程

与现有报告矩阵的衔接

媒体链路与自研架构:RTC 自研架构 ↗ / RTC 全球基础设施 ↗;编排框架与 wss 媒体流对接:语音 Agent 编排框架;把中继流量并入单通成本:全链路单通成本模型;PSTN 侧线路与合规:东南亚 / 拉美线路。

↗来源链接