こんにちは! 2014年に連載した「WebRTCを使ってみよう!」シリーズのアップデート記事も番外編を含めて6回目となりました。2016年の最後として、実際の通信では欠かせないNAT越えと、企業ネットワークで使うために必要なFirewallを通過する方法について見ていきましょう。
NATを越えて
NATの役割
NAT(+IPマスカレード)は企業だけでなく、一般家庭でも使われています。ブロードバンドルーターやWiFiルーターでは、1つのグローバルIPアドレスを、複数のPCやデバイスで共有することができます。このとき、NATには2つの役割があります。
- インターネットにつながったグローバルなIPアドレスと、家庭内/社内のローカルなネットワークでのIPアドレスの変換
- 複数のPC/デバイスが同時に通信できるように、ポートマッピングによるポート変換
WebRTCでNAT越しに通信すること考えてみましょう。
ブラウザが知っている情報
- ローカルネットワークのIPアドレス
- 自分が使う(動的に割り振った)UDPポート
ブラウザでは分からない情報
- グローバルIPアドレス
- NATによってマッピングされた、外部に向けたUDPポート
例えば
次の架空の例を見てみましょう。二つのPCのブラウザでは、それぞれ自分のローカルIPアドレス、UDPは知ることができます。
- 左のPCのブラウザが分かること
- ローカルIPアドレス 192.168.10.11
- ローカルUDPポート 4001/UDP
- 右のPCのブラウザが分かること
- ローカルIPアドレス 10.2.100.51
- ローカルUDPポート 5001/UDP
しかし、NATの外側から見たグローバルの情報は自分では分かりません。
- 左のPCのブラウザが知らないこと
- グローバルIPアドレス 178.50.111.222
- グローバルUDPポート 51111/UDP
- 右のPCのブラウザが知らないこと
- グローバルIPアドレス 200.100.50.81
- グローバルUDPポート 52222/UDP
NATを越えてPeer-to-Peer通信を行うには、シグナリング処理でお互いに自分の知らないグローバルの情報を交換する必要があります。
STUNが教えてくれること
自分で知らない情報は、誰かから教えてもらうしかありません。それを可能にするのがSTUN(Session Traversal Utilities for NATs)です。
STUNの仕組みはシンプルです。NATで変換されたIPアドレス/ポートを、外側にいるSTUNサーバーに教えてもらいます。先ほどの例で言えば、このようになります。
自分のグローバル情報が分かったら、それをシグナリングサーバー経由で相手に渡します。
STUNサーバーから得られた情報は、シグナリングの過程でICE Candidateとして取得できます。これをシグナリングサーバーなどを経由して相手に送ることになります。(参考: シグナリングサーバーを動かそう、シグナリングを拡張して、複数人で通信してみよう )
首尾よくNATとポート変換を潜り抜ければ、Peer-to-Peerで通信を行うことができます。
STUNサーバーを使ってみよう
STUNサーバーを自分で動かすこともできますが、幸い公開されているSTUNサーバーがあります。まず、そちらを使ってみましょう。Googleが公開しているサーバーがよく利用されているようです。
- stun.l.google.com:19302
- stun1.l.google.com:19302
- stun2.l.google.com:19302
- stun3.l.google.com:19302
- stun4.l.google.com:19302
通常はどれか1つ指定すればOKですが、複数指定することもできます。それでは、実際に以前のソースの一部を修正してみましょう。
1 2 3 4 5 6 7 8 9 |
function prepareNewConnection(id) { let pc_config = {"iceServers":[ {"urls": "stun:stun.l.google.com:19302"}, {"urls": "stun:stun1.l.google.com:19302"}, {"urls": "stun:stun2.l.google.com:19302"} ]}; let peer = new RTCPeerConnection(pc_config); // ... 省略 ... } |
さあ、これで接続を試してください。上手く行けば、例えば自宅と友人の家で通信が可能になっているはずです。
STUNでNATを越えられないとき
NATにはグローバルIPアドレスを共有するだけでなく、セキュリティ対策としての役割もあります。内部の端末を隠したり、通信できるポートを制限したり、一種の簡易Firewallとして利用されているケースもあります。その場合はFirewallの場合と同じく、次に説明するTURNを利用する必要があります。
またNATの構造によっては、接続先によって(今回の場合、STUNサーバーとPeer-to-Peerの通信相手)別のポートが割り当てられる Symmetric NAT という物があるようです。この場合もSTUNの仕組みでは通信することができません。やはりTURNの出番ということになります。
Firewallを越えたい
一般家庭のようにブロードバンドルーターなどでNATがある環境では、STUNを使えば通信が可能になります。
それに対して一般的な企業では、Firewallにより通信できるポートが制限されるケースが多いようです。STUNを使った場合でもUDPポートは動的に割り振られるままなので、通信をするためにはFirewallにとても大きな穴を空ける必要があります。それではきっとセキュリティ管理者に怒られてしまいます。
こんなケースに対応するのが、TURN(Traversal Using Relays around NAT)の仕組みです。先ほどのSTUNもTURNもどちらもWebRTCのために生まれたのではなく、以前からVoIPやネットワークゲームの世界で使われていたものです。
TURNの仕組み
TURNを使った通信では、TURNサーバが実際のストリームデータを受け渡す間に入ります。すべてのパケットをTURNサーバーがリレーすることになり、もはや厳密にはPeer-to-Peer通信ではなくなります。
ただしこの際、TURNサーバーではパケットをそのままリレーし、内容については一切変更はしません。
- 動画のデコード、エンコードは行わない
- データはそのまま受け渡し、両端のPeerで施された暗号は解除しない
そのため個人的には、アプリケーション的に見た場合にはPeer-to-Peerを維持していると考えていいと思っています。
TURNサーバーでは暗号化処理や動画のデコード/エンコーディングは行わないので、CPU負荷よりもネットワーク負荷が高くなりやすいです。
TURNサーバーを用意するには
それでは、実際にTURNサーバーを動かしてみましょう。2014年の記事ではrfc5766-turn-serverを使いましたが、今回はその後継であるcoturnを使います。coturnが生まれた背景は、こちらの記事に詳しく書かれています。
今回は、OSはUbuntu 16.04 LTSを使って導入してみました。 なるべくOSセットアップ直後の素の状態から自分でビルドしてみましょう。
自分でビルドする手順
まずは、依存ツールやライブラリを導入します。
1 2 |
sudo apt-get update sudo apt-get upgrade |
1 2 3 4 5 |
sudo apt-get install gcc sudo apt-get install sqlite3 sudo apt-get install libssl-dev sudo apt-get install libevent-dev sudo apt-get install make |
次にcoturnのソースを、こちらのサイトからダウンロードします。
私が試したときは4.5.0.4が最新でしたが、2016年12月現在は4.5.0.5が最新のようです。 利用したいバージョンのソースをダウンロードして、解凍してください。
1 2 |
wget http://turnserver.open-sys.org/downloads/v4.5.0.4/turnserver-4.5.0.4.tar.gz tar xvfz turnserver-4.5.0.4.tar.gz |
それではビルドしてみましょう。
1 2 3 4 |
cd turnserver-4.5.0.4 ./configure make sudo make install |
もし途中で依存ライブラリ不足でエラーが出た場合には、追加でインストールしてからビルドし直してみてください。ビルドに成功すると、Ubuntu16の場合は次の場所に関連ファイルがインストールされます。
- バイナリ → /usr/local/bin/turnserver
- 設定ファイル(ひな形) → /usr/local/etc/turnserver.conf.default
設定ファイルは適宜 turnserver.conf にコピーして、それを編集して利用してください。
※こちらの手順はcoturn 4.5.0.4 をビルドした際のものです。最新のものでは手順が変更されている可能性がありますので、もし何か見つけたらコメントいただけると嬉しいです。
coturn を動かすまで
coturnをTURNサーバーとして動かす際に、通信方法を2種類を使うことがことができます。
- TURN … 特定のUDPポートを利用する
- TURN over TCP … 特定のTCPポートを利用する
企業で使う場合は、後者を利用したいケースが多いと思います。今回は両方使えるように設定しましょう。
tunserver.conf の設定
まず、/usr/local/etc/turnserver.conf.default を /usr/local/etc/turnserver.conf にコピーし、それを編集します。たくさんの設定項目がありますが、TURNをWebRTCで使う際のポイントを見ておきましょう。
ポートの指定
1 2 3 4 5 |
# TURN listener port for UDP and TCP (Default: 3478). # Note: actually, TLS & DTLS sessions can connect to the # "plain" TCP & UDP port(s), too - if allowed by configuration. # listening-port=80 |
デフォルトでは3478/UDP を使いますが、それを変更して80/TCPを使うようにします。コメントアウトを外してポート番号80を指定してください。
資格証明方法(クレデンシャル)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Uncomment to use long-term credential mechanism. # By default no credentials mechanism is used (any user allowed). # lt-cred-mech # 'Static' user accounts for long term credentials mechanism, only. # This option cannot be used with TURN REST API. # 'Static' user accounts are NOT dynamically checked by the turnserver process, # so that they can NOT be changed while the turnserver is running. # #user=username1:key1 #user=username2:key2 # OR: #user=username1:password1 #user=username2:password2 user=user:password |
WebRTCで使うには、lt-cred-mech を有効にする必要があります。デフォルトではコメントアウトされているので、これを有効にしてください。ユーザアカウントは外部のDBを使うのが適切ですが、今回はシンプルに設定ファイルに直接記入しました。ご利用のシステムに合わせて、外部ファイルや外部DBをご利用ください。
同時にrealmも指定します。自分のサーバーのドメインに合わせて、適宜指定してください。
1 2 3 4 5 6 7 |
# The default realm to be used for the users when no explicit # origin/realm relationship was found in the database, or if the TURN # server is not using any database (just the commands-line settings # and the userdb file). Must be used with long-term credentials # mechanism or with TURN REST API. # realm=coturn.yourdomain.com |
通信プロトコルの指定(変更不要)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# Uncomment if no UDP client listener is desired. # By default UDP client listener is always started. # #no-udp # Uncomment if no TCP client listener is desired. # By default TCP client listener is always started. # #no-tcp # Uncomment if no TLS client listener is desired. # By default TLS client listener is always started. # #no-tls # Uncomment if no DTLS client listener is desired. # By default DTLS client listener is always started. # #no-dtls |
プロトコルの指定で no-tcp のコメントアウトを外すとTURN over TCPを利用しなくなります。今回はTCPを使いますので、コメントアウトのままにしておきます。もしUDPを使いたくない場合は no-udp のコメントアウトを外してください。
以前試したときはブラウザでの実装の不十分でtlsやdtlsを利用できなかったのですが、今回試したところデフォルトのまま(有効のまま)で通信できました。もしうまく通信できない場合は、コメントアウトを消してtlsやdtlsを無効にして試してみてください。
coturnを起動しよう
それではcoturnを起動しましょう。今回は80ポートを利用しているため、環境によってはroot権限が必要となりますので、sudoを利用してください。
1 |
sudo /usr/local/bin/turnserver -o -v -c /usr/local/etc/turnserver.conf |
ここで指定しているコマンドライン引数は次の意味です。
- -o .. デーモンとして起動
- -v .. verboseモード。ログを多く出す
- -c .. 設定ファイルのパスを指定
クライアントのソースを修正しよう
それでは準備したcoturnサーバーを使うように、にクライアント側のソースを修正しましょう。STUNで修正した部分と同じ個所になります。 coturnサーバーが coturn.yourdomain.com で、ポート80で動いていると仮定します。その情報をPeerConnectionに教えてあげましょう。
1 2 3 4 5 6 7 8 9 |
function prepareNewConnection(id) { let pc_config = {"iceServers":[ {"urls": "stun:coturn.yourdomain.com:80"}, {"urls":"turn:coturn.yourdomain.com:80?transport=udp", "username":"user", "credential":"password"}, {"urls":"turn:coturn.yourdomain.com:80?transport=tcp", "username":"user", "credential":"password"} ]}; let peer = new RTCPeerConnection(pc_config); // ... 省略 ... } |
また、企業内から外部のシグナリングサーバーを利用するには、そちらも80/TCPや443/TCPを使う必要があるかもしれません。環境に応じてご準備ください。
ここまでできたら、無事Firewallを越えて通信ができるはずです。企業内のブラウザと、外のブラウザ(自宅のPCやアンドロイドのブラウザ)で試してみてください。
最後に
WebRTC入門も2016年中になんとか予定していた記事を書き終えることができました。書いている間にもどんどんブラウザがバージョンアップし、新しいAPIが使えるようになっています。最新情報はブラウザのリリースノートやブログをご覧いただくとよいと思います。
- WebRTC.org Chrome Report Notes
- Mozilla blog Advancing WebRTC
みなさんもWebRTCを活用した素敵なアプリケーションを作ってくださいね。ここまでお付き合いいただき、どうもありがとうございました!