Matrix 即时通讯服务搭建流程

本文的目标是为非技术背景的(潜在)Matrix 站长们提供一个搭建时的参考。本文的主要语言是简体中文,但对于一些常用英文术语不做翻译(因为不会)。若读者具备一定的技术基础,请直接参考官方指南进行安装和配置。如对本文有任何疑问,请在 MastodonMatrix 联系作者。

开始之前

首先需要选购域名和服务器,并配置 CDN 服务(可选)。相关内容请参考 Salt 的 Mastodon 社区搭建详解的第一章至第三章 ,此处不再赘述。

本文的搭建环境是 Ubuntu 20.04,使用官方 prebuilt packages 安装 Synapse,且不配置 delegation(即对前后端地址不做区分)和 VoIP(即不支持语音通话)。如果想采取不同的设置,请参考以下资源:

Synapse 的安装和配置

Synapse 是一个由 Matrix.org 开发和维护的开源 Matrix homeserver. 本章将介绍其安装和数据库配置过程。服务器 SSH 的登录请参考 Salt 的教程。

安装 Synapse

更新 Ubuntu 系统:

sudo apt update
sudo apt upgrade

创建一个名为 matrix 的用户,并设置密码:

useradd -m matrix
passwd matrix

进入用户,并按提示输入密码:

su - matrix

安装 matrix-synapse 的 dependencies:

sudo apt install -y lsb-release wget apt-transport-https

添加 GPG keys:

sudo wget -O /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
echo \"deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main\" |
    sudo tee /etc/apt/sources.list.d/matrix-org.list

安装 matrix-synapse package:

sudo apt update
sudo apt upgrade
sudo apt install matrix-synapse-py3

安装 package 后,会弹出一个粉色的 package configuration 界面,请在对话框内输入自己的域名,然后选择 ok。

退出 matrix 用户:

exit

Synapse 的配置文件默认存储在 /etc/matrix-synapse/homeserver.yaml,官方安装指南中,为了更新时的便利,建议将此文件复制一份,放在 /etc/matrix-synapse/conf.d/

cp /etc/matrix-synapse/homeserver.yaml /etc/matrix-synapse/conf.d/

之后编辑配置文件时,就可以使用如下命令打开 homeserver.yaml:

nano /etc/matrix-synapse/conf.d/homeserver.yaml

在 root 用户下启动 matrix-synapse 服务,并检查其运行状态:

sudo systemctl enable matrix-synapse
sudo systemctl start matrix-synapse
sudo systemctl status matrix-synapse

此时应该显示服务状态为 active (running),即说明服务正常运行。

如果 matrix-synapse 服务未能正常启动,请打开 synapse 的 log 文件查看原因(之后如果出现什么错误,也可以通过此方式查询 log,然后依据其中的报错解决问题):

nano /var/log/matrix-synapse/homeserver.log 

(注:如果 log 文件过长,请使用 cat /dev/null \u003e /var/log/matrix-synapse/homeserver.log 命令将文件清空后,重新启动服务,再查看 log)

如果在 log 文件中看到这一行:

twisted.internet.error.CannotListenError: Couldn't listen on ::1:8008: [Errno 99] Cannot assign requested address.

则检查一下系统正在监听的端口:

ss -plnt

若未看到 127.0.0.1:8008,则需要修改配置文件 homeserver.yaml 中的 listeners 这一部分:

nano /etc/matrix-synapse/conf.d/homeserver.yaml

将 bind_addresses 一行修改为:

bind_addresses: ['localhost']

然后重启服务:

sudo systemctl restart matrix-synapse
sudo systemctl status matrix-synapse

如果没有其它问题,服务应该已经能够正常运行了。

为后台注册用户生成一个随机字符串:

cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1

将会得到一串类似 2lyjkU7Ybp24rWR1TBJkut65RFcXZZA 的字符,请将其保存在其它地方。

打开 homeserver.yaml,添加如下内容,注意将这个随机字符串放在双引号内。该设置意味着禁止通过网页前端注册新用户,但可以在后台以命令行的形式创建新用户:

enable_registration: false
registration_shared_secret: \"2lyjkU7Ybp24rWR1TBJkut65RFcXZZA\"

重启 matrix-synapse 服务:

sudo systemctl restart matrix-synapse

配置 PostgreSQL

Synapse 默认使用 SQLite 数据库,但这将影响其性能,官方指南建议将其更改为 PostgreSQL.

确认低版本的 postgres library 已安装上:

apt install libpq5

将 matrix 用户加入 sudoers file:

sudo nano /etc/sudoers

在 User privilege specification 处加入一行:

matrix   ALL=(all)           ALL

登录 matrix 用户:

su - matrix

认证 postgres 用户:

sudo -u postgres bash

创建 postgres 用户和数据库:

createuser --pwprompt synapse_user
createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse

这将创建一个名为 synapse_user 的数据库和一个名为 synapse 的 postgres 用户。

接下来,修改配置文件:

exit
nano /etc/matrix-synapse/conf.d/homeserver.yaml

将 homeserver.yaml 的 database 部分改为如下内容:

database:
  name: psycopg2
  args:
    user: synapse_user
    password: matrix
    database: synapse
    host: localhost
    cp_min: 5
    cp_max: 10

如果在配置 postgreSQL 的过程中,提示 FATAL: Ident authentication failed for user \"synapse_user\",则需要编辑 pg_hba.conf. 本段可参考官方文档的介绍

在 postgres 用户下,输入

psql -t -P format=unaligned -c 'show hba_file';

查询 pg_hba.conf 文件的位置,指令会返回一个路径(如 /etc/postgresql/14/main/pg_hba.conf),打开该路径指向的文件:

nano  /etc/postgresql/14/main/pg_hba.conf

添加如下内容:

host    synapse     synapse_user    ::1/128     md5

因为 pg_hba.conf 中,行的前后顺序是有意义的,所以注意以上内容应添加在此行之前:

 host    all         all             ::1/128     ident

TLS 证书

获取 TLS 证书

安装 snap package:

sudo apt install snapd
sudo snap install core

检查是否安装正常:

sudo snap install hello-world

如果正常,会返回 hello-world.

安装 certbot:

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

打开端口 80 和 443:

ufw allow 80
ufw allow 443

获取证书(尖括号部分为读者需要修改的内容,尖括号不用输入,下同):

sudo certbot certonly --nginx -d \u003c你的域名\u003e

按照提示输入信息,成功获取证书后会有如下信息提示:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for synapse.matrix.org

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/\u003c你的域名\u003e/fullchain.pem
Key is saved at: /etc/letsencrypt/live/\u003c你的域名\u003e/privkey.pem
This certificate expires on 2022-07-15.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

配置 Nginx 反向代理

安装 Nginx:

sudo apt-get install nginx -y

打开并编辑名为 matrix.conf 的配置文件:

nano /etc/nginx/sites-available/matrix

参考配置如下(该 Nginx 配置适用于配置 CDN 之后的情况,若未配置 CDN,请参考该链接,注意打开 port 8448):

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name \u003c你的域名\u003e;

    ssl_certificate /etc/letsencrypt/live/\u003c你的域名\u003e/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/\u003c你的域名\u003e/privkey.pem;

    location /.well-known/matrix/client {
        return 200 '{\"m.homeserver\": {\"base_url\": \"https://\u003c你的域名\u003e\"}}';
        default_type application/json;
        add_header Access-Control-Allow-Origin *;
    }

   location /.well-known/matrix/server {
        return 200 '{\"m.server\": \"\u003c你的域名\u003e:443\"}';
        default_type application/json;
        add_header Access-Control-Allow-Origin *;
    }

    location ~ ^(/_matrix|/_synapse/client) {
        # note: do not add a path (even a single /) after the port in `proxy_pass`,       
        # otherwise nginx will canonicalise the URI and cause signature verification 
        # errors.
        proxy_pass http://localhost:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;

        # Nginx by default only allows file uploads up to 1M in size
        # Increase client_max_body_size to match max_upload_size defined in homserver.yaml
        client_max_body_size 50M;

    # Synapse responses may be chunked, which is an HTTP/1.1 feature.
    proxy_http_version 1.1;
    }
}

载入新配置:

ln -s /etc/nginx/sites-available/matrix /etc/nginx/sites-enabled/

开启 nginx 服务,并查看服务状态:

sudo systemctl enable nginx
sudo systemctl start nginx
sudo systemctl status nginx

注意,在 nginx 中配置 tls 后,就无需再修改 homeserver.yaml 中的 listeners 部分。

至此,Synapse 的配置完成。可在 the Matrix Federation Tester 中检查 federation 的状态。

注册新用户

登录 matrix 用户:

su - matrix

运行如下指令:

register_new_matrix_user -c /etc/matrix-synapse/conf.d/homeserver.yaml

在接下来出现的指令中设置账户的详细信息,如下:

New user localpart: \u003c用户名称\u003e
Password: \u003c密码\u003e
Confirm password: \u003c密码\u003e
Make admin [no]: \u003c若想设为管理员则输入 yes,否则回车\u003e
Success!

安装并配置 Element

Element 是 Matrix 的常见客户端之一,如果偷懒可以不配,直接使用其它站点的登录界面,在 homeserver 处输入自己的域名即可登录,如这个。 也可以尝试 FluffyChat(FluffyChat 也可以配置在自己站里但是我没搞懂)。也可以在移动端使用 Element 或 FluffyChat 的 app 访问 Matrix.

本章介绍的是在自己的站点配置 Element web client 的方法,主要参(复)考(制)了这篇教程

首先在 /var/www/html 路径内为 Element 创建一个文件夹,并切换到该文件夹:

sudo mkdir -p /var/www/html/\u003c你的域名\u003e
cd /var/www/html/\u003c你的域名\u003e

使用 wget 下载 Element,注意在 GitHub 的 release 页面查询以 v 开头的最新版本号,如 v1.11.16 ,并替换到指令中:

sudo wget https://github.com/vector-im/element-web/releases/download/\u003c最新版本号\u003e/element-\u003c最新版本号\u003e.tar.gz

为 Element 导入 signing key:

 sudo gpg --keyserver keyserver.ubuntu.com --search-keys [email protected]

用其验证 asc signature:

sudo gpg --verify element-\u003c最新版本号\u003e.tar.gz.asc

解压 Element:

sudo tar -xzvf element-\u003c最新版本号\u003e.tar.gz

为 Element 创建一个别名:

 sudo ln -s element-\u003c最新版本号\u003e element
 sudo chown www-data:www-data -R element

进入 element 文件夹,复制一份实例配置文件:

cd element-\u003c最新版本号\u003e
sudo cp config.sample.json config.json

打开配置文件:

nano config.json

修改如下(此处只摘取开头的一段,后面的不做变动):

{
    \"default_server_config\": {
        \"m.homeserver\": {
            \"base_url\": \"https://\u003c你的域名\u003e\",
            \"server_name\": \"\u003c你的域名\u003e\"
        },
        \"m.identity_server\": {
            \"base_url\": \"https://vector.im\"
        }
    },
…

在 Nginx 配置中加入如下内容:

    root /var/www/html/\u003c你的域名\u003e/element-\u003c最新版本号\u003e;

    location / {
        try_files $uri $uri/ =404;
    }

重启 Nginx 服务:

sudo systemctl restart nginx

此时在浏览器中输入你的域名,就能看到 Element 客户端的页面了。

Element

媒体文件外部存储服务(s3 bucket)的部署

随着时间的累积,媒体文件的存储将会大量占用服务器存储空间,影响站点的可持续发展,因此建议配置外部媒体存储服务并定期清理。

请读者参考 Salt 的教程配置 Scaleway object storage,本章不再重复 Salt 讲过的内容,主要叙述在 Synapse 中的配置方法。

配置 s3 storage provider

使用 Synapse 的 S3 storage provider 模块实现媒体的外部存储。在 sudo 用户下,使用以下命令激活 python venv:

source /opt/venvs/matrix-synapse/bin/activate

判断是否已安装 pip

pip --version    # Python2.x
pip3 --version    # Python3.x

如无版本信息,则安装 pip

sudo apt-get install python-pip

pip 安装该模块:

pip install synapse-s3-storage-provider

打开配置文件:

nano /etc/matrix-synapse/conf.d/homeserver.yaml

增加如下内容:

media_storage_providers:
- module: s3_storage_provider.S3StorageProviderBackend
  store_local: True
  store_remote: True
  store_synchronous: True
  config:
    bucket: \u003c你的 bucket 名称\u003e
    region_name: \u003c你的 bucket 地区\u003e
    endpoint_url: https://\u003c你的 bucket 地区地址\u003e
    access_key_id: \u003c你的 access key\u003e
    secret_access_key: \u003c你的 secret access key\u003e

填写示例如下:

media_storage_providers:
- module: s3_storage_provider.S3StorageProviderBackend
  store_local: True
  store_remote: True
  store_synchronous: True
  config:
    bucket: i.blanksheet.today
    region_name: fr-par
    endpoint_url: https://s3.fr-par.scw.cloud
    access_key_id: xxxxxxxxxxxxxxxxxxxxxxx
    secret_access_key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

重启 matrix-synapse 服务:

sudo systemctl restart matrix-synapse
sudo systemctl status matrix-synapse

配置媒体文件定时删除

使用管理员账号登录 Matrix,找到自己的 Access Token. 方法因客户端而异,用 Element 举例:在 Element 客户端的 All Settings \u003e Help \u0026 About \u003e Advanced 中即可找到自己的 Access Token. 举例如下:

syt_AjfVef2_L33JNpafeif_0feKJfeaf0CQpoZk

这个 script 的内容复制到任意位置,并将域名和上一步保存的 Access Token 填入 script 中:

serverDomain=\"\u003c你的域名\u003e\"
token=\"\u003c你的 Access Token\u003e\"
…

默认清除 60 天以上的媒体文件,若想修改这个时间,也可以在 script 中进行修改。

运行 script,检查其运转情况:

sudo sh \u003c你放置 script 的路径\u003e/CleanupMedia.sh

若运行成功,会显示删除了多少文件。

设置定时任务:

crontab -e

Select an editor. 中选择 1,在打开的文件中写入以下内容:

0 3 * * * sudo sh /root/CleanupMedia.sh \u003e /dev/null 2\u003e\u00261

该任务会在每天服务器时间的 3:00 执行。可以使用这个网站检查定时任务是否写对了。

关于注册

前文中将配置文件中的 enable_registration 设为 false,意为关闭注册,只能通过后台注册新用户。若要开启注册,请将该项设为 true,并推荐同时开启以下选项中的至少一项:

也可以强行开启无验证方式的注册,将 enable_registration_without_verification 设为 true 即可,新用户在注册页面输入用户名和密码即可注册。

本章以及 Synapse configuration 的一切细节,详见 Configuration manual.

本文结束。Fly safe ^^

附:配置文件示例

本章以 blanksheet.today 作为实例域名。

Nginx:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name blanksheet.today;

    root /var/www/html/blanksheet.today/element-v1.11.15;

    ssl_certificate /etc/letsencrypt/live/blanksheet.today/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blanksheet.today/privkey.pem;

    location / {
        try_files $uri $uri/ =404;
    }

    location /.well-known/matrix/client {
        return 200 '{\"m.homeserver\": {\"base_url\": \"https://blanksheet.today\"}}';        default_type application/json;
        add_header Access-Control-Allow-Origin *;
    }
   location /.well-known/matrix/server{
        return 200 '{\"m.server\": \"blanksheet.today:443\"}';
        default_type application/json;
        add_header Access-Control-Allow-Origin *;
    }

    location ~ ^(/_matrix|/_synapse/client) {
        proxy_pass http://localhost:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;

        client_max_body_size 50M;

        proxy_http_version 1.1;
    }
}

homeserver.yaml:

server_name: blanksheet.today
pid_file: \"/var/run/matrix-synapse.pid\"
listeners:
  - port: 8008
    tls: false
    type: http
    x_forwarded: true
    bind_addresses: ['localhost']
#    bind_addresses: ['::1', '127.0.0.1']
    resources:
      - names: [client, federation]
        compress: false

database:
  name: psycopg2
  args:
    user: synapse_user
    password: matrix
    database: synapse
    host: localhost
    cp_min: 5
    cp_max: 10

enable_registration: true
registration_shared_secret: \"xxxxxxxxxxxxxxxxxxxxxxxxxxx\"
enable_registration_without_verification: true

media_storage_providers:
- module: s3_storage_provider.S3StorageProviderBackend
  store_local: True
  store_remote: True
  store_synchronous: True
  config:
    bucket: i.blanksheet.today
    region_name: fr-par
    endpoint_url: https://s3.fr-par.scw.cloud
    access_key_id: xxxxxxxxxx
    secret_access_key: xxxxxxxxxxxxxxxxx

trusted_key_servers:
  - server_name: \"vector.im\" # 建议在 Element 设置中关闭此功能

limit_remote_rooms:
  enabled: true
  complexity: 1.0
  complexity_error: \"Room maximum complexity reached. Please contact admin\"
  admins_can_join: true

#code #Fediverse #Matrix