Seu app WebRTC funciona no seu Wi-Fi e falha justamente para o cliente que liga de um hotel. Mídia ponto a ponto precisa de um caminho entre os dois lados; quando ambos estão atrás de NAT rígido, ou um firewall bloqueia UDP, não há caminho e a chamada simplesmente não conecta. Ninguém vê erro nenhum — só um «conectando…» sem fim.
Um servidor TURN é o plano B para essas chamadas: os dois lados mandam a mídia para ele, e ele retransmite. Se você está colocando vídeo ou voz num app, rodando Matrix ou Jitsi para uma equipe ou fazendo chat de voz para um jogo, você precisa de um. O coturn é a implementação open source padrão, e é leve.
Qual plano
O TURN gasta pouca CPU e memória. O que ele usa é banda: cada fluxo retransmitido passa duas vezes pelo servidor, na ida e na volta. Nos nossos planos o tráfego é ilimitado a 1 Gbit/s, então a pergunta é quantas chamadas retransmitidas rodam ao mesmo tempo, e não quanto vai aparecer na conta.
| Chamadas retransmitidas ao mesmo tempo | Plano |
|---|---|
| Algumas: uma equipe, um app pequeno | Nano-IP: 2 vCPU, 1 GB, 15 GB ($8/mês) |
| Dezenas | Micro-IP: 2 vCPU, 2 GB, 25 GB ($10/mês) |
| Mais, ou TURN ao lado do servidor do app | Small-IP: 4 vCPU, 4 GB, 35 GB ($16/mês) |
Precisa ser um plano com IPv4 próprio: TURN é todo sobre conexões de entrada em muitas portas, e planos NAT só aceitam SSH de fora. Nos nossos planos com IP dedicado, o endereço público fica direto na interface do servidor, então o coturn o encontra sem linha external-ip. Deixe o servidor perto dos seus usuários: Alemanha ou Finlândia, com a disponibilidade mostrada na hora do pedido. Pagamento em cripto, sem KYC, se isso importa para você.
Portas para abrir: 3478 UDP e TCP, 49152–65535 UDP para a mídia retransmitida e 5349 TCP se você adicionar TLS.
Instalação e configuração
Como root no Ubuntu 24.04 ou Debian 12:
apt install -y coturn
ip -4 addr # seu IP público deve aparecer na interface
Escreva a configuração com um segredo compartilhado novo:
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" # seu backend precisa deste valor
systemctl restart coturn
ufw allow 22/tcp && ufw allow 3478 && ufw allow 49152:65535/udp && ufw enable
Não pule as linhas denied-peer-ip. Sem elas, qualquer pessoa com credenciais válidas pode usar seu relay para alcançar endereços privados — inclusive serviços do lado interno do próprio servidor. Conferimos: com essas linhas, um relay para um endereço privado é recusado com «403 Forbidden IP».
Credenciais que expiram
Com use-auth-secret, o coturn aceita um nome de usuário que contém um horário de expiração e uma senha que é um HMAC dele. Seu backend gera os dois; o segredo nunca sai do lado do servidor. Em Node.js:
const crypto = require("crypto");
// Usuário/senha TURN temporários (o esquema TURN REST API que o coturn entende via use-auth-secret).
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")));
No navegador, peça ao backend um par novo e passe para o 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,
},
],
});
Teste
No servidor, com o mesmo esquema usando openssl (ele produz exatamente a mesma senha que o código Node.js — comparamos):
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
No final você quer ver «Total lost packets 0». Com a senha errada, o cliente para em «Cannot complete Allocation» — é o servidor fazendo o trabalho dele.
Também testamos a partir de um Chromium de verdade: com iceTransportPolicy: "relay" e credenciais novas, o navegador recebeu candidatos relay do servidor; com uma senha ruim, nenhum. Se quiser testar sua configuração do mesmo jeito, ative essa opção por um instante — ela força toda chamada a passar pelo TURN.
TLS, se precisar
Algumas redes bloqueiam UDP e são exigentes com TCP puro; TURN sobre TLS passa por mais delas. Obtenha um certificado para turn.example.com (o certbot serve). Os arquivos dele só podem ser lidos pelo root, enquanto o coturn roda como o usuário turnserver, então dê ao coturn uma cópia própria:
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
Adicione turns:turn.example.com:5349?transport=tcp à lista urls. Os certificados duram pouco, então coloque a cópia num deploy hook do certbot — senão o TLS quebra em silêncio na primeira renovação.
Bom saber
- TURN não transforma uma conexão ruim em boa. Ele acrescenta um salto, então coloque o servidor perto de quem precisa dele.
- Se você roda um SFU — o videobridge do Jitsi, por exemplo —, é uma peça separada. O TURN ajuda os clientes que não conseguem alcançá-lo diretamente.
- O segredo é todo o modelo de segurança. Mantenha-o fora do frontend e troque-o se algum dia vazar.
Relacionados
- VPS para Jitsi Meet: videochamadas no seu próprio servidor
- Matrix Synapse auto-hospedado: chamadas no Matrix também precisam de um servidor TURN
- VPS para um site com IP próprio: o app que distribui as credenciais
- Como configurar um firewall ufw
Comentários
Nenhum comentário ainda. Seja o primeiro.