Skip to main content

在 Synology 上部署 Trojan(Xray)

由于还是不时会去 HK/CN 出差,因此还是需要一个科学上网服务。尽管有专业机场,但是敏感服务还是从个人线路走更合适。同时也需要一个 Tailscale 的备份可以连回家中。这篇记录我在 Synology (代号 SO)上部署 Trojan 代理的全过程。目标是稳定、可审阅、可回滚,并配套“一键”脚本完成部署、更新、验证、日志分析与本地客户端测试,大部分代码由 Codex 自动生成。

目的与约束 #

  • 使用自有域名 foo.example.im 提供 Trojan 服务。
  • 走家庭网络:路由器将公网端口转发到 NAS。
  • 非 root 运行容器,避免不必要的权限。
  • 自动化:配置先渲染、后审阅、再批准部署。证书全流程也都自动化。
  • 可验证:本机、NAS 端、通过外部 remote 远端均能自动化检测并输出清晰结果,验证可访问性。

选型 #

首选默认还是 ss,之前家里也一直都跑着,部署方便(即便是混淆),但是 ss 的综合可用性已经都被其他协议超越,因此还是希望尝试新的方式。先是尝试 ss + ShadowTLS 的方式,使用 sing-box 部署,结果每几个小时就因为 goroutines 堆积而 crash,并且瞬发流量一大也会 crash。后来和 ChatGPT 一番对话,加上之前常用的专业机场基本也都使用 Trojan 我相信在规模化运行时所有小概率问题都会放大为确定性问题,既然专业平台都使用 Trojan,那么就选择它吧。

  • 核心实现:Xray-core(ghcr.io/xtls/xray-core
    • 原生支持 Trojan inbound,生态活跃、协议扩展灵活(后续可切 VLESS/Reality 等)。
  • 证书:acme.sh + Cloudflare DNS-01
    • 无需对外开放 80/443,适合家宽环境。
  • 客户端测试:sing-box(本机 SOCKS5 验证),兼容多平台。

备选对比:

  • trojan-go:轻量,但不如 Xray 的路由/传输栈丰富。
  • 纯 Trojan:功能单一,后续扩展不足。

设计 #

  • 端口与映射
    • 容器监听 12345(注意使用非特权端口,避免 DSM 需要 sudo 运行脚本,导致自动化复杂度)
    • NAS 暴露 22333,映射到容器 12345
    • 路由器将公网 54321 → NAS:22333
  • 证书
    • DNS-01 申请,安装到 $TROJAN_REMOTE_DIR/cert/{fullchain.pem,privkey.pem}
    • 容器挂载只读 /etc/xray/cert
  • 权限
    • 以 DSM 用户身份运行容器:PUID=1026PGID=100
    • 私钥权限 600
  • 配置方式
    • 单文件 /etc/xray/config.json
    • 强制 entrypoint:xray run -c /etc/xray/config.json
flowchart LR User -- Trojan TLS:54321 --> Internet Internet --> Router[Router NAT] Router -- DNAT 54321→22333 --> NAS[Synology:22333] NAS -- Docker map 22333→12345 --> Xray[Xray Container:12345] Xray --> Freedom[Freedom Outbound]

实施过程(关键步骤) #

  1. 环境与目录约定
  • .env 约定:
    • NAS_HOST=192.168.x.yyy
    • TROJAN_DOMAIN=foo.example.com
    • TROJAN_WAN_PORT=54321TROJAN_HOST_PORT=22333TROJAN_CONTAINER_PORT=12345
    • TROJAN_UID=1026TROJAN_GID=100
    • TROJAN_PASSWORD=<强密码>
  • 证书目录:/volumeX/docker/trojan/cert,存储在 NAS 目录中。
  1. 证书签发(Cloudflare DNS-01)
  • make trojan.cert
    • 读取 CF_Token/CF_Account_ID/CF_Zone_ID,使用 acme.sh 申请并安装证书到 NAS 目录。
    • 默认不重启容器;如需自动重载,设 TROJAN_CERT_RELOAD=1
  1. 渲染与部署
  • 渲染配置:make trojan.render(输出 build/trojan/config.json
  • 一键部署:make trojan.deploy APPROVE=1
    • 上传 config 与 compose;如支持 Compose,则 up -d;否则回落到 docker run(附 --entrypoint xray 与端口/卷映射)。
  • 更新镜像:make trojan.update
  • 重新应用配置:make trojan.redeploy APPROVE=1
  • 仅重启:make trojan.restart
  1. 验证与诊断
  • 服务器侧体检:make trojan.verify
    • 检查容器状态、端口、NAS 证书、容器挂载(严格校验 /etc/xray/cert <- $TROJAN_REMOTE_DIR/cert)、本机 TLS 探测、日志关键词扫描。
  • 远端体检:make trojan-verify-remote
    • 从远程机器对 foo.example.com:54321 做 DNS/TCP/TLS 验证。
  • 详细 TLS 日志:make trojan-verify-verbose
    • 临时将 loglevel=info,重启收集 TLS/证书日志后恢复原配置。
  • 客户端测试(sing-box):make trojan.singbox FLAGS="--probe"
    • 生成 sing-box 配置,在远程机器起 SOCKS5 代理 127.0.0.1:1080,用 curl 验证经过了代理访问;可指定 --dns/--server-ip
  1. 日志分析
  • make trojan.analyze
    • 输出总览、错误数、Accepted 连接数
    • Top 目的站/端口/来源 IP 排名
    • 示例“accepted …”行,方便人工核对

关键配置片段 #

Xray inbound(Trojan,容器内端口 12345):

{
  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 12345,
      "protocol": "trojan",
      "settings": {
        "clients": [{ "password": "YOUR_PASSWORD" }]
      },
      "streamSettings": {
        "security": "tls",
        "tlsSettings": {
          "alpn": ["http/1.1"],
          "certificates": [
            {
              "certificateFile": "/etc/xray/cert/fullchain.pem",
              "keyFile": "/etc/xray/cert/privkey.pem"
            }
          ]
        }
      }
    }
  ],
  "outbounds": [{ "protocol": "freedom" }]
}

Compose 运行参数(要点):

services:
  xray:
    image: ghcr.io/xtls/xray-core:latest
    user: "${TROJAN_UID}:${TROJAN_GID}"
    entrypoint: ["xray"]
    command: ["run", "-c", "/etc/xray/config.json"]
    ports:
      - "${TROJAN_HOST_PORT}:${TROJAN_CONTAINER_PORT}"
    volumes:
      - ${TROJAN_REMOTE_DIR}/config.json:/etc/xray/config.json:ro
      - ${TROJAN_REMOTE_DIR}/cert:/etc/xray/cert:ro
    restart: unless-stopped

遇到的问题与解决 #

  • 在 DSM 上 Docker 命令不可用/路径不一致(Container Manager)
    • 远端自动探测 Docker/Compose 路径(含 /var/packages/...),必要时用 sudo。
  • scp 失败(SFTP 关闭/策略限制)
    • 回退到 SSH 流式上传(cat → ssh → 重定向),目录用 tar 流。
  • Xray 默认读取 confdir
    • 显式 xray run -c /etc/xray/config.json,强制读取挂载的单文件配置。
  • 证书读取权限被拒绝
    • 使用容器 user: 1026:100 与 NAS 文件属主一致;私钥 600
  • 容器内绑定 443 被拒绝(非 root)
    • 采用容器内 12345 + NAS 2233 + 路由 54321 映射链路。
  • 多次输入 SSH 口令
    • 启用 SSH 连接复用(ControlMaster)与可选临时 ssh-agent,“一次输入,多次复用”。

小结 #

最终在远程机器上测试 Youtube 2160p 毫无压力,测速也都达到了带宽满速。完成后的脚本命令列表如下:

(base) ➜  trojan git:(main)  make
make nas.check - Check SSH, Docker, directories on NAS
make nas.setup-ssh-key - Configure SSH public key login to NAS
make trojan.render - Render Xray/Trojan config locally for review
make trojan.deploy - Deploy container (APPROVE=1 uploads & runs)
make trojan.status - Show container status and key info
make trojan.logs - Tail/show logs (SINCE=, TAIL=, FOLLOW=1)
make trojan.update - Pull image and update/restart
make trojan.restart - Restart container without pulling image
make trojan.redeploy - Re-render config, upload, restart
make trojan.analyze - Log summary
make trojan.cert - Issue & install cert via acme.sh (DNS-01)
make trojan.verify - Server-side checks: status, certs, ports, TLS probe
make trojan.verify-remote - Client-side DNS/TCP/TLS checks from this machine
make trojan.verify-verbose - Temporarily set loglevel=info, restart, capture TLS logs, restore
make trojan.singbox - Generate sing-box client config (default SOCKS5 127.0.0.1:1080)

Examples:
  make nas-check
  make trojan-render && less build/trojan/config.json
  make trojan-deploy APPROVE=1
  make trojan-logs SINCE=2h FOLLOW=1
  make trojan-analyze SINCE=24h

脚本覆盖了 Synology 路径/权限、Compose 差异、证书获取与部署、入口命令覆盖、端口映射与非 root 运行、远端/本地体检与日志分析,基本囊括运维关键细节。