Matrixサーバの構築
Matrixというプロトコルを利用したチャット・ビデオ通話システムをDockerコンテナにより運用します。
フォーラム記事:Tuwunel ( [Matrix] サーバ ) for Docker を整理した内容です。
- Matrixとは
- 導入効果
- 特徴
- Matrixサーバ
- Matrixクライアント
- システム条件
- システム構成
- 各コンテナ設定ファイル
- Docker Composeファイル
- 起動・操作
Matrixとは
Matrixとは、リアルタイムのオンラインコミュニケーションに最適化された、比較的新しい分散型のオープンソース通信プロトコルです。1対1のチャットや音声通話はもちろん、グループチャットやビデオ通話にも優れています。
このMatrixに準拠したサーバは分散型であり、異なるサーバに所属するユーザー同士でも、メールのように共通プロトコルを通じて容易に相互アクセスできるよう設計されています。
チャットや通話はクライアントアプリによってエンドツーエンドで暗号化されており、セキュリティ面でも十分な機能を備えています。エンドツーエンド暗号化が有効なルームでは、メッセージは暗号化された状態でサーバに保存され、クライアントが保持する暗号鍵または復元用のキーバックアップ情報なしでは解読できない仕組みとなっています。
導入効果
Matrixを導入することで、社内外のコミュニケーション基盤を一元化し、情報共有のスピードと正確性を大幅に向上できます。
チャット、音声通話、ビデオ通話を単一のプラットフォーム上で運用できるため、メール・電話・複数のSNSやチャットツールに分散していた連絡手段を集約し、運用コストの削減と業務効率化を実現します。
また、分散型プロトコルを採用しているため、自社サーバによる独立運用が可能であり、外部サービスへの依存を最小限に抑えられます。これにより、機密性の高い情報やプロジェクトデータを自社管理下で安全に運用できます。
エンドツーエンド暗号化により、通信内容の盗聴や漏えいリスクを低減し、セキュリティ要件の厳しい業務用途にも対応可能です。特に、設計開発部門、医療・教育機関、顧客サポート窓口など、高い情報保護が求められる環境で効果を発揮します。
さらに、プロジェクト単位でスペースやルームを柔軟に構成できるため、部門横断型のチーム運営やリモートワーク環境においても、リアルタイムかつ継続的なコミュニケーション基盤として活用できます。
既存のSNSや外部チャットサービスとのブリッジ連携を活用することで、既存ユーザー資産を活かしながら段階的な移行も可能です。
特徴
自前で用意したサーバであれば、使用するホームサーバ実装に応じて、招待制やトークンによるユーザー登録制限を設定できます。
また、管理者には専用の管理ルームや管理APIへのアクセス権限を付与し、ユーザー管理やルーム管理などの各種運用作業を行うことが可能です。
ユーザーは必要に応じてSpace(スペース)を作成し、その中に公開ルームまたは非公開ルームを複数構成できます。非公開ルームでは、他のユーザーを招待してチャットや通話を開始することが可能です。
スペースやルームは無制限に作成でき、名称も任意に設定できます。たとえば、スペースを「デザインプロジェクトチーム」、ルームを「メカ」「エレキ」「ソフト」といった形で構成することで、メンバー間のリアルタイムコミュニケーションを実現する仮想空間を構築できます。
Matrixサーバ
Matrixに準拠したサーバは以下の通りです。今回はRustでコーディングされたオープンソースのTuwunelを採用します。
continuwuity
- Stable
- Apache-2.0 Rust
- Continuwuity, a community driven 2nd degree fork of Conduit focusing on user experience and new features.
- Repository Matrix Room
Synapse Pro
- Stable
- Element Commercial License Python and Rust
- Synapse for Enterprise, with a focus on performance, scalability and compliance.
- Homepage Matrix Room
Tuwunel
- Stable
- Apache-2.0 Rust
- Enterprise successor to conduwuit, the high-performance and feature-rich fork of Conduit.
- Repository Matrix Room
Synapse
- Stable
- AGPL-3.0-or-later OR Element Commercial License Python
- Synapse is a Matrix homeserver written in Python/Twisted.
- Repository Matrix Room
Conduit
- Beta
- Apache-2.0 Rust
- Conduit is a simple, fast and reliable chat server written in Rust
- Repository Matrix Room
Dendrite
- Beta
- AGPL-3.0-or-later OR Element Commercial License Go
- Dendrite is a second-generation Matrix homeserver written in Go!
- Repository Matrix Room
Telodendria
- Alpha
- MIT C
- Telodendria is an open source Matrix homeserver implementation written from scratch in ANSI C and designed to be lightweight and simple, yet functional.
- Repository Matrix Room
Matrixクライアント
Matrix準拠の代表的なクライアントアプリは以下の通りですが、他にも多く存在します。今回はAndroid版Element XとElement WEBを使用します。 Element-Webは公式サイトで公開されていますが、自前でも用意できます。
Nheko
Cinny
- A Matrix client focusing primarily on simple, elegant and secure interface.
- Windows
- macOS
- Linux
- Web
- Open client details
Element Web / Desktop
- A glossy web and desktop client with an emphasis on performance and usability.
- Windows
- macOS
- Linux
- Web
- Open client details
Element X
- Next generation Element on mobile with native OIDC, sliding sync and Matrix RTC for calls.
- iOS
- Android
- Open client details
FluffyChat
システム条件
ホストPC
QEMU/KVMで作成したVM:Debian13(2コアCPU、メモリ2GB、ストレージ60GB)
取得ドメイン
- Matrixサーバ:matrix.example.com
- Livekit(RTCサーバ):matrix-rtc.example.com
- Matrix-Web(ウェブアプリ):chat.example.com
- Turnサーバ:turn.example.com
システム構成
DockerコンテナでTuwunelとElement Webのフロントエンドを構築します。
Dockerコンテナによる全体システム構成は以下の通りです。CoturnのTLS認証はCertbotコンテナが常駐して管理します。
公開ポート
7880:7881/tcp ALLOW Anywhere80/tcp (v6) ALLOW Anywhere (v6)443/tcp (v6) ALLOW Anywhere (v6)3478 (v6) ALLOW Anywhere (v6)5349/tcp (v6) ALLOW Anywhere (v6)7880:7881/tcp (v6) ALLOW Anywhere (v6)50201:50400/udp (v6) ALLOW Anywhere (v6)
各コンテナ設定ファイル
各コンテナの設定ファイルは以下の通りです。TuwunelはMatrixサーバコンテナ、CaddyはTLS認証とプロキシサーバを兼ねたコンテナ、LiveKitはリアルタイム通話の際に利用するWEB-RTC SFU(Selective Forwarding Unit)コンテナ、CoturnはLiveKitがNAT越えが必要となる場合に利用されるTurnサーバコンテナです。
各種設定ファイルのルールについては公式サイトを参照願います。
Caddyfile
matrix.example.com {
handle /.well-known/matrix/server {
header Content-Type "application/json"
respond `{"m.server":"matrix.example.com:443"}`
}
handle /.well-known/matrix/client {
header Access-Control-Allow-Origin "*"
header Content-Type "application/json"
respond `{"m.homeserver":{"base_url":"https://matrix.example.com"},"org.matrix.msc3575.proxy":{"url":"https://matrix.example.com"},"m.identity_server":{"base_url":"https://vector.im"},"org.matrix.msc4143.rtc_foci":[{"type":"livekit","livekit_service_url":"https://matrix-rtc.example.com"}]}`
}
reverse_proxy homeserver:6167
}
matrix-rtc.example.com {
@jwt_service {
path /sfu/get* /healthz* /get_token*
}
handle @jwt_service {
reverse_proxy matrix-rtc-jwt:8081
}
handle {
reverse_proxy host.docker.internal:7880 {
header_up Connection "upgrade"
header_up Upgrade {http.request.header.Upgrade}
}
}
}
chat.example.com {
handle .well-known/matrix/client {
header Content-Type application/json
header Access-Control-Allow-Origin *
respond `{ "m.homeserver": { "base_url": "https://matrix.example.com" }, "m.identity_server": { "base_url": "https://vector.im" } }` 200 }
reverse_proxy element-web:80
}
- Caddyfile構文チェック $ docker compose exec caddy caddy validate --config /config/caddy/Caddyfile
- 証明書確認 $ ls -la ./data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/
tuwunel.toml
[global]
server_name = "matrix.example.com"
database_path = "/var/lib/tuwunel"
address = ["0.0.0.0", "::"]
port = 6167
max_request_size = 20000000
allow_registration = true
registration_token = "xxxxxxxxxxxxxxxxxxxxx"
allow_federation = true
trusted_servers = ["matrix.org"]
turn_uris = ["turns:turn.example.com:5349?transport=tcp", "turns:turn.example.com:5349?transport=udp"]
turn_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
[global.well_known]
client = "https://matrix.example.com"
server = "matrix.example.com:443"
livekit_url = "https://matrix-rtc.example.com"
注)registration_tokenはhomeserverのドメイン:matrix.example.comでユーザ登録する際に必要。Caddyで.well-known/matrix/clientに"https://matrix-rtc.example.com"を指定していれば[global.well_known]の項目は不要(全てコメントアウト)
- Matrix APIの確認 $curl -s https://matrix.example.com/_matrix/client/versions | python3 -m json.tool
- .well-knownの確認 $ curl -s https://matrix.example.com/.well-known/matrix/client | python3 -m json.tool $ curl -s https://matrix.example.com/.well-known/matrix/server | python3 -m json.tool
livekit.yaml
port: 7880
bind_addresses:
- "::"
rtc:
tcp_port: 7881
port_range_start: 50301
port_range_end: 50400
use_external_ip: true
enable_loopback_candidate: false
turn_servers:
- host: turn.example.com
port: 5349
protocol: tls
secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
keys:
xxxxxxxxxxxxxxxxx: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
注)keys:には .env の MRTCKET: MRTCSECRET を記述。
turnserver.conf
listening-port=3478
tls-listening-port=5349
listening-ip=::
realm=turn.example.com
use-auth-secret
static-auth-secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
log-file=stdout
no-tcp-relay
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
allowed-peer-ip=2404:7a87:9140:1000:5054:ff:fe8e:4360
user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous relayed calls per user.
total-quota=1200
cert=/etc/letsencrypt/live/turn.example.com/fullchain.pem
pkey=/etc/letsencrypt/live/turn.example.com/privkey.pem
external-ip=EXTERNAL_IPV6
min-port=50201
max-port=50300
- TCP接続確認 $ nc -zv turn.example.com 5349
- TLS証明書確認 $ openssl s_client -connect turn.example.com:5349
element-config.json
{
"default_server_name": "example.com",
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.example.com"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"brand": "Element",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api"
],
"show_labs_settings": true,
"room_directory": {
"servers": ["matrix.example.com", "matrix.org"]
},
"enable_presence_by_hs_url": {
"https://matrix.example.com": true
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx",
"setting_defaults": {
"RustCrypto.staged_rollout_percent": 60
},
"features": {
"feature_video_rooms": true,
"feature_group_calls": true,
"feature_element_call_video_rooms": true
},
"element_call": {
"url": "https://call.element.io",
"use_exclusively": false
}
}
Docker Composeファイル
docker-compose.yaml
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
container_name: caddy
ports:
- 80:80
- 443:443
environment:
- CADDY_INGRESS_NETWORKS=caddy
- CADDY_DOCKER_CADDYFILE_PATH=/config/caddy/Caddyfile
networks:
- caddy
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data
- ./Caddyfile:/config/caddy/Caddyfile
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
homeserver:
image: jevolk/tuwunel:latest
container_name: tuwunel
restart: unless-stopped
volumes:
- ./db:/var/lib/tuwunel
- ./tuwunel.toml:/etc/tuwunel.toml
environment:
TUWUNEL_CONFIG: '/etc/tuwunel.toml'
networks:
- caddy
matrix-rtc-jwt:
image: ghcr.io/element-hq/lk-jwt-service:latest
container_name: jwt
environment:
- LIVEKIT_JWT_BIND=:8081
- LIVEKIT_URL=wss://matrix-rtc.example.com
- LIVEKIT_KEY=${MRTCKEY}
- LIVEKIT_SECRET=${MRTCSECRET}
- LIVEKIT_FULL_ACCESS_HOMESERVERS=matrix.example.com
restart: unless-stopped
networks:
- caddy
matrix-rtc-livekit:
image: livekit/livekit-server:latest
container_name: livekit
command: --config /etc/livekit.yaml
restart: unless-stopped
volumes:
- ./livekit.yaml:/etc/livekit.yaml:ro
network_mode: "host"
# Certbot just only for Coturn TLS
certbot:
image: certbot/dns-cloudflare
container_name: certbot
restart: unless-stopped
volumes:
- ./certbot/certs:/etc/letsencrypt
- ./certbot/cloudflare.ini:/etc/letsencrypt/cloudflare.ini:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./certbot/deploy-hook.sh:/etc/letsencrypt/renewal-hooks/deploy/restart-coturn.sh:ro
entrypoint: /bin/sh -c
command: >
"certbot certonly
--dns-cloudflare
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini
--dns-cloudflare-propagation-seconds 30
-d turn.example.com
--email [email protected]
--agree-tos
--non-interactive
&& trap exit TERM;
while :; do
sleep 12h &
wait $${!};
certbot renew --quiet;
done"
coturn:
image: docker.io/coturn/coturn
container_name: coturn
restart: unless-stopped
network_mode: host
user: root
volumes:
- ./turnserver.conf:/etc/coturn/turnserver.conf
- ./certbot/certs:/etc/letsencrypt:ro
element-web: # https://hub.docker.com/r/vectorim/element-web
image: vectorim/element-web:latest
container_name: element-web
volumes: - ./element-config.json:/app/config.json:ro
networks: - caddy
networks:
caddy:
external: true
注) - LIVEKIT_KEY=${MRTCKEY} —> “$ pwgen -s 20 1” で生成した文字列を.envに記載 - LIVEKIT_SECRET=${MRTCSECRET} ”$ pwgen -s 64 1”で生成した文字列を.envに記載
- ヘルスチェック $ curl -v https://matrix-rtc.example.com/healthz
- JWT serviceの動作確認 $ curl -s -X POST “https://matrix-rtc.example.com/sfu/get?roomName=test&deviceId=test” -H “Authorization: Bearer dummy” | python3 -m json.tool
起動・操作
システムの起動・ユーザ登録
以下のdocker composeコマンドを実行します。
$ docker compose up -d
起動後、Element-XやウェブアプリなどのMatrixクライアントを利用してユーザ登録を行います。
ユーザ登録には、
- Matrixサーバのドメイン:
matrix.example.com tuwunel.tomlファイルで設定したトークン:registration_token
が必要です。
この一番最初の登録ユーザが管理ユーザとして承認され、管理ルームへの入室を許可されます。管理ルームはデフォルトでTuwunel初回起動時に作成されます。
注)以下の設定により(デフォルト)、最初に登録したユーザーが自動で管理ルームに入ります。
tuwunel.toml
grant_admin_to_first_user = true
create_admin_room = true
他のユーザは、管理ユーザから付与されたトークンを利用してユーザ登録するか、管理ユーザが作成したユーザ名とパスワードを使ってログインします。
管理ユーザによる操作
管理ユーザは、他ユーザの登録・作成などの各種操作を管理ルームで行うことができます。
name: matrix.example.com Admin Room
この管理ルームでユーザ作成など各種操作をするには、メッセージ欄に
”!admin + コマンド"
と入力します。
例)ヘルプ:コマンド一覧
!admin help
> Usage: !admin <COMMAND>
Commands:
appservices - Commands for managing appservices
users - Commands for managing local users
rooms - Commands for managing rooms
federation - Commands for managing federation
server - Commands for managing the server
media - Commands for managing media
debug - Commands for debugging things
query - Low-level queries for database getters and iterators
token - Commands for managing registration tokens
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version