−25%

Windows 年払い、10月31日まで。 プランを見る

EQVPS
始める

WebRTC 用 TURN サーバー(coturn)向け VPS

厳しい NAT の向こうで失敗する通話には TURN リレーが必要です。専用 IPv4 付きサーバーで動かす coturn:ポート、期限付き認証情報、検証済みの設定とプラン。

WebRTC アプリが自分の Wi-Fi では動くのに、ホテルから電話してきたひとりの顧客だけ失敗する。ピアツーピアのメディアには両側をつなぐ経路が必要です。両方が厳しい NAT の内側にいたり、ファイアウォールが UDP を落としたりすると経路がなく、通話はただつながりません。エラーは誰にも見えず、「接続中…」が延々と続くだけです。

TURN サーバーはそうした通話のための予備の経路です。両側がメディアを TURN サーバーに送り、サーバーが中継します。アプリに映像や音声を組み込んでいる、チーム用に Matrix や Jitsi を運用している、ゲームのボイスチャットを作っている——そんなときに必要になります。coturn は定番のオープンソース実装で、軽量です。

どのプランを選ぶか

TURN は CPU とメモリをほとんど使いません。使うのは帯域です。中継されるストリームはどれも、入りと出でサーバーを 2 回通ります。当社プランのトラフィックは 1 Gbit/s で無制限なので、問題になるのは同時に何件の通話を中継するかであって、いくら請求されるかではありません。

同時に中継する通話プラン
数件:チーム、小さなアプリNano-IP:2 vCPU、1 GB、15 GB(月額 $8)
数十件Micro-IP:2 vCPU、2 GB、25 GB(月額 $10)
それ以上、またはアプリサーバーと同居させる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 候補を受け取りました。誤ったパスワードでは 1 件も得られませんでした。自分の構成を同じように試したいなら、このオプションを一時的に有効にしてください。すべての通話を 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不要 — 約1分でオンライン。

今すぐデプロイ →

FAQ

そもそも WebRTC の通話に TURN サーバーが必要なのはなぜですか?

WebRTC は参加者同士を直接つなごうとし、たいていは成功します。両側が対称型 NAT や UDP を落とすファイアウォールの内側にいると(ホテルの Wi-Fi、社内ネットワーク、一部の携帯キャリアなど)、直接の経路がなく、通話は何も言わずに失敗します。TURN サーバーはまさにそうした通話のメディアを中継します。よく言われる割合は、おおよそ 10 件に 1 件です。

TURN は NAT プランで動きますか?

動きません。クライアントは UDP と TCP の 3478 番、そしてリレー用のポート範囲に接続しますが、NAT プランが外部から受け付けるのは SSH ポートだけです。専用 IPv4 付きのプランが必要です。

帯域はどれくらい使いますか?

中継されるストリームはどれもサーバーを 2 回通ります。片側から入り、もう片側へ出ていきます。片方向 1〜2 Mbit/s の 1 対 1 ビデオ通話なら、サーバー上では数 Mbit/s です。CPU と RAM はほとんど気にしません。本当の上限は 1 Gbit/s のポートです。

アプリに固定のユーザー名とパスワードを書いておけばよいのでは?

ブラウザの JavaScript にあるものはすべて公開されていて、パスワードが知られた TURN サーバーはインターネット全体の無料リレーになってしまうからです。use-auth-secret を使えば、バックエンドが期限付きの認証情報を発行し、ブラウザが決して見ないシークレットで署名します。

ポート 5349 の TLS は必要ですか?

UDP を遮断し、素の TCP にも厳しいネットワークで役立ちます。通常の TURN が動いてから追加してください。TURN のホスト名の証明書を coturn に渡し、5349/tcp を開けます。TLS 経由のリレーも検証済みです。

コメント

まだコメントはありません。最初になりましょう。

コメントを残す

コメントは表示される前にモデレートされます。