簡単に映像を共有できる VDO.Ninja をセルフホスティングしてみる
VDO.Ninja はパソコンの画面やスマホのカメラの映像、音声を共有する Web サービスです。日本語の記事を検索してみると VTuber のコラボ配信で使われているみたいですね。
VDO.Ninja を使う際は特別なアプリをインストールしなくても構いません。利用側は Web ブラウザのみで完結します。また、配信ディレクター用のコントロールルーム機能が結構豊富なので、上手く使えば面白いライブ配信を行うことも可能です。
セルフホスティングの動機
VDO.Ninja をただ使うだけであれば https://vdo.ninja/ にアクセスすればいいのですが、VDO.Ninja は使いたいが映像の中身が漏れるのが心配という人もいると思います。
VDO.Ninja は映像伝送技術として WebRTC を使用しています。WebRTC は映像信号をピアツーピアで伝送し、内容は暗号化しているため盗聴は困難です。また伝送先の相手が正しいか否かは署名で確認します。
ただしセキュリティ上、TURN サーバとシグナリングサーバの存在はよく考える必要があります。
「TURN サーバ」はネットワーク構成上ピアツーピアで伝送が行えない時に映像信号を中継する役割を持ちます。映像信号の暗号化は行われているのですが、TURN サーバにアクセスが集中するとネットワークトラフィックが膨大になり、映像のやり取りが行えなくなる可能性があります。VDO.Ninja では標準の TURN サーバを提供しておりこれを使うことが可能です。ですが利用度合いによってはこれが使えなくなるかもしれません。
「シグナリングサーバ」は接続情報を交換するための役割を持ちます。通信経路上はこの情報も暗号化されているのですが https://vdo.ninja/ の運営者はこの情報を見ようと思えば見ることができます。もし接続情報を知られれば映像の中身を盗み見られる可能性があります。
https://vdo.ninja/ のリソースや運営を信用する場合は、これをそのまま使えば構いません。ただしセキュリティの観点から、より強化したい場合はセルフホスティングの動機が生まれます。
気楽に使えるから VDO.Ninja を使うのに、わざわざセルフホスティングするくらいなら他のサービスを使う、ということも考えられますが、今回はそこをあえてセルフホスティングしようという体で先に進めたいと思います。
必要なもの
VDO.Ninja をセルフホスティングするには主に以下のものが必要になります。
サーバ|オンプレミスサーバやクラウド仮想マシンなどを用意してください。最低1台でメモリは 2 GB 程度で十分動作します。
ドメイン|全て組織内ネットワークで完結する場合は内部 DNS でも構いません。そうでない場合はドメインを購入してください。
ソフトウェアの一覧
VDO.Ninja をセルフホスティングする上で使用するソフトウェアの一覧は以下の通りです。
Ubuntu|Docker が動くなら他の OS でも OK です。
Docker|Nginx・Node.js・coturn を動かすためのコンテナプラットフォームです。Docker が無くても OK ですが、取り回しがよくなるので使うことをおすすめします。
Nginx|VDO.Ninja のコンテンツをホスティングします。
VDO.Ninja|全て静的コンテンツですので Nginx からアクセスできるようにするだけです。
Node.js|VDO.Ninja 用シグナリングサーバを動かします。
VDO.Ninja シグナリングサーバ|WebRTC の接続情報をやり取りするサーバプログラムです。
coturn|TURN サーバとして使用します。これはオプションです。ネットワーク構成的にどうしても必要な場合に使用します。STUN サーバとしても動作しますが、今回は TURN サーバのみ動かします。
certbot|証明書の取得に使用します。自己署名証明書を使う場合や、証明書を購入する場合は不要です。
セルフホスティングのパターン
VDO.Ninja をセルフホスティングする上で、どの部品をセルフホスティングするか選ぶことができます。具体的には以下の通りです。
VDO.Ninja Web ページ|基本的にはセルフホスティングします。ただし 、TURN サーバだけ自前で用意したものを使う場合は https://vdo.ninja/ を使い TURN サーバを指定するという選択肢もありです。
VDO.Ninja シグナリングサーバ|接続情報を他の組織に知られたくない場合はセルフホスティングします。ただし、VDO.Ninja 公式からはサンプルの実装(https://github.com/steveseguin/websocket_server)のみが公開されています。
STUN サーバ|ピアツーピア用に自分のグローバル IP アドレスを知るためのサーバです。セキュリティ的な重要度は他のものと比べて低いです。STUN サーバの可用性がどうしても気になる場合はセルフホスティングしてください。
TURN サーバ|ネットワーク負荷の集中の影響で通信が遅延したり切断したりということを避けたい場合はセルフホスティングします。
今回は STUN サーバ以外をセルフホスティングする方法を簡単に説明します。STUN サーバは今回は Google のパブリックサーバを使用します。
サーバ構成
今回は TURN サーバとそれ以外を分離する形態を取っています。小規模の利用であれば1台に押し込めても構いません。
サーバ1
Ubuntu
Docker
Nginx +certbot|リバースプロキシ用
Nginx
VDO.Ninja Web ページ
Node.js
VDO.Ninja シグナリングサーバ
Docker Compose
サーバ2
Ubuntu
Docker
Docker Compose
coturn
certbot
前提
サーバ、OS、Docker、Docker Compose はセットアップ済みという前提で進めます。
DNS
ここから先はセルフホスティング用に example.com というドメインを取得したものとして進めます。
DNS に対して以下のように設定してください。(A レコードでも CNAME レコードでも構いません。)
ninja.example.com →サーバ1
ninjaws.example.com → サーバ1
turn.example.com → サーバ2
ネットワークポート
以下のネットワークポートにアクセスできるようにしてください。
サーバ1
80/TCP|certbot で証明書を取得するための HTTP ポートです。
443/TCP|VDO.Ninja Web ページ、シグナリングサーバへアクセスするための HTTPS ポートです。
サーバ2
80/TCP|certbot で証明書を取得するための HTTP ポートです。
443/TCP|coturn の turns 用ポートです。
49160-49200/UDP|coturn の映像信号を伝送するポートです。ピア数が多い場合はこの範囲を広げます。
VDO.Ninja Web ページをセットアップ
VDO.Ninja の本体ともいえる Web ページをホスティングするのは簡単です。ファイルの全てが静的コンテンツであり、単純なホスティングで完了するからです。
なお、公式のインストールガイドではファイルの拡張子を隠すことを推奨しています。
ということで VDO.Ninja 用のコンテナイメージを作成しました。
次に設定です。
一般的なコンテナであれば動かす際に、環境変数、コマンドライン引数、設定ファイルなどで設定を行います。しかし VDO.Ninja は HTML ファイルを直接編集して設定する方式になっています。そのためコンテナを動かす際に編集した HTML ファイルをボリュームマウントします。
編集する HTML ファイルは /var/www/html/vdo.ninja/index.html です。コンテナ内からこのファイルを取り出すか、VDO.Ninja 本体のリポジトリからファイル(index.html)を入手してください。
今回は以下の箇所を設定します。これはシグナリングサーバの接続設定です。
// session.wss = "wss://wss.yourdomainhere.com:443";
// session.customWSS = true;
この設定のコメントを外して以下のように書き換えてください。
session.wss = "wss://websocket.example.com:443";
session.customWSS = true;
書き換えたファイルをボリュームマウントすれば OK です。後ほど Docker Compose の定義ファイルをサンプルとしてお見せします。
なお、他の設定項目としてシグナリングサーバ以外にも STUN サーバ、TURN サーバ、その他配信のデフォルト設定が挙げられます。これらも index.html ファイルに記述します。今回はデフォルトのままとします。
VDO.Ninja シグナリングサーバをセットアップ
ピアツーピアの接続情報を交換するために、シグナリングサーバをセットアップします。VDO.Ninja からシグナリングサーバのサンプルの実装が公開されています。このサンプルは WebSocket を用いて接続情報を交換します。
「サンプル」とあるように、用途によってはこの実装だけでは不十分です。このサンプルは、シグナリングサーバに接続した全てのピアに対して接続情報を転送します。VDO.Ninja はルーム機能を搭載しており、同じルームに入っているピアの間で映像をやり取りするようになっています。しかし、サンプルのシグナリングサーバを使用するとルームに関係なく無差別に接続情報を送るため、ユースケースによっては機密性の点で問題があります。
ということで、ルーム機能に対応したシグナリングサーバを制作したのでこちらも紹介します。コンテナイメージも合わせて作成しています。
こちらを Docker 上にデプロイすれば、同じルームだけに接続情報が転送されるシグナリングサーバとして使うことができます。
VDO.Ninja リバースプロキシをセットアップ
WebRTC や WebSocket を使う場合は HTTPS が基本です。そのためリバースプロキシで TLS を処理し、背後にある Web ページやシグナリングサーバへリクエストを転送します。
今回は Let’s Encrypt を使用して証明書を取得する方式を採用しています。簡単に証明書を取得するために、以下のコンテナイメージを使用しました。
このコンテナイメージを使えば、簡単な設定だけで証明書の取得、Nginx への証明書設定を行うことができます。
では、Web ページ、シグナリングサーバ、リバースプロキシをまとめて Docker Compose で配備できるようにした docker-compose.yml のサンプルを紹介します。
admin@example.com の箇所には普段使用しているメールアドレスを設定してください。
version: "3.3"
services:
nginx:
image: "jonasal/nginx-certbot:2.4.1-nginx1.21.3-alpine"
environment:
- "CERTBOT_EMAIL=admin@example.com"
volumes:
- type: bind
source: ./ninja.conf
target: /etc/nginx/user_conf.d/ninja.conf
read_only: true
- type: bind
source: ./letsencrypt
target: /etc/letsencrypt
ports:
- "80:80"
- "443:443"
restart: unless-stopped
ninja:
image: "ghcr.io/tdc-yamada-ya/vdo.ninja:latest"
volumes:
- type: bind
source: ./index.html
target: /var/www/html/vdo.ninja/index.html
read_only: true
ninjaws:
image: "ghcr.io/tdc-yamada-ya/vdo.ninja-websocket-server:latest"
またリバースプロキシ設定として ./ninja.conf を配置します。内容は以下の通りです。example.com の箇所は適宜修正してください。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name ninja.example.com;
ssl_certificate /etc/letsencrypt/live/ninja.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ninja.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/ninja.example.com/chain.pem;
ssl_dhparam /etc/letsencrypt/dhparams/dhparam.pem;
client_max_body_size 10M;
server_tokens off;
location / {
proxy_pass http://ninja:80;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name ninjaws.example.com;
ssl_certificate /etc/letsencrypt/live/ninjaws.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ninjaws.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/ninjaws.example.com/chain.pem;
ssl_dhparam /etc/letsencrypt/dhparams/dhparam.pem;
client_max_body_size 10M;
server_tokens off;
location / {
proxy_pass http://ninjaws:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
設定出来たら docker-compose up -d でサービスを起動してください。
coturn のセットアップ
WebRTC は STUN サーバを用いた接続情報の交換により多くの場合でピアツーピア接続ができるのですが、ネットワーク機器の構成によってはそれだけでは接続できない場合があります。TURN サーバを使うと、ピアツーピア接続ではなく、映像信号を中継する方式になるため、接続成功率が向上します。例えばリモートワーク下のようにどんなネットワーク構成の端末から接続されるかわからない場合は TURN サーバをセットアップしておくことをおすすめします。
今回は TURN サーバのプロダクトとして coturn を使用します。coturn は STUN/TURN に関する様々な標準を実装しており、簡単な認証機能も付属しています。
coturn はコンテナイメージが公開されているのでこれを使用します。また接続の際は TLS による暗号化を行うため certbot によって証明書を取得します。
まずは証明書を取得します。証明書を取得する際のコマンドはこちらです。ドメイン名の部分は修正してください。
sudo docker run \
-it \
-p 80:80 \
-v $(pwd)/letsencrypt:/etc/letsencrypt \
certbot/certbot \
certonly \
--standalone \
-d turn.example.com
certbot のウィザードの進め方については省略します。
次に coturn のコンテナイメージをデプロイします。こちらも Docker Compose でデプロイしますので、まずは docker-compose.yml のサンプルを紹介します。
version: "3.3"
services:
coturn:
image: coturn/coturn:4.5.2-r8-alpine
user: root:root
ports:
- "3478:3478/tcp"
- "3478:3478/udp"
- "443:443/tcp"
- "49160-49200:49160-49200/udp"
volumes:
- type: bind
source: ./turnserver.conf
target: /etc/coturn/turnserver.conf
read_only: true
- type: bind
source: ./letsencrypt
target: /etc/letsencrypt
read_only: true
- type: volume
source: coturn
target: /var/lib/coturn
restart: "no"
volumes:
coturn:
続いて設定ファイル ./turnserver.conf の中身はこちらです。ドメイン名の部分は環境に合わせて修正してください。user=foo:bar の行は TURN サーバの認証情報ですので、推測困難な文字列を指定してください。
alt-listening-port=0
fingerprint
listening-port=3478
tls-listening-port=443
log-file=stdout
lt-cred-mech
max-port=49200
min-port=49160
no-cli
no-multicast-peers
no-stun
realm=turn.example.com
server-name=turn.example.com
stale-nonce=600
user=foo:bar
cert=/etc/letsencrypt/live/turn.example.com/fullchain.pem
pkey=/etc/letsencrypt/live/turn.example.com/privkey.pem
設定出来たら docker-compose up -d でサービスを起動してください。
VDO.Ninja で TURN サーバを使用する際は以下のドキュメントを参照してください。URLのパラメータに TURN サーバの接続情報を記述します。
今回のケースで言うと https://ninja.example.com/?turn=foo;bar;turn.example.com にアクセスします。
まとめ
ということで、セットアップ手順は以上です。この記事に載せた内容は全て VDO.Ninja のドキュメントから読み取ることができるのですが、一部情報が散らばっていたので一つの記事にまとめることにしました。
なお、セルフホスティングの動機はセキュリティのためと言いました。しかしセルフホスティングしたからと言ってセキュリティが向上するとも限りません。むしろ設定内容によっては危険になることもあります。機密性の観点においては、IP アドレス制限を加える、サーバを起動する時間を制限する等の対処を取れば、よりセキュアになりますので是非ご検討ください。