基于Signal协议自建去中心化安全通信服务:Signal-Bastion部署指南
1. 项目概述构建一个去中心化的安全通信堡垒最近在折腾一个挺有意思的项目叫 Signal-Bastion。这名字一听就很有感觉“Bastion”是堡垒、要塞的意思而“Signal”则指向了那个以安全著称的即时通讯应用。所以这个项目的核心目标就很清晰了打造一个属于你自己的、去中心化的、高安全性的即时通讯服务。它不是要你去破解或修改官方的 Signal 应用而是让你能够基于开源的 Signal 协议搭建一个私有的、可控的通信基础设施。为什么需要这个想象一下你和你的团队、家人或是一个小社群需要讨论一些敏感的商业计划、技术细节或是私人事务。虽然市面上有很多成熟的通讯工具但数据终究是存放在别人的服务器上。而 Signal-Bastion 让你能把“服务器”搬回家或者部署在你完全信任的云服务商那里。所有的消息、通话、文件都只流经你控制的节点从根源上杜绝了第三方窥探的可能性。这不仅仅是隐私问题更是对数据主权的完全掌控。这个项目适合谁如果你是一名开发者、系统管理员、隐私安全爱好者或者是一个对数据自主权有要求的小型组织负责人那么 Signal-Bastion 就是你值得投入时间研究的玩具或者说工具。它需要你具备基础的 Linux 命令行操作能力对 Docker 和网络配置有初步了解。当然整个过程我会拆解得非常细即便你是新手跟着步骤走也能一步步把这个“堡垒”给建起来。2. 核心架构与组件选型解析2.1 为什么选择 Signal 协议作为基石在动手之前我们得先搞清楚基石是什么。Signal-Bastion 的核心是Signal 协议目前公认的端到端加密E2EE黄金标准。WhatsApp、Facebook Messenger 的私密模式等都采用了它。它的强大之处在于“双棘轮”算法每次发送消息都会更新加密密钥即使某个密钥泄露也无法解密之前或之后的消息实现了前向保密和后向保密。但官方 Signal 服务是中心化的虽然他们看不到消息内容但元数据谁在什么时候联系了谁仍然存在其服务器上。Signal-Bastion 的目标就是保留 Signal 协议无与伦比的加密安全性同时将元数据的控制权也夺回来。我们通过自建服务器使得连接关系、在线状态等元数据也只存在于我们自己的基础设施内。2.2 项目核心组件拆解不止一个服务Signal-Bastion 并非一个单一的软件它是一套服务集合的编排。根据其项目描述和常见实现模式一个完整的、可用的私有 Signal 服务通常包含以下关键组件Signal-Server (信号服务器)这是大脑和中枢。它负责用户注册、身份验证、消息路由、群组管理、联系人发现等。这是我们需要部署的核心后端服务。开源社区有几个活跃的 fork如signalapp/Signal-Server我们需要对其进行配置和部署。消息中继/推送服务为了节省手机电量移动应用通常采用推送通知来唤醒。在自建环境中我们需要部署一个兼容的推送服务如基于 Google FCM 的自建网关或使用 Apple APNs 的替代方案或者让客户端使用 WebSocket 长连接。这是确保消息能及时送达的关键。存储后端Signal-Server 需要数据库来存储非消息内容的数据如用户账号信息、群组配置、配置文件等。通常选用 PostgreSQL。切记消息内容本身是端到端加密的服务器存储的只是加密后的密文没有密钥无法解密。客户端修改/配置官方 Signal 客户端默认连接其官方服务器。我们需要使用开源的 Signal 客户端如 Signal-Android, Signal-Desktop并修改其服务器配置指向我们自建的Signal-Server地址。这是让整个系统跑通的最后一步。反向代理与 TLS对外提供服务的必须是 HTTPS。我们需要一个反向代理如 Nginx 或 Caddy来处理 SSL/TLS 终止将加密的 HTTP 流量转发给后端的 Signal-Server。容器化与编排可选但强烈推荐使用 Docker 和 Docker Compose 来管理以上所有服务可以极大简化部署、依赖管理和更新流程。smouj/Signal-Bastion这个项目很可能就是提供了一套 Docker Compose 配置模板。注意自建 Signal 服务与官方服务器是完全隔离的。你的用户只能与你服务器上的其他用户通信无法与官方 Signal 的用户互联。这是一个独立的“孤岛”网络这也是实现完全控制所必须付出的代价。2.3 我们的部署蓝图基于以上分析我们的部署将遵循以下架构一台具有公网 IP 的 VPS 或家庭服务器需配置端口转发。使用 Docker Compose 统一拉起 PostgreSQL、Signal-Server 等服务。配置 Nginx 作为反向代理并申请 SSL 证书使用 Let‘s Encrypt 免费证书。构建或配置修改后的 Signal 客户端连接我们的服务器域名。接下来我们就进入最核心的实操环节。3. 详细部署实操从零搭建你的通信堡垒3.1 基础环境准备与安全加固假设我们使用一台全新的 Ubuntu 22.04 LTS 服务器。首先进行基础安全设置。# 1. 更新系统并安装基础工具 sudo apt update sudo apt upgrade -y sudo apt install -y curl wget git vim net-tools # 2. 创建专用用户非root操作更安全 sudo adduser signaladmin sudo usermod -aG sudo signaladmin # 切换到新用户后续操作如无特别说明均在此用户下进行 su - signaladmin # 3. 配置SSH密钥登录禁用密码登录重要 # 在本地机器生成密钥对如果还没有: ssh-keygen -t ed25519 # 将本地公钥(~/.ssh/id_ed25519.pub)内容复制到服务器的 ~/.ssh/authorized_keys # 然后编辑服务器SSH配置 sudo vim /etc/ssh/sshd_config找到并修改以下行PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes重启 SSH 服务sudo systemctl restart sshd。务必在另一个终端窗口测试用密钥登录成功再关闭当前连接否则可能被锁在服务器外。3.2 核心依赖安装Docker 与 Docker Compose容器化是我们的首选方案。# 安装 Docker curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER newgrp docker # 重新加载用户组或退出重新登录 # 安装 Docker Compose (以v2为例) DOCKER_CONFIG${DOCKER_CONFIG:-$HOME/.docker} mkdir -p $DOCKER_CONFIG/cli-plugins curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose chmod x $DOCKER_CONFIG/cli-plugins/docker-compose # 验证安装 docker compose version3.3 获取与配置 Signal-Bastion 项目现在我们来处理项目本身。我们需要获取smouj/Signal-Bastion的配置。通常这类项目会提供一个docker-compose.yml模板和配置文件。# 创建一个工作目录 mkdir ~/signal-bastion cd ~/signal-bastion # 假设项目配置已整理在此我们可能需要从GitHub获取或自行创建。 # 这里我们以构建一个典型的配置结构为例。我们需要创建以下关键文件1.docker-compose.yml 服务编排的核心。version: 3.8 services: postgres: image: postgres:15-alpine container_name: signal-postgres restart: unless-stopped environment: POSTGRES_DB: signal POSTGRES_USER: signal POSTGRES_PASSWORD: your_strong_postgres_password_here # 务必更改 volumes: - postgres_data:/var/lib/postgresql/data networks: - signal-network signal-server: # 注意官方Signal-Server镜像可能需要自行构建或使用社区维护的镜像。 # 这里假设我们使用一个名为 signal-server 的本地构建镜像。 build: ./signal-server # 指向包含Dockerfile的目录 container_name: signal-server restart: unless-stopped depends_on: - postgres environment: # 这里需要大量配置我们放在下面的配置文件中 SPRING_CONFIG_LOCATION: file:/etc/signal/application.yml # 数据库连接环境变量也可在yml中配置 DATABASE_URL: jdbc:postgresql://postgres:5432/signal DATABASE_USERNAME: signal DATABASE_PASSWORD: your_strong_postgres_password_here volumes: - ./config/application.yml:/etc/signal/application.yml:ro - signal-uploads:/var/lib/signal/uploads networks: - signal-network # 暂时只暴露给内部网络由Nginx对外 nginx: image: nginx:alpine container_name: signal-nginx restart: unless-stopped ports: - 80:80 - 443:443 volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/ssl:/etc/nginx/ssl:ro - ./data/certbot/conf:/etc/letsencrypt:ro - ./data/certbot/www:/var/www/certbot:ro depends_on: - signal-server networks: - signal-network certbot: image: certbot/certbot container_name: signal-certbot volumes: - ./data/certbot/conf:/etc/letsencrypt - ./data/certbot/www:/var/www/certbot # 此服务仅用于初次申请和续期证书平时不常运行 command: certonly --webroot -w /var/www/certbot --keep-until-expiring --agree-tos --email your-emailexample.com -d your.signal.domain.com --non-interactive networks: signal-network: driver: bridge volumes: postgres_data: signal-uploads:2../signal-server/Dockerfile 构建 Signal-Server 镜像。由于官方没有提供现成的 Docker 镜像我们需要自己构建。# 使用多阶段构建减少镜像体积 FROM openjdk:17-jdk-slim AS builder WORKDIR /build # 克隆 Signal-Server 源码这里以其中一个活跃fork为例 RUN apt-get update apt-get install -y git \ git clone https://github.com/signalapp/Signal-Server.git . \ # 切换到稳定版本标签例如 v6.xx.x git checkout $(git describe --tags git rev-list --tags --max-count1) # 构建项目这可能需要一段时间和较多内存 RUN ./gradlew clean build -x test # 运行阶段 FROM openjdk:17-jdk-slim WORKDIR /app # 复制构建产物 COPY --frombuilder /build/service/target/TextSecureServer-*.jar /app/signal-server.jar # 复制配置文件模板可选 # COPY application.yml /etc/signal/ # 运行非root用户 RUN useradd -m -u 1000 signal chown -R signal:signal /app USER signal EXPOSE 8080 ENTRYPOINT [java, -jar, /app/signal-server.jar]3../config/application.yml Signal-Server 的核心配置文件。这是最复杂的一步。# 示例配置关键部分需要你自定义 server: port: 8080 forward-headers-strategy: framework spring: datasource: url: jdbc:postgresql://postgres:5432/signal username: signal password: your_strong_postgres_password_here hikari: maximum-pool-size: 10 redis: # 如果使用Redis做缓存 host: localhost port: 6379 jackson: property-naming-strategy: SNAKE_CASE signal: server: # 你的服务器公网可访问的URL用于生成附件链接等 url: https://your.signal.domain.com # 客户端将连接到此地址进行注册和消息发送 host: 0.0.0.0 port: 8080 # 消息推送配置关键且复杂 # 由于无法使用官方FCM/APNs这里通常配置为“直接传递”或使用替代推送服务 push: enabled: true # 示例使用WebSocket进行推送需要客户端支持 # 或者可以配置一个自建的推送中继如使用Signal的“generic web socket”推送器 # 这里是一个简化配置实际需要根据你选择的推送方案调整 public-key: file:/etc/signal/push-public.key private-key: file:/etc/signal/push-private.key # 附件存储配置本地存储示例 attachments: store: file filesystem: directory: /var/lib/signal/uploads # 短信/语音验证码自建无法使用Twilio等商业服务需跳过或模拟 # 对于小型私有网络可以考虑禁用短信验证使用预共享密钥或内部账号系统 twilio: account-sid: “” auth-token: “” messaging-service-sid: “” # 账号相关配置 account: # 允许的注册国家代码为空表示允许所有 allowed-country-codes: [] # 是否允许新用户注册 registration: enabled # 日志配置 logging: level: org.whispersystems.textsecuregcm: INFO4../nginx/conf.d/signal.conf Nginx 反向代理配置。upstream signal_backend { server signal-server:8080; } server { listen 80; server_name your.signal.domain.com; # 重定向所有HTTP流量到HTTPS location / { return 301 https://$server_name$request_uri; } # 用于Certbot验证 location /.well-known/acme-challenge/ { root /var/www/certbot; } } server { listen 443 ssl http2; server_name your.signal.domain.com; ssl_certificate /etc/nginx/ssl/live/your.signal.domain.com/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/live/your.signal.domain.com/privkey.pem; # 使用现代SSL配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...; ssl_prefer_server_ciphers off; # 增大客户端body大小用于上传附件 client_max_body_size 50M; location / { proxy_pass http://signal_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Signal Server 可能需要这些头部 proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } }3.4 生成密钥与申请SSL证书Signal 协议和服务器内部通信需要密钥。# 在项目根目录下创建keys目录 mkdir -p keys config cd keys # 1. 生成推送服务的VAPID密钥对用于WebPush如果采用此方案 openssl ecparam -name prime256v1 -genkey -noout -out push_private_key.pem openssl ec -in push_private_key.pem -pubout -out push_public_key.pem # 将私钥和公钥复制到config目录供Docker挂载 cp push_private_key.pem ../config/ cp push_public_key.pem ../config/ # 2. 申请SSL证书使用Certbot Docker容器 # 首先确保你的域名 your.signal.domain.com 的A记录已指向服务器IP。 # 然后运行一次certbot服务来申请证书需要先启动nginx的80端口服务 cd ~/signal-bastion # 先启动一个临时的nginx服务仅开放80端口用于验证域名所有权 docker compose up -d nginx # 运行certbot容器申请证书 docker compose run --rm certbot # 申请成功后证书会保存在 ./data/certbot/conf/live/your.signal.domain.com/ 下 # 创建nginx的ssl目录并链接证书 sudo mkdir -p nginx/ssl sudo ln -s ../../data/certbot/conf/live/your.signal.domain.com ./nginx/ssl/3.5 构建并启动所有服务现在万事俱备。# 1. 构建Signal-Server镜像这步耗时较长 cd ~/signal-bastion docker compose build signal-server # 2. 启动所有服务PostgreSQL, Signal-Server, Nginx docker compose up -d # 3. 查看日志确认服务运行正常 docker compose logs -f signal-server # 你应该看到类似“Started Application in XX seconds”的日志没有大量ERROR。 # 4. 验证服务 curl -k https://your.signal.domain.com/v1/accounts/version # 如果返回一个JSON说明服务器基本运行正常。4. 客户端配置与连接打通最后一公里服务器跑起来了但手机上的 App 还不知道它的存在。我们需要一个修改版的 Signal 客户端。4.1 获取并修改 Signal 客户端以 Android 为例官方客户端代码在 GitHub 上但直接编译连接自建服务器需要修改配置。克隆仓库git clone https://github.com/signalapp/Signal-Android.git cd Signal-Android修改服务器配置 关键文件是app/src/main/res/values/network_config.xml。你需要找到类似SIGNAL_URL、SIGNAL_CDN_URL、SIGNAL_CONTACT_DISCOVERY_URL等配置项将它们指向你的服务器地址。!-- 示例修改 -- string nameSIGNAL_URL” translatable“false”https://your.signal.domain.com/string string nameSIGNAL_CDN_URL” translatable“false”https://your.signal.domain.com/string string nameSIGNAL_CDN2_URL” translatable“false”https://your.signal.domain.com/string string nameSIGNAL_CONTACT_DISCOVERY_URL” translatable“false”https://your.signal.domain.com/string string nameSIGNAL_KEY_BACKUP_URL” translatable“false”https://your.signal.domain.com/string string nameSIGNAL_STORAGE_URL” translatable“false”https://your.signal.domain.com/string注意修改客户端代码并重新签名是一个复杂的过程涉及 Android 开发环境搭建Gradle, JDK、依赖下载和可能的代码适配。对于新手可以寻找社区已经编译好的、支持自定义服务器地址的客户端 APK但务必从可信来源获取以防恶意代码。构建 APK 配置好 Android SDK 和 NDK 后使用./gradlew assemblePlayRelease或assembleDebug进行构建。这个过程对机器资源要求较高。4.2 注册与验证账号由于我们无法使用真实的 SMS 或语音通话进行验证Twilio 服务需要商业账户和费用我们需要在自建服务器上绕过或模拟验证。方法一在服务器配置中禁用短信验证仅用于封闭测试环境在application.yml中可以尝试将signal.server.twilio相关配置留空并寻找是否有关闭验证的开关不同版本的 Signal-Server 可能配置不同。更常见的方法是使用“验证码发放接口”。方法二使用服务器内部接口直接发放验证码推荐用于小型部署Signal-Server 提供了一个/v1/accounts/sms/code/{number}的 API 端点用于测试。你可以直接调用它来获取验证码然后在客户端输入。# 向号码1234567890请求发送验证码该号码需符合E.164格式 curl -X POST “https://your.signal.domain.com/v1/accounts/sms/code/1234567890” # 查看服务器日志验证码会以明文形式打印在日志中仅限开发/测试环境。 docker compose logs signal-server | grep “verification code”在客户端输入这个验证码即可完成注册。4.3 实际通信测试在修改后的客户端 A 上使用上述方法注册一个账号如号码1234567890。在另一台设备或模拟器上的客户端 B 上注册另一个账号如1234567891。在客户端 A 上将客户端 B 的号码添加为联系人。由于自建服务器没有全球号码数据库你需要手动输入完整号码。尝试发送一条文本消息。如果一切配置正确消息应该能加密并送达。进一步测试图片、语音消息的发送。5. 运维、问题排查与安全加固5.1 日常运维要点日志监控定期检查docker compose logs输出关注错误和警告。数据备份定期备份 PostgreSQL 数据卷 (postgres_data) 和上传的文件卷 (signal-uploads)。# 简单备份示例 docker compose exec postgres pg_dump -U signal signal backup_$(date %Y%m%d).sql证书续期Let‘s Encrypt 证书有效期为90天。可以设置一个 Cron 任务定期续期。# 在crontab中添加每月1号凌晨2点执行续期 0 2 1 * * cd /home/signaladmin/signal-bastion docker compose run --rm certbot renew docker compose exec nginx nginx -s reload更新关注 Signal-Server 源码仓库的安全更新。更新时需要重新构建signal-server镜像并重启服务。5.2 常见问题与排查实录问题1客户端无法连接服务器提示“网络错误”或“无法注册”。排查检查服务器防火墙是否开放了 80 和 443 端口sudo ufw status。检查 Nginx 容器是否正常运行docker compose ps。检查 Nginx 配置语法docker compose exec nginx nginx -t。在服务器上 curl 内部服务curl http://signal-server:8080/v1/accounts/version确认 Signal-Server 本身是否健康。检查客户端配置的域名是否正确以及手机网络是否能解析该域名。问题2消息发送成功但对方收不到。排查这是推送问题的典型表现。检查 Signal-Server 日志看是否有关于推送失败的错误如PushNotificationManager相关错误。确认你配置的推送方案如 WebSocket在客户端和服务器端是否都已正确实现和启用。自建推送是最大的技术难点之一。让接收方客户端切换到前台检查是否能收到“离线期间”的消息。如果能则确认是推送问题。问题3无法发送图片或大文件。排查检查 Nginx 配置中的client_max_body_size是否足够大如50M。检查signal-uploads卷的磁盘权限和空间。查看 Signal-Server 日志中附件处理相关的错误。问题4注册时收不到验证码日志里也没有。排查确认调用了正确的测试接口 (/v1/accounts/sms/code/...)。检查application.yml中关于账号和验证的配置项。某些版本可能需要显式开启测试模式或配置一个“假”的短信服务。查看 Signal-Server 启动日志确认相关模块如AccountController是否加载正常。5.3 安全加固建议最小化暴露面除了 80/443 端口不应将其他任何端口如 PostgreSQL 的 5432暴露到公网。定期更新及时更新 Docker 基础镜像、PostgreSQL、Nginx 以及 Signal-Server 源码修复安全漏洞。使用强密码为 PostgreSQL 数据库设置高强度密码并在application.yml和docker-compose.yml中使用环境变量或密钥管理服务避免硬编码。网络隔离在 Docker Compose 中使用自定义网络 (signal-network)仅让 Nginx 能访问 Signal-Server 和 PostgreSQL实现容器间网络隔离。审计日志将 Docker 容器日志导出到集中的日志管理系统如 Loki Grafana便于审计和分析异常行为。客户端安全确保使用的修改版客户端来自可信的构建源最好是自己从源码编译。避免使用来历不明的 APK 文件。搭建并维护一个私有的 Signal-Bastion 是一项有挑战但回报丰厚的工作。它不仅仅是一个通讯工具更是一次对现代加密通信架构的深度实践。你会对端到端加密、推送服务、分布式系统有更直观的理解。最大的坑往往在推送和客户端适配环节需要耐心调试。一旦跑通那种所有数据流都在自己指尖掌控的感觉是使用任何商业服务都无法替代的。如果只是小范围使用甚至可以简化推送让客户端更多地依赖轮询或保持长连接虽然耗电一些但架构上会简单很多。