nginx-rtmp-module でストリームキーに制限を加える方法 (Docker)

#はじめに

[nginx-rtmp-module](https://github.com/arut/nginx-rtmp-module) で何も考えずに適当に配信サーバーを立てると、
配信サーバーの URL さえ分かれば適当なストリームキーで誰でも配信可能な状態になります。
そこで今回はストリームキーに制限を加えるというやりかたで、配信に制限を加えてみました。

#ストリームキー制限のための nginx.conf の内容

ストリームキーに制限を加えるための nginx-rtmp-module 用の nginx.conf は下記になります。
重要なのは application ディレクティブ内の `on_publish` と `notify_method` の部分です。

```nginx:nginx-rtmp/nginx.conf
user nginx;
worker_processes auto;
rtmp_auto_push on;

rtmp {
    server {
        listen 1935;
        listen [::]:1935 ipv6only=on;

        application live {
            live on;
            record off;
            wait_video on;

            # 配信が開始されたときに設定した URL に通知を送る
            # 通知を送る際の HTTP METHOD は post を使用 (geｔ か post)
            # 通知先には RTMP 配信に関わる情報が送信される (詳細は後述)
            on_publish http://auth-server:3000/;
            notify_method post;
        }
    }
}
```

nginx-rtmp-module には RTMP の配信状況に応じて通知を webhook 送信する仕組みが存在します。([nginx-rtmp-module の通知一覧](https://github.com/arut/nginx-rtmp-module/wiki/Directives#notify))
その中で `on_publish` は配信が開始されたときに発火する webhook となっています。
webhook 送信を行う際にどの HTTP METHOD を使用するかは `notify_method` で設定します。
`notify_method` には get か post を選択できます。get であれば URL クエリに RTMP 配信に関わる情報が含まれます。
今回は post を選択したので body に配信に関わる情報が含まれています。

次に webhook 送信先の auth-server の中身を見ていきます。

#認証を行うための Express サーバーの内容

auth-server は Express で作成しました。サーバー内でやっている事は非常にシンプルです。
ルートパスで webhook の通知を受け付けてストリームキーを見て、条件にマッチするものであれば 200、条件にマッチしないものであれば 404 の HTTP ステータスを返却するだけになります。

```javascript:auth-server/routes/index.js
var express = require('express');
var router = express.Router();

router.post('/', function(req, res, next) {
  // nginx-rtmp-module から通知される情報に含まれるストリームキーを取得して、
  // 条件判定で配信を許可するかどうかを HTTP ステータスコードで返却します
  // 今回はストリームキーには streamkey だけを許可するようにしました。
  const name = req.body.name;
  if(name === 'streamkey')
    return res.status(200).send()

  res.status(404).send()
});

module.exports = router;
```

上記を応用すれば、例えばデータベース内に保存された各ユーザに紐付いたストリームキーのみを許可する等も実装可能です。

# nginx-rtmp-module + 認証サーバーの動作確認

今まで説明したものを実際に動かしてみたいと思います。
今回は手っ取り早く検証を行うため Docker と docker-compose を使って環境構築していきます。

環境が無い方は、Docker は各自使用 OS に応じて [Docker Desktop For Windows](https://hub.docker.com/editions/community/docker-ce-desktop-windows) か [Docker Desktop For Mac](https://hub.docker.com/editions/community/docker-ce-desktop-mac) でインストールを行っていただき、
docker-compose は Windows であれば @chr さんが書かれている [こちらの記事](https://qiita.com/chr/items/184b6af37d105bdad145) を参考にしていただき、Mac であれば brew で `brew install docker-compose` コマンドでインストール出来ます。

## nginx-rtmp-module + 認証サーバーのセットアップ

ディレクトリ構成は下記のようになりました。

```
.
├── auth-server
│   ├── Dockerfile
│   ├── app.js
│   ├── bin
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   ├── routes
│   └── views
├── docker-compose.yml
└── nginx-rtmp
    ├── Dockerfile
    └── nginx.conf
```

**上記のディレクトリ構成に従ってセットアップ作業を進めていきます。**

まずは auth-server を用意します。

```shell
mkdir auth-server
cd auth-server
npm init
npm install --save-dev express-generator@4
npx express .
```

これで auth-server フォルダに Express プロジェクトの雛形が作成出来たと思うので、
routes/index.js ファイルを `認証を行うための Express サーバーの内容` で説明した js ファイルで上書きします。

auth-server の Dockerfile は下記のようになりました。

```dockerfile:auth-server/Dockerfile
FROM node:11.14.0-alpine

RUN mkdir app/
ADD package.json app/
ADD package-lock.json app/

WORKDIR /app
RUN npm install

ADD app.js ./
ADD routes/ routes/
ADD bin/ bin/

EXPOSE 3000

CMD npm run start
```

次に nginx-rtmp-module を用意します。こちらは既に nginx-rtmp-module の [Docker のイメージ](https://hub.docker.com/r/tiangolo/nginx-rtmp/) を公開してくれている方がおりますので、それを利用させていただきます。nginx.conf は `ストリームキー制限のための nginx.conf の内容` で説明したものを利用するため COPY で上書きします。
Dockerfile は下記のようになりました。

```dockerfile:nginx-rtmp/Dockerfile
FROM tiangolo/nginx-rtmp

COPY nginx.conf /etc/nginx/nginx.conf

CMD nginx -g "daemon off;"
```

最後に auth-server と nginx-rtmp を協調して動かすための docker-compose.yml を作成します。nginx-rtmp から auth-server を参照したいため、links を設定しております。また外部から配信実験を行うため nginx-rtmp の 1935ポートを外部から参照出来るようにしております。

```yml:docker-compose.yml
version: '3.7'
services:
  auth-server:
    build: auth-server/.
    ports:
      - "3000"
  nginx-rtmp:
    build: nginx-rtmp/.
    ports:
      - "1935:1935"
    links:
      - auth-server
```

ここまできたら docker-compose でイメージのビルド & ランが可能になります。
下記で実際に nginx-rtmp + auth-server を動かしてみて動作確認していきます。

```shell
# docker-compose.yml と同じ階層に cd しておく
docker-compose build .
docker-compose up
```

正常に実行できたら [OBS](https://obsproject.com/ja) で RTMP 配信してみます。

## OBS で許可されたストリームキーでのみ配信可能になったか確認する

まずは正常に配信が可能なストリームキー `streamkey` で配信の確認を行います。

OBS の設定は下記の通りです。
<img width="970" alt="スクリーンショット 2019-06-15 12.51.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86070/b683dcd8-b4fd-869a-f8a7-ec3f400ac58f.png">

これで配信を開始して、配信が正常に行えているか [VLC](https://www.videolan.org/vlc/index.ja.html) 等の RTMP が再生可能なプレイヤーで確認してみます。今回は VLC で配信中の RTMP URL を設定して開いてみます。

<img width="594" alt="スクリーンショット 2019-06-15 13.17.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86070/958ba894-5f9c-d449-cac2-5ef9beffe2d2.png">

上記設定を行った後、`開く` をクリックすると再生が行われること確認できました↓

<img width="1269" alt="スクリーンショット 2019-06-15 13.18.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86070/206c6612-50ef-4015-c35b-e35c445291ea.png">

次に配信の失敗が想定されるであろうパターンで検証を行います。

OBS でストリームキーを `livesitai` に変更します。
<img width="972" alt="スクリーンショット 2019-06-15 12.47.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86070/9983f7c2-06be-05c1-a30b-e606dcdd3967.png">

この状態で配信を行おうとしたところ、配信が失敗しました↓
<img width="1069" alt="スクリーンショット 2019-06-15 13.31.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86070/6d49e37b-1c0a-cb42-a16e-eacf9527a0ea.png">

#おわりに

nginx-rtmp-module の設定を駆使すれば、様々な用途に使用可能な RTMP 配信サーバとして利用可能です。その中の一例としてストリームキーに制限を加えた状態での配信を行うための方法をご紹介しました。この記事が誰かのお役に立てれば幸いです。

#参考リンク

https://github.com/arut/nginx-rtmp-module/wiki/Directives
https://hub.docker.com/r/tiangolo/nginx-rtmp/