All Articles

AWS-EC2上に自分用のSTUNサーバを立てる話

WebRTCネタの続きです

間が空いてしまいましたが、引き続きWebRTC関連のエントリです。そして久々にLinux関連です。なお、前回同様調べながらの備忘録的エントリとなっています。間違っている部分もあるかもなので、専門家の方の指摘など頂けると嬉しいです。

STUNサーバも自分で用意したい

前回のエントリではうまくできなかったSTUNサーバ構築のリベンジです。今回のサーバはAWSのEC2です。(やっぱりさくらではうまく行かなかった…推測される理由は後述)

WebRTCを使ったサービスや社内アプリを開発する場合(やる可能性は低いかもですが)、当然 GoogleのようなPublicなSTUNサーバを使うのは憚られますし(SkyWayも独自に提供してますね)、認証機構などもしっかりとしておきたいですよね。今回も自分なりに理解した部分を交えて書いていきます。

EC2インスタンスのセットアップ

標準的なAmazon Linux(Amazon AMI)でセットアップしました(この辺りの詳細は他のエントリにお任せします)。yum updateまで済ませ、SSHで接続できる状態から開始です。

ソースコードのダウンロードと下準備

STUNサーバはGoogle Codeにて公開されているものを使います。全部入りのtarパッケージがあるのでダウンロードします。

$ wget https://rfc5766-turn-server.googlecode.com/files/turnserver-3.2.2.1.tar.gz

パッケージ名が「turnserver」となっているように、STUNと同時にTURNサーバも構築できるようです(今回はやってません)。STUNサーバはlibeventに依存しているようなので、先に入れておきます。合わせて、gccとopensslも無いとconfigureに失敗するようなので入れます。

$ sudo yum install libevent-devel gcc openssl-devel

つづいて、展開とコンパイル。

$ tar xvfz turnserver-3.2.2.1.tar.gz
$ cd turnserver-3.2.2.1
$ ./configure --prefix=/usr/local/tunrserver
$ make
$ sudo make install

prefixの指定は私のクセのようなものなので指定しなくても大丈夫ですが、やり直すときに/usr/local/turnserverを消せば元通りなのでこのようにしています。以降は、/usr/local/turnserver/以下にインストールされたものとして進めます(微妙にパスとか変わります)。

インストールが成功すると、ずらずらと以下のような説明文が出ます。

``` 1) If you system supports automatic start-up system daemon services, the, to enable the turnserver as an automatically started system service, you have to:
a) Create and edit /etc/turnserver.conf or
/usr/local/etc/turnserver.conf .
Use /usr/local/etc/turnserver.conf.default as an example.

b) For user accounts settingsundefined if using the turnserver
with authentication: create and edit /etc/turnuserdb.conf
fileundefined or set up PostgreSQL or MySQL or Redis database for user accounts.
Use /usr/local/etc/turnuserdb.conf.default as example for flat file DBundefined
or use /usr/local/share/turnserver/schema.sql as SQL database schemaundefined
or use /usr/local/share/turnserver/schema.userdb.redis as Redis
database schema description and/or /usr/local/share/turnserver/schema.stats.redis
as Redis status & statistics database schema description.

c) add whatever is necessary to enable start-up daemon for the /usr/local/bin/turnserver.
  1. If you do not want the turnserver to be a system service, then you can start/stop it “manually”, using the “turnserver” executable with appropriate options (see the documentation).
  2. To create database schema, use schema in file /usr/local/share/turnserver/schema.sql.
  3. For additional information, run:

    manturnserverman turnserver man turnadmin $ man turnutils

まぁ使い方ですね。メモっておくといいかもです。

設定ファイルを書く

続いて、設定ファイルを書きます。/usr/local/turnserver/etc/turnserver.conf.defaultというサンプルファイルがあるので、これをturnserver.confにコピーして使います。なお、AWS用の設定項目がちゃんと準備されているので、その辺りを重点的に設定します。

$ sudo cp /usr/local/turnserver/etc/turnserver.conf.default /usr/local/turnserver/etc/turnserver.conf
$ sudo vim /usr/local/turnserver/etc/turnserver.conf

# 以下、設定した部分のみ抜粋

# listenとrelayするIP
listening-ip=ec2 のプライベートip 
relay-ip= ec2のプライベートip

# Peer Connection時にアクセスを受け付けるIP
external-ip=ec2のパブリックip

# 受け付けるポートレンジ
min-port=32355
max-port=65535

# 出力
verbose

# フィンガープリントを有効に ※
fingerprint

# long-time-credientialを有効に ※
lt-cred-mech

# realm指定も必要 ※
realm=webrtc

# 今回はSSLは使わない(起動時に警告が出るので対策)
no-tls
no-dtls

# STUNサーバのみ使用
stun-only

# ログの出力先指定
log-file=/var/log/turnserver.log

冒頭でさくらでうまく行かなかった理由はここかもしれない。STUNサーバでは外部から受け入れるIPの他に、内部でlistenするプライベートなIPが必要なようです。もしかしたら127.0.0.1でもいいのかもしれませんが、EC2なら内部のプライベートIPも存在するので、これを使います。

STUNサーバの挙動について

STUNサーバでは、上記IP/PortのUDPTCPのパケット両方を必要とするようです。動きとしては、Peer接続が始まった(ブラウザで new RTCPeerConnection(iceServers)した)時点で、STUNサーバにUDPパケットを送信し(このタイミングでNATの情報を保持する?)、実際にコネクションを行う際にTCPパケットを使うんじゃないかなと思う。また、ポートも3478/3479の他に、min/max-portのレンジも開放する(ここでは32355-65535)必要があります。これはiptables…ではなく、UDP/TCP両方ともSecurity Groupで指定する感じですね。

またWebRTCの仕組み上では、fingerprintとlt-cred-mech、realmを有効にする必要があるようです(このあたり検証不足)。

設定の記述、ポートの開放ができたら、起動してみます。

# 面倒なのでパス通す
$ echo "export PATH=$PATH:/usr/local/turnserver/bin" >> ~/.bashrc
$ source ~/.bashrc

$ turnserver

ずらーっとログが流れ、最後にlistener openedと出れば起動成功。WARNINGが出た場合はsudoで起動すればOK(ログやpid周りだと思う)。

0: IPv4. CLI listener opened on : 127.0.0.1:5766

127.0.0.1となってますが、大丈夫みたい。netstatで確認してみるとlistenしているようです。

$ netstat -lnp
tcp        0      0 private ip:3478          0.0.0.0:*                   LISTEN      12599/turnserver
ttcp        0      0 127.0.0.1:5766              0.0.0.0:*                   LISTEN      12599/turnserver
udp        0      0 private ip:3478 0.0.0.0:* 12599/turnserve

また、ローカルマシンから3478番ポートに向けてtelnetしても接続が確認できました。

$ telnet public ip3478
Trying public ip...
Connected to xxxxxxxxxxxx.compute.amazonaws.com.
Escape character is '^]'.

あとはWebRTCのアプリからこのサーバをSTUNプロトコルで接続指定すればOKですね。

var peer = new webtitRTCPeerConnection(
	{ "iceServers": [{"url": "stun:<ec2のpublic ip>:3478"}] }
);
</ec2のpublic>```

<p>試しにNAT越しの方とPeer接続してみましたが、問題なく接続できました!</p>

<h2>起動スクリプトとか</h2>
<p>このままではコンソールに制御が戻らないので、daemonizeします。起動スクリプトを書いたのでこれを/etc/init.d/turnserverにおいてオプションで起動とかできると思います。</p>

!/bin/bash

Startup script for TURN Server

chkconfig: 345 85 15

description: RFC 5766 TURN Server

Source function library.

. /etc/rc.d/init.d/functions

TURNDIR=/usr/local/turnserver TURN=$TURNDIR/bin/turnserver PROG=turnserver TURNCFG=TURNDIR/etc/turnserver/$PROG.conf PIDFILE=/var/run/PROG.pidLOCKFILE=/var/lock/subsys/PROG.pid LOCK_FILE=/var/lock/subsys/PROG DEFAULTS=/etc/sysconfig/$PROG RETVAL=0 USER=ec2-user

start() { echo -n "Starting"StartingPROG: ” daemon —user=USERUSERTURN OPTIONSRETVAL=OPTIONS RETVAL=? if [ $RETVAL = 0 ]; then pidofproc TURN>TURN >PIDFILE RETVAL=?[? [RETVAL = 0 ] && touch $LOCKFILE && success fi echo return $RETVAL }

stop() { echo -n "Stopping"StoppingPROG: ” killproc TURNRETVAL=TURN RETVAL=? echo [ $RETVAL = 0 ] && rm -f LOCKFILELOCK_FILEPID_FILE }

[ -f $DEFAULTS ] && . DEFAULTSOPTIONS="ocDEFAULTS OPTIONS="-o -cTURNCFG $EXTRA_OPTIONS”

See how we were called.

case ”1"instart)start;;stop)stop;;status)status1" in start) start ;; stop) stop ;; status) statusTURN RETVAL=?;;restart)stopstart;;condrestart)if[f? ;; restart) stop start ;; condrestart) if [ -fPID_FILE ] ; then stop start fi ;; *) echo "Usage:"Usage:PROG {start|stop|restart|condrestart|status|help}” exit 1 esac

exit $RETVAL

起動

$ sudo /etc/init.d/turnserver start

自動起動

$ sudo chkconfig —add turnserver

とか

<p>ここまででSTUNサーバの構築は完了です!(結構大変だった)</p>

<h2>認証周りとか</h2>
<p>独自のSTUNサーバを作る理由として「認証機構を設ける」とありました。turnサーバのパッケージにはturnadminというコマンドが付属していて、独自に認証ユーザを作成することができます。また、認証情報DBはファイル(/etc/turnuserdb.conf)、DB(MySQL / PostresSQL)、Redisが使えるようです。MySQLでやろうと思ったんですが、何故かturnserver.confに設定を書くと"invalid format"って怒られたので、turnuserdb.confに追加します。以下のコマンドでユーザが発行できます。turnserver.confと同じくturnuserdb.conf.defaultというファイルがあるので、コピーしておきましょう。

sudocp/usr/local/turnserver/etc/turnuserdb.conf.default/usr/local/turnserver/etc/turnuserdb.confsudo cp /usr/local/turnserver/etc/turnuserdb.conf.default /usr/local/turnserver/etc/turnuserdb.conf sudo turnadmin -a -u sugimoto -r webrtc -p <パスワード> <password入力> </password入力>`

turnuserdb.confを見てみると、ユーザが追加されているのがわかります。なお、-aは追加オプション、-uはユーザ名、-rはrealm指定(turnserver.confのrealmと同じものを指定する)、-pはパスワードです。

$ less /usr/local/turnserver/etc/turnuserdb.conf

# 一番最後の方に追記されている
sugimoto:XXXXXXXXXXXXXXXXXXXXXXXXXX

これで認証ユーザが作成できたので、Peer接続の際にcredientialを指定することで認証することができます。

var peer = new webtitRTCPeerConnection(
	{ "iceServers": [
	    {
	      "url": "stun:ec2のpublic ip:3478"undefined
	       "username": "sugimoto"undefined
	       "crediential": "<パスワード>"
	    }
	]}
);

という感じですかね。(なぜかユーザを作成しても認証無しで通ってしまった。なんでだろう?ブラウザ側が認証に対応してない?)2014/04/01追記:username-crediential認証はTURNサーバの場合のみ有効なようです。なのでSTUNでは意味が無いです><

最後に

これで独自のPeer接続とSTUNサーバの準備ができたので、何かやろうかな、という感じです。

なお、stun-onlyとしてTURNサーバを使わなかったのは、前回のエントリにもある通り、TURNサーバはPeer接続の全てを中継してしまうので、万が一使った時のAWSの請求が怖かったという理由で…

まだまだ未検証な部分が多いですが、とりあえずの備忘録のまとめとして書きました。 詳細が理解できたら修正・追記すると思います。日本語の情報がほぼ皆無だったので、どなたかの参考なれば幸いです。

現場からは以上です。