服务部署 Step 5. Matrix 聊天服务器 Synapse
简介
Matrix 是一个即时通讯项目,或者说一个开放的「协议」,一个「API」。而且它是联邦式的。
解释联邦式的通讯协议时,我最喜欢举的一个例子就是电子邮件:电子邮件就是一个协议(族),世界上有不同的公司,它们处于世界各地:例如 Google(Gmail)、Microsoft(Outlook)、网易(163 邮箱)、腾讯(QQ 邮箱)等。它们各自都有自己的服务器,拥有自己的一群用户。同一家服务商内部的 2 个用户,如 [email protected]
和 [email protected]
,可以互相通信,这是不言而喻的;而由于这些不同的公司在收发邮件时遵循的都是同一套电子邮件标准,所以在不同公司之间的通信也是可行的:例如 Google 的用户 [email protected]
发信给网易的用户 [email protected]
,这是完全可以的。
更好的消息是,由于电子邮件协议是开放的,除了上述传统大公司,每个小公司、学校乃至个人都可以搭建自己的电子邮件服务器,比如清华大学的 [email protected]
,你个人的 [email protected]
等。只要大家都遵守同一套电子邮件协议,就可以互相通邮件。
而 Matrix 协议就是一个与电子邮件架构十分类似,专门用于即时通信、IP 语音等应用的开放式协议。用户可以找一家大的服务商注册一个账号(正如在 Gmail、Outlook 等大型邮件服务商处注册邮件账号),也可以建一个自己的服务器(正如您自己建立一个电子邮件服务器)。本文就是要建立一个自己的服务器,安装在服务器上的软件名称叫 Synapse。
在社交媒体界也有一个类似的联邦式架构的项目,Mastodon,大致可以比作联邦式架构的 Twitter。我不是一个极端的去中心化主义者,并且觉得 Telegram 之类的中心化聊天软件的使用体验可能更好。但是在中心化巨头公司占了绝对主流、定义了人们的互联网乃至现实生活方式的今天,我想很多去中心化的项目都值得一试。
Delegation
注 2:本文自建 Synapse 容器要成功运行,前提是已经完成前面文章中启动 Traefik 容器的步骤。
在 Synapse 的配置中,server_name
变量决定了用户 ID 后面的域名,正如电子邮件地址 @
符号后面的域名一样。假如我们将要搭建的服务器名 server_name=my-example.com
,那么服务器上的一个叫做 user
的用户,其完整的用户名就是 @user:my-example.com
。默认情况下,Matrix Federation 里的其他服务器,会通过 server_name:8443
来连接我们的服务器。
如果您打算使用的 Synapse 服务器的实际域名(即 Traefik 路由中的 Host(`my-example.com`)
里面的域名),与用户名后缀(即 server_name
)的确是一致的,而且也开放了 8443
端口,那么可以跳过本节,直接开始服务部署。但要注意下面所有代码中的 my-example.com
和 chat.my-example.com
域名,全部替换成自己的同一个域名,例如:
my-example.com ==> yourdomain.com
chat.my-example.com ==> yourdomain.com
但在实际应用中:
- 我们的一级域名例如
my-example.com
可能已经另作他用,而 Synapse 服务器的实际地址不得不使用一个二级子域名,例如chat.my-example.com
;或者我不想开放8443
端口,而是只开放443
等端口; - 另一方面,我们又希望用户的 ID 后缀(即
server_name
)是一级域名@user:my-example.com
而非二级域名@user:chat.my-example.com
—— 正如人们希望自己的电子邮件地址是[email protected]
而不是[email protected]
。
总之,若 Synapse 服务器的实际域名与 server_name
不一致,则需要 Delegation。
假定我们将要使用的 server_name
是 my-example.com
,而 Synapse 服务器的实际可 DNS 地址是 chat.my-example.com:443
。我们需要做的准备工作是:
设法在 https://<server_name>/.well-known/matrix/server
放置一个文件,或令该 URL 返回一个 JSON 对象,其内容是:
{
"m.server": "chat.my-example.com:443"
}
例子见这里。
创建文件目录及配置文件
Synapse 需要一个后端数据库,我们可以选择默认的 SQLite3,或者较重的 PostgreSQL 等数据库。这里我使用默认的 SQLite3 数据库,不需要额外的数据库容器,只会在 Synapse 自己的数据目录下多一个 homeserver.db
文件。
Update: 后来我迁移到了 PostgreSQL 数据库后端,迁移的过程也有官方文档指导,见这里。要留意,该官方文档描述的是没有使用 Docker 的情形,使用 Docker 的话需要对各个步骤执行的命令做出适当的调整。但思路是一致的。
mkdir -p ~/site/synapse
mkdir -p ~/volumes/synapse
接下来生成 Synapse 的 homeserver.toml
文件。官方文档在此。首先运行
docker run -it --rm \
-v ~/volumes/synapse:/data \
-e SYNAPSE_SERVER_NAME=my-example.com \
-e SYNAPSE_REPORT_STATS=yes \
matrixdotorg/synapse:latest generate
sudo vim ~/volumes/homeserver.yaml
运行成功后,Synapse 就会在 ~/volumes/synapse
目录下生成初始配置文件。我们需要编辑 homeserver.yaml
配置文件。这个文件有接近 3000 行,注释详尽,如果不着急的话可以慢慢阅读。我只编辑了其中少数几行:
# homeserver.yaml
# ... 注意到 server_name 和 public_baseurl 的区别
# 回顾本文 Delegation 一节中讲到的事项
server_name: "my-exmaple.com"
# ...
public_baseurl: https://chat.my-example.com/
# ... 这里保持默认
listeners:
# ...
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
# ... 小内存机器需要限制能加入的远程 Room,如果外部房间过于巨大,可能会把我们的 Server 挤死机
limit_remote_rooms:
# Uncomment to enable room complexity checking.
#
enabled: true
# ... ACME 保持默认关闭,因为我们将用 Traefik 来处理 HTTPS 证书
acme:
# ACME support is disabled by default. Set this to `true` and uncomment
# tls_certificate_path and tls_private_key_path above to enable it.
#
enabled: false
# ... SQLite3 数据库保持默认。如果使用 PostgreSQL 数据库,需要注释掉 sqlite3 的配置,
# 而添加 psycopg2 的配置
database:
name: sqlite3
args:
database: /data/homeserver.db
# ... 记下这个 secret
registration_shared_secret: "xxxxxxxxxxxxxxxxxxxx..."
# ... Email 填写自己能用的 SMTP Server,可以是自己的邮箱
# 这里设置好才能给用户发邮件,例如验证用户邮箱等
email:
# The hostname of the outgoing SMTP server to use. Defaults to 'localhost'.
smtp_host: smtp.mailgun.org
# The port on the mail server for outgoing SMTP. Defaults to 25.
smtp_port: 587
# Username/password for authentication to the SMTP server. By default, no
# authentication is attempted.
smtp_user: "..."
smtp_pass: "..."
# ...
require_transport_security: true
# ...
notif_from: "Your Friendly %(app)s homeserver <[email protected]>"
其余的选项保持默认,以后有需求慢慢改。
然后创建 docker-compose.yml
配置:
cd ~/site/synapse
vim docker-compose.yml
# docker-compose.yml
version: "3.5"
services:
synapse:
image: matrixdotorg/synapse
restart: always
container_name: synapse
volumes:
- synapse:/data
environment:
- TZ=Asia/Hong_Kong
labels:
- "traefik.enable=true"
# Replace with your own hostname
- "traefik.http.routers.synapse.rule=Host(`chat.my-example.com`)"
- "traefik.http.routers.synapse.service=synapse"
- "traefik.http.services.synapse.loadbalancer.server.port=8008"
- "traefik.http.routers.synapse.entrypoints=websecure"
- "traefik.http.routers.synapse.tls.certresolver=mydnschallenge"
- "traefik.http.middlewares.synapse.headers.stsSeconds=311040000"
- "traefik.http.middlewares.synapse.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.synapse.headers.stsPreload=true"
- "traefik.docker.network=traefik"
networks:
- traefik
- default
volumes:
synapse:
driver_opts:
type: none
o: bind
device: ${HOME}/volumes/synapse
networks:
traefik:
external:
name: traefik
default:
现在可以启动 Server 了:
docker-compose up -d
docker ps
Synapse 容器也有状态(STATUS
)检查,等待变成 healthy
,在浏览器中打开 Synapse 服务器的实际地址(不是 server_name
):https://chat.my-example.com
,如果自动跳转到如下页面,提示「It works! Synapse is running」即是成功:
接下来测试我们的 Synapse 服务器能否正常加入 Matrix 宇宙/联邦(Universe/Federation)。我们希望搭建好的服务器能够和其他同样使用 Matrix 协议的服务器互相通信,而不是只能自己服务器内部的用户之间自闭交流。用 Matrix Federation Tester 进行测试。输入 server_name
而不是 Synapse 的实际地址,点击 Go:
创建第一个账号
默认情况下,Synapse 没有开放新用户注册功能。如果您经营的 Matrix 节点,希望开放用户注册,可以编辑 homeserver.yaml
里面相关设置项,然后重启 Synapse 容器。在这里,由于我只希望我自己的 Synapse 服务器服务于我以及少数几个人(所谓「私人服务器」),为防止网络上的恶意注册 bot 滥用,所以保持新用户注册功能在关闭状态。
但是,我们依然可以用上面配置文件里面的 registration_shared_secret
在后台强行注册用户。下面用这个方法注册一个用户给自己用:
# 进入容器内的 bash
docker exec -it synapse /bin/bash
# 从这一行往下,都是在 Synapse 容器内部运行的命令
# ========
# 把 "xxxxxxxx" 替换成 registration_shared_secret 的内容
register_new_matrix_user -k "xxxxxxxx" https://chat.my-example.com
# 下面根据屏幕提示填写
New user localpart [root]: xxxx
Password:
Confirm password:
Make admin [no]:
Sending registration request...
Success!
# 退出 Synapse 容器
exit
登录
接下来找一个 Matrix 协议的客户端登录。Matrix 协议的客户端多种多样,在这里有一个列表。下面以目前比较比较主流的 Element (旧称 Riot.im)为例,它有 Android、iOS、macOS、Windows、Linux 全平台版本,还有网页版。下面的操作以网页版为例,其他平台的版本界面几乎一致。
在 Homeserver 下点击 Edit,输入自己的服务器 URL https://chat.my-example.com
,并输入刚才创建的用户名和密码:
登录成功:
然后就可以探索一下 Element 的界面,浏览一遍系统设置、个人 Profile 设置、密钥设置等设置项,体验联邦式聊天了。点击左侧 People 和 Rooms 旁边的加号 +
,寻找其他人或房间。我们刚建立的私人服务器目前只有 1 个人,你可以通过完整用户名 @username:domain.tld
的形式寻找其他服务器上朋友。
一个小 Tips: iOS 版本的 Element,在 iOS 系统「语言与地区」设置为非中国大陆的情况下,似乎可以使用 CallKit。关于什么是 CallKit,看这里。
Next
以上算是完成了 Synapse 服务器的基本搭建,可以找人聊天了。
然而无论在服务后台维护层面 —— 包括服务器性能调优等;还是聊天应用层面 —— 例如如何添加表情包、添加 Telegram 转发机器人等,都还有非常多可以调整、修改、增加的内容。这些内容留待以后慢慢写。
Self-Hosted Service 风险提示
鉴于 Matrix 及其客户端的(可能并非默认开启的)端到端加密机制,如果您使用一个您并不 100% 信任的朋友自建的 Homeserver,可能需要了解:
- Homeserver 的管理员可以重置任意用户的登录密码。
- 如果开启了端到端加密(方法和原理参考上述链接),Homeserver 的管理员在不知道您的 Passphrase 或 Recovery Key 的情况下,无法查看您之前发送的加密消息。即,即使 Homeserver 的管理员突然变得恶意,她可以改掉您的账户密码,但她:
- 无法看到您发送或收到的加密历史消息;
- 无法完美地冒充您的身份,因为如果管理员以您的账号在新设备登录,您的联系人会收到提示,显示您的账号在一个新设备上登录,需要重新私下验证,重新决定是否信任这个新设备。如果账号冒用者不知道您的 Passphrase 或 Recovery Key,她无法单方面消除这个提示。