Skip to content

Repository files navigation

SubStore 节点域名更换工具

对 Sub-Store 订阅中的节点进行双栈探测,把可达节点的 server 替换为 Cloudflare 上分配的随机中性域名(一个节点对应一个域名), 再把替换后的订阅同步回 Sub-Store,客户端即可使用域名入站(隐藏真实 IP)。

  • 数据库驱动(SQLite):节点身份、探测历史、域名映射全部持久化,幂等可恢复
  • 增量探测:新节点 / 入站 IP 变化 / 超 24h 未探测时触发;ws、xhttp 等传输层跳过探测
  • 默认每周一次全量探测(可配置)
  • 可预留前端主域名(如 ridge.example.com),工具绝不会占用或改动其 DNS 记录
  • 一键 Docker 部署:Sub-Store + 探测同步容器

目录结构

.
├── docker-compose.yml     # 一键部署(sub-store + node-domain)
├── Dockerfile             # node-domain 镜像(内置 cron 定时)
├── entrypoint.sh          # 容器入口:定时任务 + 启动探测
├── node_domain.py         # 主工具(node 模式 + --swap 模式)
├── config.example.json    # 配置示例(复制为 config.json 填写)
├── nodes.example.json     # 节点清单示例(复制为 nodes.json)
├── requirements.txt
└── data/                  # 运行时数据(数据库、输出订阅)

快速开始(Docker)

cp config.example.json config.json
cp nodes.example.json nodes.json
# 编辑 config.json:填入 Cloudflare 凭据、zone、订阅地址
# 编辑 docker-compose.yml:修改 SUB_STORE_PREFIX 为随机路径

docker compose up -d --build
  • Sub-Store 前端:http://<服务器IP>:3000/
  • 前端域名请在 Cloudflare 中开启代理(橙云),即可使用 CF 边缘证书,避免自签证书导致客户端 TLS 报错(zone 的 SSL 模式建议保持 Full
  • 容器启动即执行一次探测 + 同步
  • 之后每 6 小时增量探测、每周日 03:15 全量探测(可用环境变量覆盖 cron)

查看结果

docker compose exec node-domain cat /app/data/node-domain-swapped.yaml   # 替换后的订阅
docker compose logs -f node-domain                                      # 运行日志

订阅链接(给客户端用)

Sub-Store 订阅导出端点为 /download/<订阅名>,会按客户端 UA 自动选择格式(mihomo/Clash 系返回 clashmeta、Surge 返回 surge、其他返回通用 base64)。 域名化订阅为 node-swapped,客户端直接导入:

https://你的域名/<SUB_STORE_PREFIX>/download/node-swapped?target=clashmeta

(去掉 ?target=clashmeta 则按客户端 UA 自动选择格式。) 开启 replace_originals: true 后,原始订阅自身也会被写回域名版:/download/subfree/download/ikunkun/download/DMIT 等客户端原本使用的链接即可直接拿到域名入站(原始远程地址备份在 originals_file,可手动恢复)。

裸机部署(不使用 Docker)

cd /srv/node-dns
python3 -m pip install -r requirements.txt   # 或 apt install python3-yaml

# 预览(不写 Cloudflare)
python3 node_domain.py --config config.json --swap --json

# 实际写入 Cloudflare 并同步 Sub-Store
python3 node_domain.py --config config.json --swap --apply

# 强制全量
python3 node_domain.py --config config.json --swap --apply --full

# 普通节点域名映射模式(node_domain.py 原有功能)
python3 node_domain.py --config config.json --apply

配合 systemd timer 或 cron 定时执行即可。

配置说明

{
  "cf_api_email": "your@email.com",          // Cloudflare 账号
  "cf_api_key": "your-api-token",            // Cloudflare API Token
  "cf_zone": "example.com",                  // 托管域名
  "probe_db": "data/probe.db",               // SQLite 数据库路径
  "probe_strategy": "auto",                  // auto | incremental | full
  "probe_full_interval_days": 7,             // 全量探测间隔(默认 7 天)
  "probe_incremental_interval_hours": 24,    // 增量探测间隔
  "probe_skip_transports": ["ws", "xhttp"],  // 跳过 TCP 探测的传输层
  "reserved_domains": ["ridge.example.com"],   // 预留域名:绝不分配/修改(如 Sub-Store 前端主域名)
  "swap": {
    "subscription_urls": [],                   // 可选:额外的原始订阅 URL
    "substore_subs": [],                       // 可选:显式指定 Sub-Store 订阅名;省略则自动发现全部订阅(排除输出订阅自身)
    "replace_originals": false,                // true:域名化内容直接写回原始同名订阅(download/<原名> 即域名版)
    "originals_file": "data/original_sources.json",  // 原始订阅远程地址备份(覆盖前保存,仅用于恢复;不参与探测数据源)
    "output_file": "data/node-domain-swapped.yaml",
    "sync_mode": "api",                        // api(推荐)| file
    "sync_api_base": "http://sub-store:3000",  // Sub-Store 后端地址
    "sync_api_prefix": "/substore",            // 与 SUB_STORE_BACKEND_PREFIX 一致
    "sync_substore_sub": "node-swapped"        // 写入的 Sub-Store 订阅名
  }
}

行为约定

  • 节点来源:默认自动发现 Sub-Store 中的全部订阅(排除输出订阅自身),数据源固定为 download/<订阅名>,与客户端看到的内容一致(含订阅自身 process 处理结果),重复运行幂等
  • 写回原始订阅:replace_originals: true 时,每个订阅用自身节点渲染域名版并覆盖同名订阅,客户端仍用原 download/<原名> 链接
  • 预留域名:reserved_domains 中的域名(如前端主域名)绝不分配、也绝不改其 DNS 记录;若旧数据误将其映射到节点,会自动释放并重新分配
  • 域名跟随入站 IP:每次运行会抓取原始源(originals_file 备份的远程地址)检测节点入站 IP 变化,变化时复用原域名并把 CF 记录更新到新 IP,客户端域名保持稳定
  • 可达性重探:已映射域名指向的 IP 按增量周期自动重探,入站失效会在日志标记(映射保留,源恢复或 IP 变化后自动更新)
  • 幂等:server 已是域名(含已换过的域名)的节点不探测、不替换,保持原样
  • 保留全部节点:同 (name+server+port+transport) 的 UUID 变体不丢弃,探测去重、渲染复用同一域名
  • 一节点一域名:按 (name+server+port+transport) 指纹唯一映射,重复运行不重复分配域名
  • ws/xhttp 传输层:跳过 TCP 探测,保留原 server 不替换
  • 不可达节点:保留原 server,超过增量周期自动重试
  • 入站 IP 变化:上次可达且解析 IP 变化时立即重新探测并更新 A/AAAA 记录
  • 幂等:探测结果与域名映射写入 SQLite,恢复后自动复用原域名

Sub-Store 数据 git 备份

每日 2 次(北京时间 06:00 / 18:00)把 Sub-Store 状态提交到 VPS 本地 git 仓库 /srv/sub-store-backup,可随时 git log 回溯、git checkout 恢复。

# 1. 上传脚本与 systemd 单元
cp deploy/substore-backup/substore-backup.sh /usr/local/bin/substore-backup.sh
cp deploy/substore-backup/substore-backup.service /etc/systemd/system/
cp deploy/substore-backup/substore-backup.timer /etc/systemd/system/
chmod +x /usr/local/bin/substore-backup.sh

# 2. 首次运行(自动初始化仓库并提交首个快照)
/usr/local/bin/substore-backup.sh

# 3. 启用定时任务
systemctl daemon-reload
systemctl enable --now substore-backup.timer
  • 备份内容:sub-store.json(订阅/集合/设置)、root.jsonnode-domain-swapped.yaml/json
  • 无变更时跳过提交;运行日志在 /var/log/substore-backup.log
  • 仓库仅存于 VPS 本地;如需异地备份(私有 GitHub 仓库),提供 token 后在脚本里加 git remote + git push 即可

安全

  • config.jsonnodes.json 含敏感信息(CF 凭据、真实节点),已加入 .gitignore,请勿提交
  • 建议将 CF 凭据放入环境变量或密钥管理

About

Sub-Store 节点探测 + server 域名更换工具:双栈探测、增量更新、Docker 一键部署

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages