ejabberd

此文档仍在编写中!

ejabberd 是高性能的 XMPP 服务器,可以轻松扩展到大型公共服务器,但对于自托管来说并不太重。它可以相对容易地从 Linux 发行版存储库安装,默认配置适用于运行良好的 XMPP 服务器。然而,这些默认设置往往相当保守,如果您想更好地支持新式 XMPP 功能,就需要进行一些调整。所有 ejabberd 模块设置的官方文档都可以在此处找到。

必要条件:

  • 域名
  • 服务器

本教程从单个虚拟主机[^2]开始。例如,example.net 是域。将所有 example.net 实例替换为您自己的域。

[^2]:虚拟主机允许在一台服务器上运行多个 XMPP 服务。例如,一个 XMPP 服务的域名是 example.net,另一个 XMPP 服务的域名是 example.org,两者在某种程度上是分开的,就像它们在不同的服务器上运行一样。

XMPP 服务器上的每个服务都需要 DNS A/AAAA 记录和 SRV 记录。

A 记录:

  • example.net
  • muc.example.net(用于群聊)
  • upload.example.net(用于 HTTP 文件上传)
  • pubsub.example.net(用于 pubsub 节点)
  • proxy.example.net(用于文件传输代理)
  • turn.example.net(用于 STUN/TURN)

每个都指向要运行 ejabberd 的服务器的 IP 地址。

为每个创建 SRV 记录,指向解析到服务器的域,如下所示:

(所有记录的格式均为 _service._proto.name IN SRV 优先级 权重 端口 目标主机

1_xmpp-client._tcp   IN SRV  5 0 5222 example.net.
2_xmpps-client._tcp  IN SRV  5 0 5223 example.net.
3_xmpp-server._tcp   IN SRV  5 0 5269 example.net.
4_xmpps-server._tcp  IN SRV  5 0 5270 example.net.

1_xmpp-client._tcp.muc   IN SRV  5 0 5222 example.net.
2_xmpps-client._tcp.muc  IN SRV  5 0 5223 example.net.
3_xmpp-server._tcp.muc   IN SRV  5 0 5269 example.net.
4_xmpps-server._tcp.muc  IN SRV  5 0 5270 example.net.

为每个子域(从 muc 开始),不包括 turn.example.net

最后,为 STUN/TURN 添加一组 SRV 记录。

1_stun._udp   IN SRV  5 0 3478 turn.example.net.
2_stun._tcp   IN SRV  5 0 3478 turn.example.net.
3_stuns._tcp  IN SRV  5 0 5349 turn.example.net.
4
5_turn._udp   IN SRV  5 0 3478 turn.example.net.
6_turn._tcp   IN SRV  5 0 3478 turn.example.net.
7_turns._tcp  IN SRV  5 0 5349 turn.example.net.

额外信息:由于这些 SRV 委托记录,可以选择在 example.net 以外的服务器上托管 XMPP(即,如果将一个域上的服务拆分到多个服务器)。更多信息请访问 XEP-0368

打开端口:

TCP: 80 443 5222 5223 5269 5270 3478 5349 49152-65535

UDP: 3478 49152-65535

80443 用于 web 服务器,5222522352695270 用于 XMPP,其余用于 STUN/TURN。

为了避免使用 nginx + Certbot 之类的东西,请使用内置的 ejabberd acme 模块,但本文假设希望在同一系统上托管其他 web 服务,在这种情况下,每个 HTTP 服务都将反向代理到单个 HTTPS web 服务。

最后安装系统包。确保构建有 PostgreSQL 支持。

确认文件 /etc/ejabberd/ejabberd.yml 存在,并且运行 ejabberd 的账号可以读取(几乎可以肯定为 ejabberd),如有必要,可以从 GitHub 仓库 复制示例 ejabberd.ymlwget/curl。如果是从仓库获取,请确保版本与您的操作系统打包的 ejabberd 版本相对应。

应该为每个虚拟主机创建一个单独的数据库,因为这会使事情更清楚,此外,将来更容易迁移单个虚拟主机。然而,如此处所述,现在也存在只使用一个的能力。

遵循操作系统的标准 PostgreSQL 安装说明。完成此操作后,以管理员身份连接到数据库,然后:

  1. 使用 CREATE USER ejabberd WITH PASSWORD 'your_password'; 创建 ejabberd 数据库账号。别忘了更改密码,并记下来。
  2. 使用 CREATE DATABASE ejabberd_example OWNER ejabberd; 为虚拟主机创建数据库。将 example 替换为与虚拟主机相对应的内容。
  3. 退出 psql shell,并使用命令 curl -s https://raw.githubusercontent.com/processone/ejabberd/master/sql/pg.sql | sudo -u ejabberd psql ejabberd_example 从 GitHub 导入数据库模式(再次替换 example)。

首先将 hosts 下的 localhost 替换为虚拟主机(例如 example.net),然后列出之前在 certfiles 下获得的证书文件。

1hosts:
2  - example.net
3
4certfiles:
5  - "/etc/ejabberd/certs/*/*"

现在在 YAML 文件的 root 级别设置 default_db: sql。后面应该是 host_config 和虚拟主机的数据库配置,如下所示。根据设置自定义每个值。

 1host_config:
 2  example.net:
 3    sql_type: pgsql
 4    sql_server: "localhost"
 5    sql_port: 5432
 6    sql_username: "ejabberd"
 7    sql_password: "postgres_password"
 8    sql_database: "ejabberd_example"
 9    auth_method: sql
10    auth_password_format: scram

listen 下,确保每个端口上都启用了所有正确的服务,包括端口 5270(非默认)上的 s2s TLS:

 1listen:
 2  -
 3    port: 5222
 4    module: ejabberd_c2s
 5    max_stanza_size: 262144
 6    shaper: c2s_shaper
 7    access: c2s
 8    starttls_required: true
 9  -
10    port: 5223
11    tls: true
12    module: ejabberd_c2s
13    max_stanza_size: 262144
14    shaper: c2s_shaper
15    access: c2s
16    starttls_required: true
17  -
18    port: 5269
19    module: ejabberd_s2s_in
20    max_stanza_size: 524288
21  -
22    port: 5270
23    tls: true
24    module: ejabberd_s2s_in
25    max_stanza_size: 524288

接下来,启用 HTTP 服务器和 STUN/TURN 服务器模块。将 turn_ipv4_addressip 设置为服务器的 IPv4 地址。HTTP 服务器的 TLS 将关闭,因为它通过之前设置的 web 服务器进行反向代理。

 1  -
 2    port: 5443
 3    module: ejabberd_http
 4    request_handlers:
 5      /xmpp/admin: ejabberd_web_admin
 6      /xmpp/bosh: mod_bosh
 7      /xmpp/upload: mod_http_upload
 8      /xmpp/ws: ejabberd_http_ws
 9      /.well-known/host-meta: mod_host_meta
10      /.well-known/host-meta.json: mod_host_meta
11  -
12    port: 3478
13    transport: udp
14    module: ejabberd_stun
15    use_turn: true
16    turn_min_port: 49152
17    turn_max_port: 65535
18    # The server's public IPv4 address:
19    turn_ipv4_address: 0.0.0.0
20  -
21    port: 5349
22    transport: tcp
23    module: ejabberd_stun
24    use_turn: true
25    tls: true
26    turn_min_port: 49152
27    turn_max_port: 65535
28    ip: 0.0.0.0
29    turn_ipv4_address: 0.0.0.0

在 root 下设置 s2s_use_starttls: required

此时,可以设置一些 ACL。acls 只是访问控制列表,根据您的需求设置 access_rules,这将是传递给模块设置的内容。建议至少使用管理员账号。例子:

 1acl:
 2  admin:
 3    user: juliet@example.net
 4  capulet:
 5    - user: juliet@example.net
 6    - user: tybalt@example.net
 7  nurse:
 8    - user: angelica@example.net
 9
10access_rules:
11  household:
12    allow: capulet
13    allow: nurse

mod_disco 下添加滥用地址。也可以根据 XEP-0157 添加其他联系地址:

1modules:
2# ...
3  mod_disco:
4    server_info:
5    -
6      modules: all
7      name: "abuse-addresses"
8      urls: ["mailto:abuse@example.net"]

添加 mod_host_meta

1  mod_host_meta:
2    bosh_service_url: "https://@HOST@/xmpp/bosh"
3    websocket_url: "wss://@HOST@/xmpp/ws"

编辑 mod_mam,并将 assume_mam_usage 更改为 false,如果不希望默认在服务器上归档消息,则将 default 更改为 never

1  mod_mam:
2    db_type: sql
3    assume_mam_usage: never
4    default: never

添加 mod_stun_disco 以向客户端通告 STUN 服务,分别将 0.0.0.0example.net 更改为服务器的 IP 和主机名:

 1  mod_stun_disco:
 2    credentials_lifetime: 12h
 3    services:
 4        -
 5          host: 0.0.0.0
 6          port: 3478
 7          type: stun
 8          transport: udp
 9          restricted: false
10        -
11          host: 0.0.0.0
12          port: 3478
13          type: turn
14          transport: udp
15          restricted: true
16        -
17          host: turn.example.net
18          port: 5349
19          type: stuns
20          transport: tcp
21          restricted: false
22        -
23          host: turn.example.net
24          port: 5349
25          type: turns
26          transport: tcp
27          restricted: true

将主机设置为 muc 子域,否则它将尝试使用 conference.example.net。默认情况下,在 default_room_options 中设置 mam: false 将禁用服务器端消息归档。

 1  mod_muc:
 2    host: muc.example.net
 3    access:
 4      - allow
 5    access_admin:
 6      - allow: admin
 7    access_create: muc_create
 8    access_persistent: muc_create
 9    access_mam:
10      - allow
11    default_room_options:
12      mam: false
1  mod_proxy65:
2    access: local
3    max_connections: 5
1  mod_http_upload:
2    put_url: https://@HOST@/xmpp/upload
3    docroot: /var/www/ejabberdupload
4    max_size: 1073741824
5    custom_headers:
6      "Access-Control-Allow-Origin": "https://@HOST@"
7      "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
8      "Access-Control-Allow-Headers": "Content-Type"

docroot 创建文件夹,并确保它归 ejabberd 账号所有。将 max_size(最大上传大小)更改为首选值。

1  mod_pubsub:
2    access_createnode: pubsub_createnode
3    plugins:
4      - flat
5      - pep
6    force_node_config:
7      ## Avoid buggy clients to make their bookmarks public
8      storage:bookmarks:
9        access_model: whitelist

启动 ejabberd 服务器!

1sudo -u ejabberd ejabberdctl start

(根据您的操作系统和 ejabberd 的安装方式,此步骤可能有所不同。)

注册管理员用户 admin@example.net,使用密码 password

1sudo -u ejabberd ejabberdctl register admin example.net password

compliance.conversations.im 有一个合规性测试工具可以对服务器进行测试。

正确设置一切后,可以选择更改配置 root 下的 loglevel

将在 https://example.net/xmpp/admin 上提供一个管理页面。

可以使用 mod_conversejs 设置 conversejs。可能需要更新 web 服务器配置以代理新端点(下面的 /chat)。

 1listen:
 2  -
 3    port: 5443
 4    module: ejabberd_http
 5    request_handlers:
 6      /xmpp/bosh: mod_bosh
 7      /xmpp/ws: ejabberd_http_ws
 8      /chat: mod_conversejs
 9
10modules:
11  mod_conversejs:
12    websocket_url: "ws://@HOST@/xmpp/ws"
13    bosh_service_url: "https://@HOST@/xmpp/bosh"

对于每个附加虚拟主机,都应该创建一个新的数据库,并将其添加到配置的数据库部分。例如:

 1host_config:
 2  example.net:
 3    sql_type: pgsql
 4    sql_server: "localhost"
 5    sql_port: 5432
 6    sql_username: "ejabberd"
 7    sql_password: "postgres_password"
 8    sql_database: "ejabberd_net"
 9    auth_method: sql
10    auth_password_format: scram
11  example.org:
12    sql_type: pgsql
13    sql_server: "localhost"
14    sql_port: 5432
15    sql_username: "ejabberd"
16    sql_password: "postgres_password"
17    sql_database: "ejabberd_org"
18    auth_method: sql
19    auth_password_format: scram

配置文件中的声明之间不能有冲突,因此,如果在 root 的 modules 下声明了 mod_mucmod_proxy65mod_http_uploadmod_pubsub,则在 root 的 append_host_config 下为每个虚拟主机必须删除复制它们(以及虚拟主机之间的其他配置差异)。示例如下:

 1append_host_config:
 2  example.org:
 3    modules:
 4      mod_muc:
 5        host: muc.example.org
 6        access_create: org_account
 7        access_persistent: org_account
 8        access:
 9          - allow
10        access_admin:
11          - allow: admin
12        default_room_options:
13          mam: false
14      mod_proxy65:
15        access: org_account
16        max_connections: 5
17      mod_http_upload:
18        access: org_account
19        put_url: https://@HOST@/xmpp/upload
20        docroot: /var/www/ejabberdupload
21        max_size: 1073741824
22        custom_headers:
23          "Access-Control-Allow-Origin": "https://@HOST@"
24          "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
25          "Access-Control-Allow-Headers": "Content-Type"
26      mod_pubsub:
27        access_createnode: org_account
28        plugins:
29          - flat
30          - pep
31        force_node_config:
32          ## Avoid buggy clients to make their bookmarks public
33          storage:bookmarks:
34            access_model: whitelist
35  example.net:
36    modules:
37      mod_muc:
38        hosts:
39          - muc.example.net
40        access_create: net_account
41        access_persistent: net_account
42        access:
43          - allow
44        access_admin:
45          - allow: admin
46        default_room_options:
47          mam: false
48      mod_proxy65:
49        access: net_account
50        max_connections: 5
51      mod_http_upload:
52        access: net_account
53        put_url: https://@HOST@/xmpp/upload
54        docroot: /var/www/ejabberdupload
55        max_size: 1073741824
56        custom_headers:
57          "Access-Control-Allow-Origin": "https://@HOST@"
58          "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
59          "Access-Control-Allow-Headers": "Content-Type"
60      mod_pubsub:
61        access_createnode: net_account
62        plugins:
63          - flat
64          - pep
65        force_node_config:
66          ## Avoid buggy clients to make their bookmarks public
67          storage:bookmarks:
68            access_model: whitelist

如上所述,可以使用 ACL 禁用对每个虚拟主机的某些服务的访问,例如,为了防止 example.net 上的账号在 muc.example.org 上创建群聊。

在这种情况下,将 mod_stun_disco 更改为这个,并且不启用 STUN/TURN 的 listen 选项。生成认证密钥并将其与 TURN 服务器实例共享。

1  mod_stun_disco:
2    secret: "auth_secret"
3    services:
4      -
5        host: turn.example.net
6        type: stun
7      -
8        host: turn.example.net
9        type: turn

ejabberd 26.01 版本起,新增了 mod_invites 模块,允许您创建邀请令牌,您可以将这些令牌分享给他人,从而可控地允许他人创建新账号。

下面我们来探讨几种不同的配置方案,帮助您根据自身需求灵活运用这一新功能。

仅允许管理员用户,不允许常规带内注册(ibr)

1modules:
2  mod_invites: {}
3  mod_register:
4    allow_modules:
5      - mod_invites
请确保不要对 mod_register 实施如 ip_access 之类进一步的限制。
1access_rules:
2  access_create_account:
3    allow: local
4#[...]
5modules:
6  mod_invites:
7    access_create_account: access_create_account
强烈推荐配置登陆页面,因为这将极大提升用户体验。否则,您需要确保已安装兼容的客户端,并且目标系统能够处理 XMPP URI。

集成式登陆页面基于 bootstrap4 和 jQuery。由于某些 Linux 发行版的限制,无法提供其软件包管理系统已包含的库。因此,根据您的安装方式和系统配置,可能需要手动安装这些库。

在 Debian 上非常简单,只需

1sudo apt install libjs-jquery libjs-bootstrap4

为了方便起见,ejabberd 源代码分发中包含一个位于 tools/dl_invites_page_deps.sh 的脚本,您可以这样使用:

1./tools/dl_invites_page_deps.sh /usr/share/javascript

请确保您具有写入此目录的必要权限。

如果您使用 Docker 或我们的二进制镜像,这些可能已经包含在内了。

本指南后续内容将假设我们已将这些库安装到上述位置。

现在配置 ejabberd 的 http 处理程序来提供登陆页面和这些库。

 1listen:
 2  -
 3    port: 5281
 4    module: ejabberd_http
 5    request_handlers:
 6      /xmpp/invites: mod_invites
 7      /share: mod_http_fileserver
 8# [...]
 9modules:
10  mod_http_fileserver:
11    docroot: /usr/share/javascript
12  mod_invites:
13    landing_page: https://@HOST@/xmpp/invites/{{ invite.token }}
您不必从 ejabberd 的 http 处理程序来提供 /share 服务,您也可以配置您首选的 Web 服务器来实现。

如果您不想提供包含的登陆页面,您仍然可以配置外部服务。您可以自行托管,也可以使用现有服务,例如我们提供的 https://invite.joinjabber.org 服务。

在这种情况下,您必须相应地配置 landing_page 参数。

1modules:
2  mod_invites:
3    landing_page: https://invite.joinjabber.org/#{{ invite.uri|strip_protocol }}
通过外部登陆页面无法使用基于网络的注册表单。账号必须使用带内注册进行创建,但许多客户端不支持。
1sudo -u ejabberd ejabberdctl generate_invite example.net

如果您想为此账号建议用户名,您可以通过以下方式

1sudo -u ejabberd ejabberdctl generate_invite_with_username sponge_bob example.net

结果将如下所示(取决于您的配置):

1xmpp:example.net?register;preauth=fvR20OkCyXyjZoOSybZh4ebx	https://example.net/xmpp/invites/fvR20OkCyXyjZoOSybZh4ebx

第一部分是 XMPP URI,第二部分是登陆页面(仅在配置后存在)。这就是您需要传递的内容。

首先,您需要为 mod_invite 的 access_create_account 设置一个专用的访问规则,并在其中添加滥用者的账号。

1access_rules:
2  access_create_account:
3    deny:
4      - user: plankton@example.net
5    allow: local
6# [...]
7modules:
8  mod_invites:
9    access_create_account: access_create_account

如有需要,请重新加载配置。

1sudo -u ejabberdctl reload_config

现在将该账号发出的所有邀请失效。

1sudo -u ejabberdctl expire_invite_tokens plankton example.net

您可以清理过期和已使用的令牌来释放数据库空间。但请注意,此操作会移除所有可追溯信息(“谁邀请了谁”),并重置 max_invites 限制。这可能会影响您识别滥用者的能力。

1sudo -u ejabberd ejabberdctl cleanup_expired_tokens example.net

现在是时候推广这一功能了!我们提供了一份指南,介绍了如何在一些常用的 XMPP 客户端中使用此功能

这显然不是详尽的列表,如果您有任何问题或非常好的建议,请在此处联系我们: servers@joinjabber.org (网页聊天) (xmpp)


  1. 原文改编自:blos.sm ↩︎