こんにちは!がねこまさしです。前回は複数人の同時通話まで実現しました。社内で使うには十分なレベルです。
しかし本格的な企業ユースとなると、まだまだ障害があります。会社と家、自社と別の会社さんなど、実際に通信しようとするとNATやFirewallといった壁が立ちはだかります。
NATを越えよう
NATの役割は
NAT(+IPマスカレード)は企業だけでなく、一般家庭でも使われています。ブロードバンドルーターやWiFiルーターでは、1つのグローバルIPアドレスを、複数のPCやデバイスで共有することができます。このとき、NATには2つの役割があります。
- インターネットにつながったグローバルなIPアドレスと、家庭内/社内のローカルなネットワークでのIPアドレスの変換
- 複数のPC/デバイスが同時に通信できるように、ポートマッピングによるポート変換
WebRTCでNAT越しに通信すること考えてみましょう。
ブラウザが知っている情報
- ローカルネットワークのIPアドレス:A
- 自分が使う(動的に割り振った)UDPポート:A/UDP
ブラウザでは分からない情報
- グローバルIPアドレス:A’
- NATによってマッピングされた、外部に向けたUDPポート:A’/UDP
Peer-to-Peer通信を行うには、シグナリング処理でお互いに(ローカルネットワーク内の情報ではなく)インターネット側から見た情報を通知する必要があります。
ブラウザーが、インターネット側から見た情報を知るための仕組みが、STUNになります。
STUNの仕組みは
STUNの仕組みは意外とシンプルです。インターネット側にいる誰か(STUNサーバー)に、自分(ブラウザ)がどう見えるか教えてもらうだけです。
STUNは元々WebRTCのために作られた仕組みではなく、より汎用的なUDP通信の補助として生まれました。VoIPやネットワークゲームの世界でも使われているようです。
自分を外側から見た情報が分かったら、それをシグナリングサーバー経由で通信相手に渡します。
お互いの情報が伝わったら、そこを目掛けて通信を行います。間にNATが挟まりますが、ポートを直接マッピングしているのであくまでもPeer-to-Peerです。
STUNサーバーを動かそう
それでは実際にSTUNサーバーを動かしてみましょう。Linuxで動作するオープンソースのものがあるので、そちらを使います。
rfc5766-turn-server
このサーバはSTUNだけでなく、後程説明するTURNにも対応しています。
DownloadページからOSに合わせたgzファイルをダウンロードし、INSTALL手順に従ってビルド、インストールしてください。参考までに以前私が実施した手順を載せておきます。※ちょっと古いバージョンです。最新のものを取得してインストールしてください。
1 2 3 4 |
$ wget https://rfc5766-turn-server.googlecode.com/files/turnserver-3.2.1.4-CentOS6-x86_64.tar.gz $ tar zxvf turnserver-3.2.1.4-CentOS6-x86_64.tar.gz $ cd turnserver-3.2.1.4 $ ./install.sh |
CentOSの場合、以下の場所に導入されました。
1 2 |
バイナリ → /usr/bin/turnserver 設定ファイル → /etc/turnserver/*.conf |
次に設定ファイル( /etc/turnserver/turnserver.conf )を見てみましょう。ポイントとなるのは次の箇所です。
1 2 3 4 5 6 7 8 |
# STUN/TURNサーバーの接続待ポート番号。デフォルトは3478です。 # コメントアウトを外して数値を指定すれば、任意のポートに変更できます。 # # 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=3478 |
TCPとUDPの両方で待ち受けできますが、STUNはUDPのみが有効なので注意が必要です。※つまりUDPが通らない環境ではSTUNは使えません。
1 2 3 4 5 6 7 8 |
# このサーバーはデフォルトでSTUN/TURNの両方をサポートしています。 # STUNのみ使いたい場合は、stun-only のコメントアウトを外します。 # # Option to suppress TURN functionality, only STUN requests will be processed. # Run as STUN server only, all TURN requests will be ignored. # By default, this option is NOT set. # #stun-only |
STUNはPeer-to-Peer通信が始まればサーバーのCPU負荷、ネットワーク負荷はかかりません。それに対して後述するTURNでは特にネットワーク負荷がかかります。サーバーを借りていてネットワーク通信量で課金されるようなケースでは、stun-onlyを設定してTURNは無効にしておいた方が良いかもしれません。
設定がすんだらSTUNサーバーを起動してみましょう。画面にエラーが出なければ無事に起動成功です。エラーが出る場合は turnserver.conf を確認してみてください。
1 |
/usr/bin/turnserver -o -v -c /etc/turnserver/turnserver.conf |
クライアントのソースを修正しよう
ここまでで準備したSTUNサーバーを使うように、クライアント側のソースを修正しましょう。前回のソースの一部を変更します。
STUNサーバーが stun.yourdomain.com で、デフォルトのポート3478で動いていると仮定します。その情報をPeerConnectionに教えてあげます。
1 2 3 4 5 6 7 8 9 10 11 |
function prepareNewConnection(id) { var pc_config = {"iceServers":[ {"url":"stun:stun.yourdomain.com:3478"} ]}; var peer = null; try { peer = new webkitRTCPeerConnection(pc_config); } catch (e) { console.log("Failed to create PeerConnection, exception: " + e.message); } //...省略... } |
さあ、これで接続を試してください。上手く行けば、自宅と友人の家で通信が可能になっているはずです。
STUNでNATを越えられないとき
NATにはグローバルIPアドレスを共有するだけでなく、セキュリティ対策としての役割もあります。内部の端末を隠したり、通信できるポートを制限したり、一種の簡易Firewallとして利用されているケースもあります。その場合はFirewallの場合と同じく、次に説明するTURNを利用する必要があります。
また、NATの構造によっては、接続先によって(今回の場合、STUNサーバーとPeer-to-Peerの通信相手)別のポートが割り当てられる Symmetric NAT という物があります。この場合もSTUNの仕組みでは通信することができません。やはりTURNの出番ということになります。
Firewallを越えよう
一般家庭のようにブロードバンドルーターなどでNATがある環境では、STUNを使えば通信が可能になります。次は一般的な企業で使えるようにしましょう。
企業ではFirewallが設置されているケースがほとんどです。その場合、外部と通信できるポートも制限されます。STUNではUDPポートは動的に割り振られるままなので、Firewallにとても大きな穴を空けないと通信ができません。きっとセキュリティ管理者に怒られてしまいます。こんなケースに対応するのが、TURNの仕組みです。TURNもWebRTCのために生まれたのではなく、VoIPやネットワークゲームの世界で使われていたものです。
TURNの仕組みは
TURNを使った通信では、TURNサーバが実際のストリームデータを受け渡す間に入ります。すべてのパケットをTURNサーバーがリレーすることになり、もはやPeer-to-Peer通信ではなくなります。この際TURNサーバーでは動画のエンコーディングは行わないので、CPU負荷よりもネットワーク負荷が高くなりやすいです。
Firewallに穴を空けよう
TURNを使うには、Firewallに1つ穴を空ける必要があります。標準では3478/UDPを使うので、そのポートが通過可能になるように設定して(してもらって)ください。
もう1つ、シグナリングサーバーと通信するための穴も空ける必要があります。例えば9000番を使うのであれば、9000/TCPも同様に通過可能になるように設定して(してもらって)ください。
会社間で通信するのは、両方の会社でFirewallに穴を空ける必要があります。
※これを読んで「結局Firewallをいじるのかよー」とがっかりした人もいますよね? Firewallをいじらない方法もあるので、最後までお楽しみに。
TURNサーバーを動かそう
TURNサーバーは先ほどSTUNサーバーとしてインストールしたrfc5766-turn-server
がそのまま使えます。/etc/turnserver/turnserver.conf の設定を変更してTURNとして使える様にします。
1 2 3 4 5 6 7 8 |
# STUN/TURNサーバーの接続待ポート番号。デフォルトは3478です。 # コメントアウトを外して数値を指定すれば、任意のポートに変更できます。 # # 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=3478 |
デフォルトのポートは3478ですが、自由に設定できます。
1 2 3 4 5 6 7 8 9 10 11 12 |
# UDP/IPの他に、rfc5766-turn-serverではTLS/DTLSでの通信も可能です(デフォルトで有効になっています) # 残念ながらブラウザー側の実装がまだ不十分なので、この機能は無効にしておく(コメントアウトを外す)ことをお勧めします # 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 |
TLS/DTLSは今回は使わない設定にしておきます。また先ほどのSTUNを動かす際に stun-only を設定した場合は、再びコメントアウトしてTURNも使える様にして下さい。
1 2 3 4 5 |
# Option to suppress TURN functionality, only STUN requests will be processed. # Run as STUN server only, all TURN requests will be ignored. # By default, this option is NOT set. # #stun-only |
そして、WebRTCでTURNを使う際のキモはこちらの設定です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 認証の方法を選択します。次の3種類があります。 # no-auth # st-cred-mech (short-term credential mechanism) # lt-cred-mech (long-term credential mechanism) # WebRTCでは lt-cred-mechを使用する必要がありますので、その行のコメントアウトを外します。 # Uncomment to use long-term credential mechanism. # By default no credentials mechanism is used (any user allowed). # This option can be used with either flat file user database or # PostgreSQL DB or MySQL DB or Redis DB for user keys storage. # lt-cred-mech # 同時に、realmも設定が必要になります。お忘れなく。 # # Realm for long-term credentials mechanism and for TURN REST API. # realm=turn.yourdomain.com |
なかなか lt-cred-mech が必須だとは分からず、とても長い間悩んでしまいました。lt-cred-mech で使用するアカウント情報はPostgreSQL, MySQL, Redisなどで管理できますが、今回はシンプルにファイル管理にします。
1 2 3 4 5 6 7 8 9 10 11 12 |
# ユーザーアカウントを定義するファイル名を指定します。 # # 'Dynamic' user accounts database file name. # Only users for long-term mechanism can be stored in a flat file, # short-term mechanism will not work with option, the short-term # mechanism required PostgreSQL or MySQL or Redis database. # 'Dynamic' long-term user accounts are dynamically checked by the turnserver process, # so that they can be changed while the turnserver is running. # # Default file name is turnuserdb.conf. # userdb=/etc/turnserver/turnuserdb.conf |
/etc/turnserver/turnuserdb.conf を使うことにしたので、その内容も変更します。
1 2 3 4 5 6 7 8 |
#This file can be used as user accounts storage for long-term credentials mechanism. # #username1:key1 #username2:key2 # OR: #username1:password1 #username2:password2 yourid:yourpassword |
例としてユーザID: yourid 、パスワード: yourpassword としました。 ※実際にはもっと強度の高いパスワードにしてくださいね。
これで turnserverを再起動すれば、TURNでの通信が有効になります。
1 |
/usr/bin/turnserver -o -v -c /etc/turnserver/turnserver.conf |
※ちなみに turnserverを安全に停止させる手段が分かりません。仕方がないので kill で殺しています…。
クライアントのソースを修正しよう
ここまでで準備したTURNサーバーを使うように、クライアント側のソースを修正しましょう。STUNで修正した部分と同じ個所になります。 STUN/TURNサーバーが turn.yourdomain.com で、デフォルトのポート3478で動いていると仮定します。その情報をPeerConnectionに教えてあげます。(STUNとTURNの両方を候補にすることができます)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function prepareNewConnection(id) { var pc_config = {"iceServers":[ {"url":"stun:turn.yourdomain.com:3478"}, {"url":"turn:turn.yourdomain.com:3478", "username":"yourid", "credential":"yourpassword"} ]}; var peer = null; try { peer = new webkitRTCPeerConnection(pc_config); } catch (e) { console.log("Failed to create PeerConnection, exception: " + e.message); } //...省略... } |
さあ、これで接続を試してください。上手く行けば、会社と自宅、あるいは会社と友人の会社で通信が可能になります。
Firewallはそのままで
実際の企業ではセキュリティ上の制約や手続き上の問題で、Firewallに穴を空けるのが大変なことも多々あります。お客様の会社だったらなおさらですよね。そんな時のために、TURN over TCP という規格があり、rfc5766-turn-server と Chrome の両方ともサポートしてます。これを使えば、Firewallはそのままで、通信が可能になります。
TURNサーバを設定し直そう
1 2 3 4 5 6 7 8 |
# 一つのポート番号で、UDPとTCPの両方を待ち受けすることができます。 # HTTPと同じ、80番を設定します。 # # 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 |
設定が終わったら、turnserverを再起動してください。
クライアントのソースを修正しよう
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function prepareNewConnection(id) { var pc_config = {"iceServers":[ {"url":"stun:turn.yourdomain.com:80"}, {"url":"turn:turn.yourdomain.com:80?transport=udp", "username":"yourid", "credential":"yourpassword"}, {"url":"turn:turn.yourdomain.com:80?transport=tcp", "username":"yourid", "credential":"yourpassword"} ]}; var peer = null; try { peer = new webkitRTCPeerConnection(pc_config); } catch (e) { console.log("Failed to create PeerConnection, exception: " + e.message); } //...省略... } |
ここでは省略しますが、シグナリングサーバーも 80/TCP で動かす必要があります。サーバー側のNode.jsのポート番号と、クライアント側のsocket.ioのつなぎ先のポート番号を80番に変更してください。※Webサーバー、シグナリングサーバー、TURNサーバーの3つをすべて80/TCPで動かすので、サーバーを3つ別々に立てる必要があります。頑張ってください。
さあ、これで最後です。ブラウザをリロードしてください。きっと壁を越えて対話ができることと思います。
最後に
これまで全5回、WebRTCの使いかたを説明してきました。開発者向けにコードを一から書いてきましたが、世の中には便利なライブラリやサービスも数多くあります。
こちらを利用するのもありですね。日本でWebRTCが盛り上がって、企業ユースでも認知されるのを期待しています。
おつきあいいただき、どうもありがとうございました。