Twilio Video で TURN（443/tcp）を強制してみる

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズのTwilioエバンジェリストの高橋です。

今回は、Twilio VideoでTURNサーバーを強制的に利用する設定方法について説明します。

# なぜTURNを強制するのか

Twilio Videoは御存知の通り、WebRTCという技術を利用しています。
WebRTCでは、ビデオ会議に参加する参加者がどのような方式で通信をするかを、事前にやり取りして決めています。これをシグナリングと呼びます。
シグナリングの中でも難しいのが、NAT（Network Address Translation）と呼ばれるアドレス変換や、社内から外部へのファイアウォールの対応です。
社内のPC同士が通信をするのであれば、そもそもアドレス変換も不要なため、直接お互いのIPアドレスで通信が可能ですが、社外のPCとの通信となると、前述のNATやファイアウォールを超えなくてはなりません。
NATやファイアウォールを超える記述も色々用意されていますが、もっともシンプルなのがUDPを使った**UDPホールパンチング**と呼ばれる手法です。しかし、企業によってはUDP通信に対してより厳しい制限をかけているところもあり、UDPホールパンチングも利用できないケースが発生します（特定のサーバーからのUDPのみ許可をしている、もしくはUDP自体を禁止しているなど）。

## UDP or TCP

本来、WebRTCの通信では、パフォーマンスの面や、再接続の簡便さなどからUDP通信を推奨していますが、企業側の制約などによりUDPが使えない場合を想定して、TCPポートを使った通信を実現する方法が用意されています。
その方法が今回ご紹介するTCPポート（443）を使ったTURN接続です。ちなみにTURNでは、TCP以外にもUDPも利用できますので、TURNのTCP接続は非常に厳しいセキュリティポリシーで運用されている企業向けになります。
TCPポート443は、本来HTTPS（SSL）通信に利用されるポートで、インターネットでは標準的なプロトコルなため、多くの企業でも許可されています（ただし、アプリケーションレイヤーでセキュリティチェックをしている場合は、本来のHTTPSプロトコルでないものは弾かれるケースもあります。そのような場合は、残念ながら今回の設定をしても接続はできません）。

# Twilio VideoにおけるNTS

前述の通り、ひとことでWebRTCといっても、実際に接続する方法は色々あります。そのため、どの方式を利用するかを事前にやり取りして決めるわけですが、そのときに登場するのが、STUNサーバーやTURNサーバーです。
STUNサーバーとTURNサーバーの総称を**Network Traversal Service（NTS）**と呼び、Twilio Videoでは、Twilioが用意したNTSを利用します。ちなみにTwilioではNTSを単体でも提供していますが、サービス自体はどちらも同じものです。
なお、NTSについて詳しく知りたい方は、[こちら](https://jp.twilio.com/docs/stun-turn/faq)のドキュメントを参照してください。

Twilioが提供するNTSはグローバルで９箇所のリージョンで提供されており、STUNサーバーの利用料は無料、TURNサーバーは有料ですが、Twilio Videoを利用した場合は、Twilio Videoの料金に含まれます（WebRTC GOを利用した場合のみ、一定の上限があります）。

## STUNサーバー

WebRTC通信に先立って使われるのが、STUNサーバーです。このサーバーの目的は、自分自身が相手からどのように見えるかをチェックするためのものです。STUNサーバーはインターネット上に存在するため、前述のNATやファイアウォールを経由した場合でも通信ができるかを確認するために重要な役割を果たします。
たとえば、前述のUDPホールパンチングで通信ができそうであれば、そちらを優先して利用しようと調整します。

## TURNサーバー

社内に設置されているファイアウォールやNAT機能を実装したルータでは、一般的に外部から内部へのアクセスは禁止されており、内部からの戻りパケットに関してのみ外部からの通信が許可されるケースがほとんどです。UDPホールパンチングでも、この仕組みを使って通信を実現しています。
しかしそれでも外部からの通信がうまく行かない場合に利用されるのがTURNサーバーです。簡単に言うと**WebRTCの中継サーバー**です。

# Twilio Videoで、TURNを強制するには

通常は、シグナリング作業の中でTURNサーバーの利用の有無やプロトコルの選択などが行われますが、コード内から利用するTURNサーバーを強制させることもできます。
具体的には、シグナリングの中でやり取りされる**ICE Server**と呼ばれる接続サーバーのリストをスタティックに指定することで実現します。

## JavaScript SDKでの実装例

以下のコードは、Twilio Videoのルーム接続時にTURNのTCP/443を利用するICEサーバーを強制的に指定するサンプルです。

```javascript
Video.connect(token, {
    iceServers: [{
        username: NTS利用ユーザー名,
        credential: NTS利用パスワード,
        url: turn:global.turn.twilio.com:443?transport=tcp,
        urls: turn:global.turn.twilio.com:443?transport=tcp,
    }],
    iceTransportPolicy: 'relay',
}).then(room => { // 省略
```

コードを見ると、NTSを利用するためのユーザ名とパスワードが必要であることがわかります。
なので、これを[取得するためのAPI](https://www.twilio.com/docs/stun-turn)が用意されています。

```javascript:Node.jsの例
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(context.API_KEY, context.API_SECRET, { 
    accountSid: context.ACCOUNT_SID 
});

client.tokens.create().then(token => console.log(token.username));
```

このAPIを実行すると、以下のようなトークンが取得でき、その中にICEサーバーのリストが含まれます。

```json
{
  "username": "dc2d2894d5a9023620c467b0e71cfa6a35457e6679785ed6ae9856fe5bdfa269",
  "ice_servers": [
    {
      "url": "stun:global.stun.twilio.com:3478?transport=udp",
      "urls": "stun:global.stun.twilio.com:3478?transport=udp"
    },
    {
      "username": "dc2d2894d5a9023620c467b0e71cfa6a35457e6679785ed6ae9856fe5bdfa269",
      "credential": "xxxxxxxxxxxxxxxxxx",
      "url": "turn:global.turn.twilio.com:3478?transport=udp",
      "urls": "turn:global.turn.twilio.com:3478?transport=udp"
    },
    {
      "username": "dc2d2894d5a9023620c467b0e71cfa6a35457e6679785ed6ae9856fe5bdfa269",
      "credential": "xxxxxxxxxxxxxxxxxx",
      "url": "turn:global.turn.twilio.com:3478?transport=tcp",
      "urls": "turn:global.turn.twilio.com:3478?transport=tcp"
    },
    {
      "username": "dc2d2894d5a9023620c467b0e71cfa6a35457e6679785ed6ae9856fe5bdfa269",
      "credential": "xxxxxxxxxxxxxxxxxx",
      "url": "turn:global.turn.twilio.com:443?transport=tcp",
      "urls": "turn:global.turn.twilio.com:443?transport=tcp"
    }
  ],
  "date_updated": "Fri, 01 May 2020 01:42:57 +0000",
  "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "ttl": "86400",
  "date_created": "Fri, 01 May 2020 01:42:57 +0000",
  "password": "xxxxxxxxxxxxxxxxxx"
}
```

このリストにある、`turn:global.turn.twilio.com:443?transport=tcp`が今回強制的に設定したいTURNサーバーの情報になります。
TTLが86400なので、usernameとcredentialの有効期限は１日に設定されていますので、通常はこのコードをルーム接続前に実行するのがよいでしょう（たとえば、Videoトークンを取得するときに一緒に取得するなど）。

# テストしてみる

では実際にICEサーバーの指定をした場合と、しなかった場合をWebRTCのステータスで確認してみましょう。
WebRTCのステータスを確認するには、ChromeブラウザがWebRTC Internalsが便利です。
具体的には、Chromeブラウザを開き、アドレスバーに`chrome://webrtc-internals/`と入力します。

## ICEサーバーを指定しなかったとき

ICEサーバーの指定はせずに、NATの内側にあるブラウザからグループルームに接続したときのステータスは以下のように表示されています。
![UDPホールパンチング.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/9ee778f5-cca7-0038-e1e1-978924d87566.png)

217.178から始まるIPアドレスは、NATの外側アドレス（ISPから割り当てられているグローバルアドレス）です。
また、`candidateType`が`prflx`になっていることにも注目します。これは、UDPホールパンチングを使って接続されていることを意味しています。

## ICEサーバーを指定したとき

同様に、ICEサーバーを指定して見た場合のステータスも見てみましょう。
![TURNサーバー利用時.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/47e11ece-9249-ddd3-84bc-c7d016db6e0d.png)

今度は、`candidateType`が`relay`になっています。これはTURNサーバーを利用しているというステータスです。また、`relayProtocol`には、`tcp`と表示されているので、UDPではなくTCPを使ったTURN接続になっていることがわかります。
ちなみに、54.65.63.206は、Twilio NTSの日本（jp1）リージョンです。ICEサーバーに`global.turn.twilio.com`を指定すると、Twilioがクライアントが一番近い（遅延がすくない）リージョンを選択してくれます。

# まとめ

繰り返しにはなりますが、通常はTURNサーバーを強制しなくとも自動的に接続方法が選択されます。しかし、ファイアウォールの設定などにより、どうしても接続がうまく行かない場合などでは、上記のような接続パターンを強制的に試してみるのが良いでしょう。

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilio は音声通話、メッセージング（SMS /チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウド API サービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。

自己紹介|　
:--|:--
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b51a8b88-abd5-ce5e-195a-de3564ba2ae6.png" height='200' width='150'/>|高橋克己（Katsumi Takahashi） 自称「<font color='red'>赤い芸人</font>」<br />グローバル・インターネット・ジャパン株式会社　代表取締役<br />株式会社KDDIウェブコミュニケーションズ　Twilio事業部エバンジェリスト<br /><br />2001年より大手通信事業者の法人サービスの教育に携わり、企業における電話のしくみや重要性を研究。2016年よりTwilio事業部にジョインし、Twilioを使ったスマートコミュニケーションの普及活動を精力的に行っている。<br />2015 Hall of Doers<br />2019 Twilio Champions