
2026/02/16 22:39
自分でXMPPサーバーを運営する
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
このガイドは、Docker 内で Prosody を実行する完全にフェデレートされた XMPP チャットサーバーを展開する方法を説明します。重要な要素をすべて網羅しています:
• ドメイン設定、Let’s Encrypt TLS 証明書(Cloudflare DNS チャレンジ付き)、およびと_xmpp-client._tcp用の DNS SRV レコード。_xmpp-server._tcp
• ポート 5222(クライアント)と 5269(フェデレーション)を公開し、data/config/certs をマウントし、公式イメージをプルするprosodyim/prosody:13.0。docker‑compose.yml
• ロスト、SASL 認証、TLS、ブロックリストフィルタリング、多デバイス同期(carbons, smacks, cloud_notify)、メッセージアーカイブ(1 年有効期限の MAM)および管理シェルを有効にする Prosody モジュール。
• セキュリティ強化:内部ハッシュ認証、、サーバー間リンクを暗号化するためのallow_registration = false、不要なドメイン/IP のブロックリスト。s2s_secure_auth
• HTTP ファイルアップロードコンポーネント()は Caddy を通じてポート 5280 で提供され、サイズ/有効期限制限と MUC コンポーネント(upload.xmpp.example.com)を備える。conference.xmpp.example.com
• Docker 内で実行される coturn TURN/STUN サーバーにより NAT トラバーサルをサポートし、Prosody と静的シークレットを共有する(ホストネットワーキング)。
• ファイアウォール規則は 5222、5269、3478/5349、TURN リレー範囲 49152–49200 UDP を開放し、リバースプロキシ用 HTTPS を許可。
• 登録が無効になっているため、により手動でユーザーを作成。prosodyctl adduser
• 検証ツール:と XMPP コンプライアンステスター(正しい設定でスコアは 90 % を超える可能性)。prosodyctl check
セットアップ後、管理者は手動でユーザーを登録し、メッセージ送信や音声/ビデオ通話、メッセージアーカイブへのアクセス、ファイルの安全なアップロードを行うことができます。この方法により、組織は商用プロバイダーと競合する自己ホスト型オープンソース通信スタックを提供し、サーバー側設定なしでエンドツーエンド暗号化(OMEMO)を可能にします。
本文
Docker で Prosody を立ち上げてフェデレーション型メッセージング(ファイル共有・音声通話・エンドツーエンド暗号化)を実現するノート
概要
約1年前に個人用メッセージングを Signal に移行しました。Signal は使い勝手がよくても、サービスプロバイダーが一つだけなので「Signal が停止したらまた最初からやり直し」というリスクがあります。XMPP を使えばフェデレーション(相互接続)が自動で行われるため、サーバーを自由に選べます。
本記事では Prosody を Docker で稼働させ、DNS 設定から音声通話まで完全に機能する XMPP サーバーを構築した手順を紹介します。
前提条件
- Docker と Docker Compose がインストールされたサーバー
- 所有しているドメイン
- TLS 証明書(Let’s Encrypt で十分)
- 適切な DNS レコード設定
DNS レコード
XMPP は SRV レコードでクライアントや他のサーバーに自分のサーバーを知らせます。以下を追加してください。
_xmpp-client._tcp.xmpp.example.com SRV 0 5 5222 xmpp.example.com. _xmpp-server._tcp.xmpp.example.com SRV 0 5 5269 xmpp.example.com.
- 5222:クライアント接続
- 5269:サーバー間フェデレーション
また
xmpp.example.com を自サーバーの IP に向ける A レコードも必須です。
HTTP ファイルアップロード(推奨)を有効にしたい場合は、同じサーバーを指す CNAME または A レコードで
upload.xmpp.example.com を追加します。グループチャット用のクリーンなサブドメインが必要なら
conference.xmpp.example.com も設定してください(Prosody は内部で処理します)。
TLS 証明書
Prosody は証明書無しでは起動しません。以下は Cloudflare DNS チャレンジを使った Let’s Encrypt の例です。
docker run --rm \ -v ~/docker/xmpp/certs:/etc/letsencrypt \ -v ~/docker/xmpp/cloudflare.ini:/etc/cloudflare.ini:ro \ certbot/dns-cloudflare certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /etc/cloudflare.ini \ -d xmpp.example.com
cloudflare.ini は API トークンを格納します。
dns_cloudflare_api_token = your-cloudflare-api-token
取得後、Prosody が読み込めるよう権限を調整してください。
chmod -R 755 ~/docker/xmpp/certs/live/ ~/docker/xmpp/certs/archive/ chmod 644 ~/docker/xmpp/certs/archive/xmpp.example.com/*.pem
毎月自動更新用に cron を設定します。
0 3 1 * * docker run --rm -v ~/docker/xmpp/certs:/etc/letsencrypt \ -v ~/docker/xmpp/cloudflare.ini:/etc/cloudflare.ini:ro \ certbot/dns-cloudflare renew \ --dns-cloudflare-credentials /etc/cloudflare.ini \ && docker restart xmpp
Docker 設定
docker-compose.yml
docker-compose.ymlservices: prosody: image: prosodyim/prosody:13.0 container_name: xmpp restart: unless-stopped ports: - "5222:5222" # クライアント - "5269:5269" # フェデレーション volumes: - prosody-data:/var/lib/prosody - ./prosody.cfg.lua:/etc/prosody/prosody.cfg.lua:ro - ./certs/live/xmpp.example.com/fullchain.pem:/etc/prosody/certs/xmpp.example.com.crt:ro - ./certs/live/xmpp.example.com/privkey.pem:/etc/prosody/certs/xmpp.example.com.key:ro volumes: prosody-data:
- 5222 と 5269 を公開。
ボリュームにユーザー情報とメッセージアーカイブを保持。prosody-data- 設定ファイルと証明書は読み取り専用でマウント。
Prosody の設定 (prosody.cfg.lua
)
prosody.cfg.luaモジュール
modules_enabled = { -- 基本機能 "roster", "saslauth", "tls", "dialback", "disco", "posix", "ping", "register", "time", "uptime", "version", -- セキュリティ "blocklist", -- マルチデバイス & モバイル "carbons", "csi_simple", "smacks", -- ストリーム管理(信頼性の高い配信) "cloud_notify", -- モバイルプッシュ通知 -- メッセージアーカイブ "mam", -- ユーザープロファイル & プレゼンス "vcard_legacy", "pep", "bookmarks", -- 管理者 "admin_shell", }
- carbons:全デバイス間でメッセージを同期。
- smacks:切断・再接続時に安全に再送信。
- cloud_notify:モバイルのバッテリー節約に不可欠なプッシュ通知。
- mam:サーバー側で履歴保存。
セキュリティ設定
c2s_require_encryption = true s2s_require_encryption = true s2s_secure_auth = true authentication = "internal_hashed" allow_registration = false
全ての接続を暗号化し、登録は無効(
prosodyctl で手動作成)。s2s_secure_auth は自己署名証明書や設定ミスしたサーバーとの認証を拒否します。
OMEMO 暗号化
OMEMO はエンドツーエンド暗号化を実現し、サーバー管理者がメッセージ内容を読むことはできません。クライアント(Monal, Conversations, Gajim)側で処理され、サーバー設定は不要です。
メッセージアーカイブ
archive_expires_after = "1y" default_archive_policy = true
メッセージは 1 年保存。デフォルトでアーカイブを有効にし、クライアントが個別にオプトアウト可能。
HTTP ファイルアップロード
http_interfaces = { "*" } http_ports = { 5280 } https_ports = {} http_external_url = "https://xmpp.example.com"
Prosody は内部でポート 5280 を使い HTTP サービスを提供。HTTPS はリバースプロキシ(Caddy)で処理します。
http_external_url はクライアントにファイルアップロード時の URL を伝えるために使用。
バーチャルホストとコンポーネント
VirtualHost "xmpp.example.com" ssl = { key = "/etc/prosody/certs/xmpp.example.com.key"; certificate = "/etc/prosody/certs/xmpp.example.com.crt"; } Component "conference.xmpp.example.com" "muc" modules_enabled = { "muc_mam" } restrict_room_creation = "local" Component "upload.xmpp.example.com" "http_file_share" http_file_share_size_limit = 10485760 -- 10 MB http_file_share_expires_after= 2592000 -- 30日 http_external_url = "https://xmpp.example.com"
- MUC:グループチャット。
により履歴を保持。muc_mam - 部屋作成はローカルユーザー限定。
- File Share:画像・ファイルアップロード、1 ファイルあたり 10 MB、30 日で期限切れ。
ファイルアップロード用リバースプロキシ(Caddy)
xmpp.example.com { reverse_proxy xmpp:5280 }
クライアントが画像を送信すると、Prosody は
https://xmpp.example.com/upload/... という URL を返し、受信側は HTTPS 経由で取得します。
アカウント作成
登録機能を無効にしているため、コマンドラインからユーザーを追加します。
docker exec -it xmpp prosodyctl adduser danny@xmpp.example.com
パスワードの入力が求められます。完了すると任意の XMPP クライアントでログインできます。
ファイアウォール設定
必要ポートを開放します。
sudo ufw allow 5222 comment 'XMPP client' sudo ufw allow 5269 comment 'XMPP federation' # リバースプロキシ用に 80/443 が未開放なら開く
サーバーがルータの背後にある場合は、5222 と 5269 を転送してください。
音声・ビデオ通話
テキストとファイル共有は標準で動作します。音声/ビデオを行うには TURN/STUN サーバー(coturn)が必要です。クライアントは NAT 背後にいる場合にメディア接続を確立するためにこれを利用します。
coturn の Docker Compose (docker-compose.yml
)
docker-compose.ymlservices: coturn: image: coturn/coturn:latest container_name: coturn restart: unless-stopped network_mode: host # NAT トラバーサルに必須 volumes: - ./turnserver.conf:/etc/coturn/turnserver.conf:ro tmpfs: - /var/lib/coturn
turnserver.conf
turnserver.conflistening-port=3478 tls-listening-port=5349 min-port=49152 max-port=49200 relay-threads=2 realm=xmpp.example.com use-auth-secret static-auth-secret=YOUR_SECRET_HERE no-multicast-peers no-cli no-tlsv1 no-tlsv1_1 denied-peer-ip=10.0.0.0-10.255.255.255 denied-peer-ip=172.16.0.0-172.31.255.255 denied-peer-ip=192.168.0.0-192.168.255.255 log-file=stdout # NAT 直下の場合: # external-ip=YOUR_PUBLIC_IP/YOUR_PRIVATE_IP
Prosody に TURN を知らせる
"turn_external" モジュールを追加し、VirtualHost ブロック内に以下を設定。
turn_external_host = "xmpp.example.com" turn_external_port = 3478 turn_external_secret= "YOUR_SECRET_HERE"
ファイアウォールでポートを開放します。
sudo ufw allow 3478 comment 'STUN/TURN' sudo ufw allow 5349 comment 'TURNS' sudo ufw allow 49152:49200/udp comment 'TURN relay'
動作確認は次のコマンドで行います。
docker exec xmpp prosodyctl check turn
クライアント
- iOS:Monal(OSS、最新 XEP 対応)
- Android:Conversations
- デスクトップ:Gajim (Linux/Windows)、Monal (macOS)
全クライアントは OMEMO 暗号化、ファイル共有、グループチャット、音声/ビデオ通話をサポートしています。
設定の確認
Prosody の組み込み診断ツールでチェックできます。
docker exec xmpp prosodyctl check
DNS レコード、TLS 証明書、接続性、モジュール構成を検証し、警告が出たら対処してください。
XMPP Compliance Tester も併用すると、設定後に 90 % 以上のスコアを取得できました。
最後に
Docker コンテナで Prosody を稼働させ、リバースプロキシと組み合わせることで得られるメリットは以下です。
- フェデレーション型メッセージング:任意の XMPP サーバーとの通信が可能
- ファイル共有:HTTP ファイルアップロードコンポーネントで実現
- メッセージアーカイブ:サーバー側に保存して検索・同期を簡単に
- プッシュ通知:モバイルデバイスのバッテリー消費を抑制
- 音声/ビデオ通話:coturn TURN/STUN サーバーで NAT を越える
日常的には Signal を主に使っていますが、自前の XMPP サーバーを持つことで「1 つのサービスに完全依存しない」という安全策と、Docker が稼働しているサーバーなら週末プロジェクトとしても楽しめるというメリットがあります。