Loading... # XMPP 服务器搭建指南:使用 Prosody 与 Coturn 实现语音通话 # 一、概述 ## 1. 简介 ### A. 是什么 XMPP(Extensible Messaging and Presence Protocol)是一种基于 XML 的即时通讯协议,诞生于 1999 年。它采用联邦化架构,允许不同服务器之间自动互联,用户不会被锁定在单一服务提供商上。本指南介绍如何使用 Prosody XMPP 服务器、Coturn TURN/STUN 服务器和 Docker 容器技术,搭建一套功能完整的即时通讯系统。 ### B. 为什么学 - 数据主权:消息存储在自己控制的硬件上,不依赖第三方公司 - 联邦互通:可与任何 XMPP 服务器的用户通讯,不受平台限制 - 协议成熟:经过 20 多年发展,协议稳定可靠,不会突然消失 - 功能完整:支持文本消息、文件分享、群组聊天、语音视频通话 ### C. 学完能做什么 - 部署完整的 XMPP 服务器,支持联邦互通 - 配置 OMEMO 端到端加密保护通讯隐私 - 实现 HTTP 文件上传功能 - 搭建 TURN/STUN 服务器支持语音视频通话 ## 2. 前置知识 ### A. 必备技能 - 基本 Linux 命令行操作 - Docker 和 Docker Compose 基础知识 - DNS 记录配置(SRV、A、CNAME) - TLS 证书管理 ### B. 推荐知识 - 反向代理配置(Caddy 或 Nginx) - 防火墙规则设置 - XMPP 协议基础概念 # 二、环境准备 ## 1. 系统要求 - 服务器已安装 Docker 和 Docker Compose - 拥有可控制的域名 - 服务器可访问公网,开放必要端口 ## 2. 网络架构 ```mermaid graph LR Client1[客户端1] -->|5222| Prosody[Prosody XMPP] Client2[客户端2] -->|5222| Prosody Prosody -->|5269| FedServer[联邦服务器] Prosody -->|内部| Caddy[Caddy反向代理] Caddy -->|443| Client1 Prosody -->|凭证| Coturn[Coturn TURN/STUN] Client1 -.|NAT穿透|-> Coturn Client2 -.|NAT穿透|-> Coturn ```  ## 3. 核心组件 - **Prosody**:轻量级 XMPP 服务器,支持模块化扩展 - **Coturn**:TURN/STUN 服务器,处理 NAT 穿透 - **Caddy**:反向代理,处理 HTTPS 终止 - **Let's Encrypt**:提供 TLS 证书 # 三、DNS 配置 ## 1. SRV 记录 XMPP 使用 SRV 记录让客户端和其他服务器发现你的服务: ```dns _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**:服务器间联邦连接端口 ## 2. A 记录 ```dns xmpp.example.com. A 你的服务器公网IP ``` ## 3. 可选子域名 如果需要 HTTP 文件上传和群组聊天,添加以下记录: ```dns upload.xmpp.example.com. CNAME xmpp.example.com. conference.xmpp.example.com. CNAME xmpp.example.com. ``` 注意:conference 子域名也可由 Prosody 内部处理,无需单独配置。 # 四、TLS 证书配置 ## 1. 证书获取 Prosody 启动必须有有效证书。推荐使用 Let's Encrypt 配合 DNS 验证,无需开放 80 端口: ```bash 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 ``` ## 2. Cloudflare API 配置 cloudflare.ini 文件包含 API Token: ```ini dns_cloudflare_api_token = your-cloudflare-api-token ``` ## 3. 权限设置 证书生成后,设置权限让 Prosody 可读取: ```bash chmod -R 755 ~/docker/xmpp/certs/live/ ~/docker/xmpp/certs/archive/ chmod 644 ~/docker/xmpp/certs/archive/xmpp.example.com/*.pem ``` ## 4. 自动续期 设置每月 1 号凌晨 3 点自动续期: ```bash 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 部署 ## 1. docker-compose.yml Prosody 容器配置: ```yaml services: 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: ``` ## 2. 端口说明 - **5222**:客户端连接 - **5269**:服务器联邦 - 数据卷持久化用户账户和消息存档 # 六、Prosody 配置详解 ## 1. 核心模块配置 ```lua modules_enabled = { -- 核心功能 "roster"; -- 联系人列表 "saslauth"; -- 认证 "tls"; -- 加密传输 "dialback"; -- 服务器验证 "disco"; -- 服务发现 "posix"; -- POSIX 系统功能 "ping"; -- 连接保活 "register"; -- 注册(可禁用) "time"; -- 时间 "uptime"; -- 运行时间 "version"; -- 版本信息 -- 安全 "blocklist"; -- 黑名单 -- 多设备和移动端优化 "carbons"; -- 消息同步 "csi_simple"; -- 客户端状态指示 "smacks"; -- 流管理(可靠传输) "cloud_notify"; -- 推送通知 -- 消息存档 "mam"; -- 消息归档管理 -- 用户资料和状态 "vcard_legacy"; -- 电子名片 "pep"; -- 个人事件协议 "bookmarks"; -- 书签 -- 管理工具 "admin_shell"; -- 管理Shell } ``` ## 2. 移动端关键模块 以下模块对移动端体验至关重要: | 模块 | 功能 | 作用 | |------|------|------| | carbons | 消息同步 | 将消息同步到所有在线设备,而非只发送给某一台设备 | | smacks | 流管理 | 优雅处理不稳定连接,防止信号丢失时消息丢失 | | cloud_notify | 推送通知 | 允许移动客户端无需持久连接,大幅节省电量 | | mam | 消息归档 | 服务端存储历史消息,支持搜索和跨设备同步 | ## 3. 安全设置 ```lua c2s_require_encryption = true s2s_require_encryption = true s2s_secure_auth = true authentication = "internal_hashed" allow_registration = false ``` 配置说明: - 所有连接强制加密 - 禁用开放注册,使用 prosodyctl 手动创建账户 - 启用 s2s_secure_auth 会拒绝自签名或错误配置证书的服务器连接 - 虽然会失去与部分配置不当服务器的联邦能力,但隐私优先级更高 ## 4. OMEMO 端到端加密 TLS 加密传输中的消息,但服务器本身仍可读取。OMEMO 在此基础上增加端到端加密,确保即使是服务器管理员也无法查看消息内容。 OMEMO 基于 Signal 使用的相同加密技术,Monal、Conversations、Gajim 等客户端均支持,且通常默认启用。建议对所有对话开启 OMEMO。 服务端无需额外配置,OMEMO 完全由客户端处理。 ## 5. 消息归档配置 ```lua archive_expires_after = "1y" default_archive_policy = true ``` 消息保留 1 年,默认开启归档。客户端可按对话选择退出。 ## 6. HTTP 文件上传 ```lua http_interfaces = { "*" } http_ports = { 5280 } https_ports = { } http_external_url = "https://xmpp.example.com" ``` Prosody 在 5280 端口提供 HTTP 服务,HTTPS 由 Caddy 反向代理处理。http_external_url 告诉 Prosody 向客户端提供什么 URL 用于文件上传。 ## 7. 虚拟主机和组件 ```lua 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 days http_external_url = "https://xmpp.example.com" ``` 组件说明: - **MUC 组件**:提供群组聊天,muc_mam 提供消息历史 - 限制房间创建权限为本地用户 - **文件分享组件**:处理图片和文件上传,10 MB 限制,30 天过期 # 七、Caddy 反向代理配置 ## 1. Caddyfile ```caddyfile xmpp.example.com { reverse_proxy xmpp:5280 } ``` ## 2. 工作流程 客户端发送图片时,Prosody 返回形如 https://xmpp.example.com/upload/... 的 URL,接收方客户端通过 HTTPS 获取文件。 ```mermaid sequenceDiagram participant C as 客户端 participant P as Prosody participant Ca as Caddy participant R as 接收方 C->>P: 上传图片(HTTP/5280) P->>C: 返回上传URL C->>Ca: 上传文件(HTTPS/443) Ca->>P: 转发到Prosody C->>R: 发送消息含URL R->>Ca: 下载文件(HTTPS/443) Ca->>P: 转发请求 ```  # 八、用户管理 ## 1. 创建账户 禁用开放注册后,通过命令行创建账户: ```bash docker exec -it xmpp prosodyctl adduser danny@xmpp.example.com ``` 系统提示输入密码,完成后即可使用任何 XMPP 客户端登录。 # 九、防火墙配置 ## 1. XMPP 端口 ```bash sudo ufw allow 5222 comment 'XMPP client' sudo ufw allow 5269 comment 'XMPP federation' ``` ## 2. HTTP/HTTPS 端口 ```bash sudo ufw allow 80 comment 'HTTP' sudo ufw allow 443 comment 'HTTPS' ``` ## 3. 路器端口转发 如果服务器在路由器后,需转发 5222 和 5269 端口。 # 十、语音视频通话配置 ## 1. TURN/STUN 服务器 文本和文件分享到此已可工作。语音视频通话需要额外部署 TURN/STUN 服务器,没有它,NAT 后的客户端无法建立直接媒体连接。 ## 2. 生成共享密钥 ```bash openssl rand -hex 32 ``` ## 3. Coturn Docker 配置 ```yaml services: coturn: image: coturn/coturn:latest container_name: coturn restart: unless-stopped network_mode: host volumes: - ./turnserver.conf:/etc/coturn/turnserver.conf:ro tmpfs: - /var/lib/coturn ``` 使用 network_mode: host 是因为 TURN 需要真实网络接口处理 NAT 穿透,Docker 端口映射会破坏此功能。 ## 4. Coturn 配置文件 ```ini listening-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 ``` ## 5. NAT 配置 如果服务器在 NAT 后,添加: ```ini external-ip=YOUR_PUBLIC_IP/YOUR_PRIVATE_IP ``` ## 6. Prosody 集成 在 modules_enabled 中添加 turn_external,并在 VirtualHost 块中配置: ```lua turn_external_host = "xmpp.example.com" turn_external_port = 3478 turn_external_secret = "YOUR_SECRET_HERE" ``` ## 7. 防火墙规则 ```bash sudo ufw allow 3478 comment 'STUN/TURN' sudo ufw allow 5349 comment 'TURNS' sudo ufw allow 49152:49200/udp comment 'TURN relay' ``` ## 8. 验证配置 ```bash docker exec xmpp prosodyctl check turn ``` # 十一、客户端推荐 ## 1. 移动端 | 平台 | 客户端 | 特点 | |------|--------|------| | iOS | Monal | 开源,支持现代 XEP,推送通知良好 | | Android | Conversations | 推荐,功能完整 | ## 2. 桌面端 | 平台 | 客户端 | 特点 | |------|--------|------| | Linux | Gajim | 功能全面 | | Windows | Gajim | 跨平台支持 | | macOS | Monal | 与 iOS 版本同步 | 所有推荐客户端均支持 OMEMO 加密、文件分享、群组聊天和语音视频通话。 # 十二、系统验证 ## 1. Prosody 内置诊断 ```bash docker exec xmpp prosodyctl check ``` 此命令检查 DNS 记录、TLS 证书、连接性和模块配置,错误信息非常 helpful。 ## 2. 在线合规测试 使用 [XMPP Compliance Tester](https://compliance.conversations.im/),配置正确后应得分 90% 以上。 # 十三、完整架构图 ```mermaid graph TB subgraph 客户端层 iOS[iOS/Monal] Android[Android/Conversations] Desktop[桌面/Gajim] end subgraph 网络层 Caddy[Caddy 443] XMPPc[XMPP 5222] XMPPs[XMPP 5269] TURN[TURN 3478] TURNS[TURNS 5349] TURNRelay[TURN Relay 49152-49200] end subgraph 服务层 Prosody[Prosody容器] Coturn[Coturn容器] end subgraph 数据层 Data[prosody-data卷] Certs[证书挂载] Config[配置挂载] end iOS -->|443| Caddy Android -->|443| Caddy Desktop -->|443| Caddy iOS -->|5222| XMPPc Android -->|5222| XMPPc Desktop -->|5222| XMPPc XMPPs -.联邦.-> FedServer[其他XMPP服务器] Caddy -->|5280| Prosody XMPPc -->|5222| Prosody XMPPs -->|5269| Prosody iOS -.NAT穿透.-> TURN Android -.NAT穿透.-> TURN Desktop -.NAT穿透.-> TURN iOS -.NAT穿透.-> TURNS Android -.NAT穿透.-> TURNS iOS -.媒体中继.-> TURNRelay Android -.媒体中继.-> TURNRelay TURN --> Coturn TURNS --> Coturn TURNRelay --> Coturn Prosody -->|数据| Data Prosody -->|证书| Certs Prosody -->|配置| Config ```  # 十四、总结 ## 1. 部署成果 整个系统运行在两个小型 Docker 容器和一条反向代理规则中,提供: - 即时消息和联邦互通 - HTTP 文件上传 - 消息归档和搜索 - 移动端推送通知 - 群组聊天 - 语音视频通话 ## 2. 使用建议 - Signal 仍是日常对话的首选 - XMPP 作为备用方案,确保不被单一服务锁定 - 可与任何 XMPP 服务器的用户通讯 ## 3. 部署难度 - 如果已有 Docker 服务器,这是一个合适的周末项目 - 配置虽然复杂,但 Prosody 的错误提示非常 helpful - 验证工具完善,易于排查问题 *** ## 参考资料 1. [Running My Own XMPP Server](https://blog.dmcc.io/journal/xmpp-turn-stun-coturn-prosody/) 最后修改:2026 年 02 月 17 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏