T-CREATOR

WebRTC で自前 TURN サーバー構築:coturn を TLS/443 で堅牢運用する方法

WebRTC で自前 TURN サーバー構築:coturn を TLS/443 で堅牢運用する方法

WebRTC アプリケーションを本番環境で運用する際、企業のファイアウォールや NAT 越えの問題に直面したことはありませんか?そんなときに欠かせないのが TURN サーバーです。今回は、オープンソースのcoturnを使って、TLS 暗号化とポート 443 を活用した堅牢な TURN サーバーの構築方法をご紹介します。

セキュアで信頼性の高い通信環境を実現するために必要な設定を、段階的に解説していきますね。

背景

WebRTC 通信における NAT 越えの課題

WebRTC は、ブラウザ間でリアルタイムの音声・映像通信を可能にする技術です。しかし、実際の通信環境では多くの場合、ルーターやファイアウォールによって直接接続が阻まれてしまいます。

以下の図は、WebRTC 通信における接続確立の流れを示しています。

mermaidflowchart TB
  peerA["Peer A<br/>(クライアント)"] -->|"1. STUN で<br/>自身の IP を確認"| stun["STUN サーバー"]
  peerB["Peer B<br/>(クライアント)"] -->|"1. STUN で<br/>自身の IP を確認"| stun

  peerA -->|"2. 直接接続<br/>試行"| peerB
  peerB -->|"2. 直接接続<br/>試行"| peerA

  peerA -.->|"3. 直接接続失敗時<br/>TURN 経由"| turn["TURN サーバー"]
  turn -.->|"メディア中継"| peerB

  style turn fill:#ff9,stroke:#333,stroke-width:3px
  style stun fill:#9cf,stroke:#333,stroke-width:2px

図で理解できる要点

  • STUN サーバーは自身のグローバル IP アドレス確認に使用されます
  • 直接接続が失敗した場合に、TURN サーバーが中継役として機能します
  • TURN サーバーは通信の最後の砦として重要な役割を担います

TURN サーバーの役割

STUN(Session Traversal Utilities for NAT)は、クライアントが自身のグローバル IP アドレスを知るためのプロトコルです。多くの場合、これだけで P2P 接続が確立できるでしょう。

しかし、企業ネットワークなど厳格なファイアウォール環境では、STUN だけでは不十分なケースがあります。そこで登場するのがTURN(Traversal Using Relays around NAT)です。

TURN サーバーは、クライアント間の通信を中継する役割を果たします。直接接続できない場合でも、TURN サーバーを経由することで確実に通信を確立できるのです。

なぜ自前で TURN サーバーを構築するのか

パブリックな TURN サーバーを利用する方法もありますが、以下の理由から自前構築が推奨されます。

#理由説明
1セキュリティ自社の通信データを外部サーバーに依存させないため
2コスト管理通信量に応じた従量課金を避け、固定コストで運用可能
3カスタマイズ性認証方式や帯域制限など、自由に設定を調整できる
4可用性外部サービスの障害に左右されない安定運用が可能
5コンプライアンスデータの所在地や処理方法を完全に管理できる

特にエンタープライズ環境では、セキュリティとコンプライアンスの観点から自前構築が必須となるケースが多いですね。

課題

一般的な TURN サーバー構築の問題点

TURN サーバーを構築する際、以下のような課題に直面することがあります。

mermaidflowchart TD
  start["TURN サーバー構築"] --> prob1["課題1: 暗号化されていない<br/>通信のリスク"]
  start --> prob2["課題2: ファイアウォールで<br/>ブロックされるポート"]
  start --> prob3["課題3: 証明書管理の<br/>複雑さ"]
  start --> prob4["課題4: 認証設定の<br/>セキュリティ不足"]

  prob1 --> risk1["盗聴・改ざんの危険性"]
  prob2 --> risk2["企業ネットワークで<br/>接続不可"]
  prob3 --> risk3["証明書期限切れによる<br/>サービス停止"]
  prob4 --> risk4["不正利用・<br/>帯域の浪費"]

  style start fill:#9cf,stroke:#333,stroke-width:2px
  style risk1 fill:#f99,stroke:#333,stroke-width:2px
  style risk2 fill:#f99,stroke:#333,stroke-width:2px
  style risk3 fill:#f99,stroke:#333,stroke-width:2px
  style risk4 fill:#f99,stroke:#333,stroke-width:2px

図で理解できる要点

  • 構築時の課題が複数のリスクに直結します
  • 各課題に対する適切な対策が必要です
  • 特にセキュリティ面での考慮が重要となります

課題 1:暗号化されていない通信

デフォルト設定のまま TURN サーバーを構築すると、通信が暗号化されません。音声や映像といった機密性の高いデータが平文でネットワークを流れてしまうのは、セキュリティ上大きな問題です。

中間者攻撃(MITM 攻撃)によって、通信内容が盗聴されたり改ざんされたりする可能性があります。

課題 2:ファイアウォールでブロックされるポート

TURN サーバーは通常、UDP/TCP 3478 ポートを使用します。しかし、企業のファイアウォールでは、HTTP(80)と HTTPS(443)以外のポートがブロックされているケースが多いでしょう。

このため、せっかく TURN サーバーを構築しても、クライアントから接続できないという事態が発生してしまいます。

課題 3:SSL/TLS 証明書の管理

TLS 通信を実現するには、有効な SSL/TLS 証明書が必要です。証明書の取得、更新、管理を適切に行わないと、以下のような問題が発生します。

  • 証明書期限切れによるサービス停止
  • 自己署名証明書による警告表示
  • 証明書チェーンの不備による接続エラー

Let's Encrypt などの無料証明書を使う場合、90 日ごとの自動更新の仕組みを構築する必要がありますね。

課題 4:認証とセキュリティ設定

TURN サーバーを公開する際、適切な認証設定を行わないと、誰でもサーバーを利用できてしまいます。これにより以下のリスクが生じるでしょう。

#リスク影響
1帯域の不正利用サーバーリソースの枯渇とコスト増大
2DDoS 攻撃の踏み台化第三者への攻撃に加担してしまう
3法的責任不正利用による法的トラブルの発生

これらの課題を解決するために、次のセクションでは具体的な解決策を見ていきます。

解決策

coturn を使った TLS/443 での堅牢な構築方針

これらの課題に対して、以下の方針で TURN サーバーを構築します。

mermaidflowchart LR
  solution1["TLS 暗号化<br/>の実装"] --> secure["セキュアな<br/>通信環境"]
  solution2["ポート 443<br/>の利用"] --> secure
  solution3["Let's Encrypt<br/>証明書"] --> secure
  solution4["認証機能<br/>の実装"] --> secure

  secure --> result["堅牢な<br/>TURN サーバー"]

  style result fill:#9f9,stroke:#333,stroke-width:3px
  style secure fill:#ff9,stroke:#333,stroke-width:2px

図で理解できる要点

  • 4 つの主要な解決策を組み合わせます
  • すべての要素がセキュアな通信環境に貢献します
  • 最終的に堅牢な TURN サーバーが実現されます

解決策 1:coturn のインストールと基本設定

coturnは、オープンソースの TURN/STUN サーバー実装です。高性能で設定の柔軟性が高く、商用環境でも広く使われています。

まず、Ubuntu/Debian ベースのシステムに coturn をインストールしましょう。

パッケージのインストール

bash# システムパッケージの更新
sudo apt update && sudo apt upgrade -y

次に、coturn パッケージをインストールします。

bash# coturnのインストール
sudo apt install coturn -y

インストール後、coturn サービスを有効化する必要があります。

bash# coturnサービスの有効化
sudo systemctl enable coturn

解決策 2:TLS 証明書の取得(Let's Encrypt)

Let's Encrypt を使って、無料の SSL/TLS 証明書を取得します。Certbot ツールを使うことで、証明書の取得と自動更新が簡単に実現できますよ。

Certbot のインストール

bash# Certbotとnginxプラグインのインストール
sudo apt install certbot python3-certbot-nginx -y

証明書の取得

ドメインが turn.example.com の場合、以下のコマンドで証明書を取得します。

bash# 証明書の取得(standaloneモード)
sudo certbot certonly --standalone \
  -d turn.example.com \
  --agree-tos \
  --email your-email@example.com \
  --non-interactive

注意点

  • turn.example.com は実際のドメイン名に置き換えてください
  • DNS レコードが正しく設定されている必要があります
  • ポート 80 が一時的に利用可能である必要があります

証明書は ​/​etc​/​letsencrypt​/​live​/​turn.example.com​/​ に保存されます。

証明書の自動更新設定

Let's Encrypt の証明書は 90 日で期限切れになります。Certbot は自動更新のための cron ジョブを設定してくれますが、更新後に coturn を再起動する必要がありますね。

bash# 更新後のフックスクリプトを作成
sudo nano /etc/letsencrypt/renewal-hooks/post/coturn-restart.sh

以下の内容を記述します。

bash#!/bin/bash
# coturnサービスを再起動して新しい証明書を読み込む
systemctl restart coturn

スクリプトに実行権限を付与します。

bash# 実行権限の付与
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/coturn-restart.sh

解決策 3:coturn の設定ファイル編集

coturn の設定ファイルは ​/​etc​/​turnserver.conf です。このファイルを編集して、TLS/443 での運用に必要な設定を行います。

設定ファイルのバックアップ

まず、オリジナルの設定ファイルをバックアップしておきましょう。

bash# 設定ファイルのバックアップ
sudo cp /etc/turnserver.conf /etc/turnserver.conf.backup

基本設定の記述

設定ファイルを開きます。

bash# 設定ファイルの編集
sudo nano /etc/turnserver.conf

以下は、TLS/443 で運用するための基本設定です。

conf# リスニングポートの設定
listening-port=3478
# TLS用のリスニングポート(443を使用)
tls-listening-port=443

# 外部IPアドレスの設定(サーバーのグローバルIP)
external-ip=203.0.113.10

# リレーIPの範囲指定(サーバーのグローバルIP)
relay-ip=203.0.113.10

設定の説明

  • listening-port: 通常の TURN 通信用ポート(デフォルト 3478)
  • tls-listening-port: TLS 暗号化通信用ポート(443 に設定)
  • external-ip: サーバーのグローバル IP アドレス
  • relay-ip: リレー通信で使用する IP アドレス

TLS 証明書の設定

Let's Encrypt で取得した証明書のパスを指定します。

conf# TLS証明書の設定
cert=/etc/letsencrypt/live/turn.example.com/fullchain.pem
pkey=/etc/letsencrypt/live/turn.example.com/privkey.pem

# 暗号スイートの設定(セキュアな暗号のみ使用)
cipher-list="HIGH:!aNULL:!MD5:!RC4"

# TLSバージョンの制限(TLS 1.2以上のみ許可)
no-tlsv1
no-tlsv1_1

この設定により、古い脆弱な暗号化方式を排除し、セキュアな通信のみを許可できます。

認証設定

静的な認証情報を使う場合の設定です。

conf# 長期認証の有効化
lt-cred-mech

# ユーザー認証情報の設定(username:password形式)
user=myuser:mypassword

# Realmの設定(ドメイン名を指定)
realm=turn.example.com

セキュリティのポイント

  • myusermypassword は強力なものに変更してください
  • パスワードは最低でも 16 文字以上の複雑なものを使用しましょう
  • 定期的にパスワードを変更することを推奨します

その他の重要な設定

conf# ログレベルの設定
verbose

# ログファイルの出力先
log-file=/var/log/turnserver.log

# 最大同時接続数の制限
max-bps=1000000

# セッションタイムアウト(秒)
stale-nonce=600

# フィンガープリント検証の有効化
fingerprint

# WebRTC対応のための設定
no-multicast-peers
no-cli

設定の説明

  • verbose: 詳細なログを出力(トラブルシューティングに有効)
  • max-bps: 最大帯域幅の制限(DoS 対策)
  • fingerprint: STUN メッセージの改ざん検出
  • no-cli: CLI アクセスを無効化(セキュリティ向上)

解決策 4:ファイアウォール設定

coturn が使用するポートをファイアウォールで許可する必要があります。UFW(Uncomplicated Firewall)を使用する場合の設定例です。

必要なポートの開放

bash# HTTPS(証明書取得用)
sudo ufw allow 80/tcp

# TLS TURN(443ポート)
sudo ufw allow 443/tcp
sudo ufw allow 443/udp

UDP と TCP の両方を開放することで、より広範なクライアント環境に対応できますよ。

通常の TURN ポートの開放(オプション)

443 ポートだけでなく、標準ポートも開放しておくと、より柔軟な運用が可能です。

bash# 標準TURNポート
sudo ufw allow 3478/tcp
sudo ufw allow 3478/udp

リレー用ポート範囲の開放

coturn はリレー通信に 49152-65535 の範囲のポートを使用します。

bash# リレー用ポート範囲の開放
sudo ufw allow 49152:65535/tcp
sudo ufw allow 49152:65535/udp

ファイアウォール設定を有効化します。

bash# UFWの有効化
sudo ufw enable

# 設定の確認
sudo ufw status verbose

解決策 5:coturn サービスの起動と確認

設定が完了したら、coturn サービスを起動します。

サービスの起動

bash# coturnサービスの起動
sudo systemctl start coturn

# サービスステータスの確認
sudo systemctl status coturn

サービスが正常に起動していれば、active (running) と表示されるはずです。

ログの確認

ログファイルを確認して、エラーがないかチェックしましょう。

bash# ログファイルの確認(最新100行)
sudo tail -n 100 /var/log/turnserver.log

確認ポイント

  • 証明書の読み込みが成功しているか
  • ポートのリスニングが開始されているか
  • エラーメッセージが出ていないか

接続テスト

TURN サーバーが正しく動作しているか、オンラインツールでテストできます。

Trickle ICE にアクセスし、以下の情報を入力してください。

javascript{
  "urls": "turns:turn.example.com:443?transport=tcp",
  "username": "myuser",
  "credential": "mypassword"
}

テスト結果の確認

  • relay タイプの候補が表示されれば成功です
  • srflx は STUN による候補です
  • host はローカル候補です

具体例

クライアント側の実装例(JavaScript)

実際に WebRTC アプリケーションから、構築した TURN サーバーを利用する例を見ていきましょう。

ICE Servers の設定

WebRTC 接続を確立する際、RTCPeerConnection に TURN サーバーの情報を渡します。

javascript// TURNサーバーの設定情報
const iceServers = [
  {
    // STUNサーバー(Googleの公開STUN)
    urls: 'stun:stun.l.google.com:19302',
  },
  {
    // 自前TURNサーバー(TLS/443)
    urls: 'turns:turn.example.com:443?transport=tcp',
    username: 'myuser',
    credential: 'mypassword',
  },
];

設定のポイント

  • turns: プロトコルで TLS 暗号化を指定します
  • transport=tcp で TCP 接続を明示的に指定します
  • 認証情報は環境変数などで管理することを推奨します

RTCPeerConnection の生成

ICE Servers を使って PeerConnection を作成します。

javascript// RTCPeerConnectionの設定オブジェクト
const configuration = {
  iceServers: iceServers,
  iceTransportPolicy: 'all', // すべての候補を試行
  bundlePolicy: 'max-bundle',
  rtcpMuxPolicy: 'require',
};

実際に PeerConnection オブジェクトを生成します。

javascript// PeerConnectionの生成
const peerConnection = new RTCPeerConnection(configuration);

console.log(
  'PeerConnection created with TURN server configuration'
);

ICE 候補の収集とイベント処理

ICE 候補が収集されたときのイベントハンドラーを設定します。

javascript// ICE候補が見つかったときの処理
peerConnection.onicecandidate = (event) => {
  if (event.candidate) {
    // 候補の種類をログ出力
    console.log(
      'ICE Candidate Type:',
      event.candidate.type
    );
    console.log(
      'ICE Candidate:',
      event.candidate.candidate
    );

    // シグナリングサーバーを通じて相手に送信
    sendToSignalingServer({
      type: 'ice-candidate',
      candidate: event.candidate,
    });
  } else {
    console.log('All ICE candidates have been gathered');
  }
};

候補タイプの説明

  • host: ローカルネットワーク内のアドレス
  • srflx: STUN で取得したグローバルアドレス
  • relay: TURN サーバー経由のアドレス(最も確実)

接続状態の監視

接続状態の変化を監視して、適切にハンドリングします。

javascript// ICE接続状態の監視
peerConnection.oniceconnectionstatechange = () => {
  const state = peerConnection.iceConnectionState;
  console.log('ICE Connection State:', state);

  switch (state) {
    case 'connected':
      console.log('Successfully connected via WebRTC');
      break;
    case 'disconnected':
      console.warn('Connection temporarily lost');
      break;
    case 'failed':
      console.error(
        'Connection failed, TURN server may be needed'
      );
      break;
    case 'closed':
      console.log('Connection closed');
      break;
  }
};

動的認証の実装(REST API 方式)

静的なユーザー名・パスワードではなく、動的に認証情報を生成する方法も見ていきましょう。この方式は、セキュリティをより高められます。

サーバー側の実装(Node.js + Express)

バックエンドで一時的な認証情報を生成する API を作成します。

javascriptconst express = require('express');
const crypto = require('crypto');
const app = express();

// TURNサーバーの秘密鍵(coturn設定と同じものを使用)
const TURN_SECRET = 'your-secret-key-here';
const TURN_SERVER = 'turn.example.com';

認証情報を生成するエンドポイントを実装します。

javascript// TURN認証情報を生成するエンドポイント
app.get('/api/turn-credentials', (req, res) => {
  // 有効期限(24時間後のUnixタイムスタンプ)
  const timestamp = Math.floor(Date.now() / 1000) + 86400;

  // ユーザー名(タイムスタンプを含む一時的なもの)
  const username = `${timestamp}:tempuser`;

  // HMACを使ってパスワードを生成
  const hmac = crypto.createHmac('sha1', TURN_SECRET);
  hmac.update(username);
  const password = hmac.digest('base64');

  // クライアントに返すレスポンス
  res.json({
    urls: `turns:${TURN_SERVER}:443?transport=tcp`,
    username: username,
    credential: password,
    ttl: 86400, // 有効期限(秒)
  });
});

動的認証のメリット

  • 認証情報が時間制限付きで自動的に無効化されます
  • ユーザーごとに異なる認証情報を発行できます
  • セキュリティリスクが大幅に低減されます

coturn での動的認証設定

coturn の設定ファイルに、動的認証用の秘密鍵を設定します。

conf# 静的認証を無効化(コメントアウト)
# user=myuser:mypassword

# 動的認証の有効化
use-auth-secret

# 秘密鍵の設定(サーバー側と同じものを使用)
static-auth-secret=your-secret-key-here

# Realmの設定
realm=turn.example.com

設定変更後、coturn を再起動します。

bash# coturnサービスの再起動
sudo systemctl restart coturn

クライアント側での利用

フロントエンドから動的認証情報を取得して使用します。

javascript// TURNサーバーの認証情報を取得する関数
async function getTurnCredentials() {
  try {
    const response = await fetch('/api/turn-credentials');
    const credentials = await response.json();

    return credentials;
  } catch (error) {
    console.error('Failed to get TURN credentials:', error);
    throw error;
  }
}

取得した認証情報で PeerConnection を作成します。

javascript// WebRTC接続の初期化
async function initializeWebRTC() {
  // 動的なTURN認証情報を取得
  const turnCredentials = await getTurnCredentials();

  // ICE Serversの設定
  const iceServers = [
    { urls: 'stun:stun.l.google.com:19302' },
    turnCredentials,
  ];

  // PeerConnectionの作成
  const peerConnection = new RTCPeerConnection({
    iceServers: iceServers,
  });

  console.log(
    'WebRTC initialized with dynamic TURN credentials'
  );

  return peerConnection;
}

Docker を使った構築例

Docker コンテナで coturn を運用する方法も見ていきましょう。コンテナ化することで、環境の再現性が高まり、デプロイが容易になります。

Dockerfile の作成

coturn の Docker イメージを作成するための Dockerfile です。

dockerfileFROM ubuntu:22.04

# パッケージのインストール
RUN apt-get update && \
    apt-get install -y coturn && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 設定ファイルとログディレクトリの準備
RUN mkdir -p /var/log

次に、coturn の起動コマンドを指定します。

dockerfile# ポートの公開
EXPOSE 3478 3478/udp
EXPOSE 443 443/udp
EXPOSE 49152-65535/udp

# エントリーポイント
CMD ["turnserver", "-c", "/etc/turnserver.conf", "-v"]

docker-compose.yml の作成

Docker Compose を使って、より簡単に管理できます。

yamlversion: '3.8'

services:
  coturn:
    build: .
    container_name: coturn-server
    restart: unless-stopped
    network_mode: host

証明書と設定ファイルをマウントします。

yamlvolumes:
  # 設定ファイル
  - ./turnserver.conf:/etc/turnserver.conf:ro
  # Let's Encrypt証明書
  - /etc/letsencrypt:/etc/letsencrypt:ro
  # ログファイル
  - ./logs:/var/log

環境変数を設定します。

yamlenvironment:
  - EXTERNAL_IP=203.0.113.10
  - REALM=turn.example.com

設定のポイント

  • network_mode: host でホストネットワークを使用します
  • 証明書は読み取り専用(:ro)でマウントします
  • ログは永続化のためにボリュームにマウントします

コンテナの起動

Docker Compose でコンテナを起動します。

bash# コンテナのビルドと起動
docker-compose up -d

# ログの確認
docker-compose logs -f coturn

コンテナのステータスを確認します。

bash# コンテナの状態確認
docker-compose ps

# coturnプロセスの確認
docker-compose exec coturn ps aux | grep turnserver

モニタリングとメトリクス収集

TURN サーバーの稼働状況を監視するための設定例です。

Prometheus エクスポーターの導入

coturn のメトリクスを Prometheus 形式で公開するエクスポーターを使用します。

bash# coturn-prometheus-exporterのインストール
git clone https://github.com/bolkedebruin/coturn_exporter.git
cd coturn_exporter

Python の依存関係をインストールします。

bash# 依存パッケージのインストール
pip3 install -r requirements.txt

エクスポーターを起動します。

bash# エクスポーターの起動
python3 coturn_exporter.py --turnserver-host localhost --turnserver-port 3478

メトリクスの確認

エクスポーターが公開するメトリクスを確認できます。

bash# メトリクスの取得
curl http://localhost:9641/metrics

取得できる主なメトリクス

  • アクティブなセッション数
  • 総転送バイト数
  • 接続エラー数
  • ピア接続数

これらのメトリクスを Grafana などで可視化することで、サーバーの健全性を継続的に監視できますよ。

まとめ

本記事では、WebRTC アプリケーションに欠かせない TURN サーバーを、coturn を使って TLS/443 で堅牢に構築する方法をご紹介しました。

企業のファイアウォール環境でも確実に動作させるために、以下のポイントを押さえることが重要です。

#ポイント効果
1TLS 暗号化の実装通信内容の盗聴・改ざんを防止
2ポート 443 の活用ファイアウォール環境での接続性向上
3Let's Encrypt 証明書無料で信頼性の高い証明書を自動更新
4動的認証の導入セキュリティリスクを大幅に低減
5適切なファイアウォール設定必要なポートのみを開放

特に、動的認証の実装は、セキュリティを大きく向上させることができます。時間制限付きの認証情報を使うことで、万が一情報が漏洩しても被害を最小限に抑えられるでしょう。

また、Docker を使った運用方法も合わせてご紹介しました。コンテナ化することで、環境構築の再現性が高まり、複数のサーバーへのデプロイも容易になります。

これらの設定を組み合わせることで、本番環境でも安心して運用できる、堅牢な TURN サーバーを構築できますよ。

WebRTC アプリケーションの品質は、TURN サーバーの設定に大きく左右されます。今回ご紹介した方法を参考に、ぜひセキュアで信頼性の高いリアルタイム通信環境を実現してください。

関連リンク