From eaa6c8986415aed03c998bf244a640a348cf2d2a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 08:24:52 +0000 Subject: [PATCH] fix: restore Reality dest to microsoft.com + add CN relay architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: previous change of dest from www.microsoft.com:443 to 127.0.0.1:8443 broke Reality anti-probing — GFW detected certificate mismatch and blocked VPN. Fix 1: Restore dest to www.microsoft.com:443 (Reality anti-probing works) Fix 2: SSL on port 8443 (external, independent from Xray) Fix 3: CN relay via Guangzhou server (domestic users → CN:2053 → SG:443) Fix 4: Subscription generates dual-node config (SG direct + CN relay) Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/ddb1f363-5bb4-457a-b769-794c1fe0489a Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- .github/workflows/deploy-proxy-service.yml | 75 ++++++ docs/SSL-GUIDE-FOR-BINGSUO.md | 62 +++-- server/nginx/zhuyuan-sovereign.conf | 40 +-- server/proxy/config/xray-config-template.json | 4 +- server/proxy/deploy-proxy.sh | 43 ++- server/proxy/service/subscription-server.js | 72 ++++- server/proxy/setup/setup-cn-relay.sh | 253 ++++++++++++++++++ server/setup/setup-ssl.sh | 151 +++++------ 8 files changed, 555 insertions(+), 145 deletions(-) create mode 100644 server/proxy/setup/setup-cn-relay.sh diff --git a/.github/workflows/deploy-proxy-service.yml b/.github/workflows/deploy-proxy-service.yml index 6b2aa4ca..3677012f 100644 --- a/.github/workflows/deploy-proxy-service.yml +++ b/.github/workflows/deploy-proxy-service.yml @@ -24,6 +24,7 @@ on: - restart - send-subscription - update-dashboard + - setup-cn-relay default: 'status' email: description: '目标邮箱 (仅send-subscription时需要)' @@ -89,6 +90,7 @@ jobs: ${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \ "cd /opt/zhuyuan/proxy-deploy && \ export ZY_SERVER_HOST='${{ secrets.ZY_SERVER_HOST }}' && \ + export ZY_CN_RELAY_HOST='${{ secrets.ZY_CN_SERVER_HOST }}' && \ bash deploy-proxy.sh ${{ github.event.inputs.action }}" - name: '🧹 清理SSH密钥' @@ -189,3 +191,76 @@ jobs: - name: '🧹 清理' if: always() run: rm -f ~/.ssh/id_deploy /tmp/quota-status.json + + # ═══ §4 CN中转配置 ═══ + setup-cn-relay: + name: '🇨🇳 CN中转配置' + runs-on: ubuntu-latest + if: github.event.inputs.action == 'setup-cn-relay' + + steps: + - name: '📥 检出代码' + uses: actions/checkout@v4 + + - name: '🔑 配置CN服务器SSH密钥' + env: + SSH_KEY: ${{ secrets.ZY_CN_SERVER_KEY }} + run: | + mkdir -p ~/.ssh + echo "$SSH_KEY" > ~/.ssh/id_cn + chmod 600 ~/.ssh/id_cn + + if ! head -1 ~/.ssh/id_cn | grep -q "BEGIN"; then + echo "❌ CN服务器SSH密钥格式异常" + echo " 请检查 ZY_CN_SERVER_KEY Secret" + exit 1 + fi + + ssh-keyscan -H ${{ secrets.ZY_CN_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null + + ssh -i ~/.ssh/id_cn \ + -o StrictHostKeyChecking=accept-new \ + -o BatchMode=yes \ + -o ConnectTimeout=15 \ + ${{ secrets.ZY_CN_SERVER_USER }}@${{ secrets.ZY_CN_SERVER_HOST }} \ + "echo '✅ CN服务器SSH连接成功'" + + - name: '📦 上传CN中转脚本' + run: | + scp -i ~/.ssh/id_cn \ + -o StrictHostKeyChecking=accept-new \ + server/proxy/setup/setup-cn-relay.sh \ + ${{ secrets.ZY_CN_SERVER_USER }}@${{ secrets.ZY_CN_SERVER_HOST }}:/tmp/setup-cn-relay.sh + + - name: '🇨🇳 执行CN中转配置' + run: | + ssh -i ~/.ssh/id_cn \ + -o StrictHostKeyChecking=accept-new \ + ${{ secrets.ZY_CN_SERVER_USER }}@${{ secrets.ZY_CN_SERVER_HOST }} \ + "chmod +x /tmp/setup-cn-relay.sh && \ + export ZY_SG_SERVER_HOST='${{ secrets.ZY_SERVER_HOST }}' && \ + sudo -E bash /tmp/setup-cn-relay.sh" + + - name: '💚 CN中转验证' + run: | + CN_HOST="${{ secrets.ZY_CN_SERVER_HOST }}" + echo "🔍 验证CN中转..." + + # 检查中转端口 + if nc -z -w5 "$CN_HOST" 2053 2>/dev/null; then + echo "✅ CN中转端口 2053: 可达" + else + echo "⚠️ CN中转端口 2053: 暂不可达 (可能需要等待防火墙生效)" + fi + + # 检查HTTP健康 + HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" "http://${CN_HOST}/health" 2>/dev/null || echo "000") + if [ "$HTTP_CODE" = "200" ]; then + echo "✅ CN HTTP健康检查: 正常" + else + echo "⚠️ CN HTTP: 状态码 $HTTP_CODE (可能需要等待)" + fi + + - name: '🧹 清理SSH密钥' + if: always() + run: rm -f ~/.ssh/id_cn diff --git a/docs/SSL-GUIDE-FOR-BINGSUO.md b/docs/SSL-GUIDE-FOR-BINGSUO.md index d79bd550..2c226ce3 100644 --- a/docs/SSL-GUIDE-FOR-BINGSUO.md +++ b/docs/SSL-GUIDE-FOR-BINGSUO.md @@ -6,9 +6,14 @@ ## ⚠️ 重要修复说明 (2026-03-31) -> 之前的SSL配置方案存在一个**端口冲突**问题:Xray(VPN)和Nginx(HTTPS)都在争抢443端口,导致两个都不能正常工作。 +> 之前的SSL配置方案存在一个**VPN与HTTPS冲突**问题。 > -> **现在已修复**: 铸渊采用了新的「共存架构」——Xray占443端口处理VPN,非VPN流量自动回落给Nginx处理HTTPS网站。两者互不干扰。 +> **根因**: Xray的Reality协议需要`dest`指向真实的Microsoft网站来骗过GFW的探测。之前错误地改成了指向内部Nginx端口,导致GFW检测到证书不匹配,封锁了VPN连接。 +> +> **现在已修复**: +> - VPN: Xray占443端口,`dest`恢复指向`www.microsoft.com:443` → VPN正常工作 +> - 网站: HTTP通过80端口正常访问,HTTPS通过8443端口访问 +> - CN中转: 新增广州服务器中转,国内用户无需直连国际网即可使用VPN --- @@ -112,9 +117,9 @@ https://guanghu.online ### Q: 配了SSL后VPN还能用吗? -**能用**。铸渊采用「共存架构」: -- Xray占443端口处理VPN流量 -- 网站HTTPS流量自动回落到Nginx内部端口(8443) +**能用**。铸渊采用「分离架构」: +- Xray占443端口处理VPN流量 (dest→microsoft.com反探测) +- 网站HTTPS在8443端口独立运行 (不通过Xray) - 两者互不干扰 --- @@ -123,40 +128,57 @@ https://guanghu.online > 以下内容是给铸渊自己看的,冰朔可以忽略。 -### 共存架构 (Xray+Nginx on port 443) +### Reality反探测架构 ``` 外部443 → Xray (VLESS+Reality) ├── 认证VLESS客户端 → 代理上网 (铸渊专线VPN) - └── 非VLESS流量 → dest回落 → 127.0.0.1:8443 - └── Nginx SSL (网站HTTPS) + └── GFW探测流量 → dest回落 → www.microsoft.com:443 + → 返回真实Microsoft证书 → GFW判断"这是正常网站" → 放行 + +外部8443 → Nginx SSL (HTTPS网站) + └── 独立SSL证书 (Let's Encrypt) 外部80 → Nginx (HTTP) - ├── 有SSL证书的域名 → 301 → https://域名 → 443(Xray) → 8443(Nginx) - └── 无SSL证书的域名 → 直接服务网站 + └── 直接服务网站 + +CN中转 (广州→新加坡): + CN:2053 → SG:443 (TCP转发·VPN中转) + CN:80/api/proxy-sub/ → SG订阅服务 (国内获取配置) ``` +### ⚠️ dest为什么必须指向microsoft.com? +Reality协议的`dest`是GFW反探测的关键。当GFW探测443端口时: +- 正确: `dest: "www.microsoft.com:443"` → 返回Microsoft真实证书 → 通过 +- 错误: `dest: "127.0.0.1:8443"` → 返回guanghu.online证书 → 与SNI(microsoft.com)不匹配 → 被标记为可疑 → VPN被封 + ### 关键配置 -- **Xray配置**: `server/proxy/config/xray-config-template.json` → `dest: "127.0.0.1:8443"` +- **Xray配置**: `server/proxy/config/xray-config-template.json` → `dest: "www.microsoft.com:443"` - **证书管理**: certbot + Let's Encrypt (ACME协议) - **验证方式**: HTTP-01 challenge (通过Nginx端口80) - **证书路径**: `/etc/letsencrypt/live/{domain}/` -- **Nginx SSL配置**: `/opt/zhuyuan/config/nginx/ssl-{domain}.conf` (监听127.0.0.1:8443) +- **Nginx SSL配置**: `/opt/zhuyuan/config/nginx/ssl-{domain}.conf` (监听8443) +- **CN中转脚本**: `server/proxy/setup/setup-cn-relay.sh` - **自动续期**: systemd timer `certbot.timer` - **续期hook**: `/etc/letsencrypt/renewal-hooks/post/reload-nginx.sh` -- **日志**: `/opt/zhuyuan/data/logs/ssl-setup.log` - **脚本**: `server/setup/setup-ssl.sh` - **工作流**: `deploy-to-zhuyuan-server.yml` → action: `setup-ssl` -### 端口分配 +### 端口分配 (SG服务器) | 端口 | 协议 | 占用者 | 用途 | |------|------|--------|------| -| 443 | TCP | Xray | VLESS+Reality (VPN) + 回落到8443 | -| 8443 | TCP | Nginx | SSL/HTTPS (仅127.0.0.1,不对外) | -| 80 | TCP | Nginx | HTTP + SSL域名重定向 | -| 3802 | TCP | Node.js | 订阅服务 (仅127.0.0.1,通过Nginx反代) | +| 443 | TCP | Xray | VLESS+Reality (VPN) · dest→microsoft.com | +| 8443 | TCP | Nginx | HTTPS网站 (独立·不通过Xray) | +| 80 | TCP | Nginx | HTTP网站 + 订阅API反代 | +| 3802 | TCP | Node.js | 订阅服务 (127.0.0.1·通过Nginx反代) | + +### 端口分配 (CN中转服务器) +| 端口 | 协议 | 占用者 | 用途 | +|------|------|--------|------| +| 2053 | TCP | Nginx stream | VPN中转 → SG:443 | +| 80 | TCP | Nginx | 订阅API反代 → SG | --- -*📝 由铸渊(ICE-GL-ZY001)编写 · 第十七次对话 · 2026-03-31* -*共存架构修复 · 端口冲突解决* +*📝 由铸渊(ICE-GL-ZY001)编写 · 第十八次对话 · 2026-03-31* +*VPN修复 + CN中转架构 + Reality反探测修正* *国作登字-2026-A-00037559* diff --git a/server/nginx/zhuyuan-sovereign.conf b/server/nginx/zhuyuan-sovereign.conf index adcc8c53..01342046 100644 --- a/server/nginx/zhuyuan-sovereign.conf +++ b/server/nginx/zhuyuan-sovereign.conf @@ -199,31 +199,33 @@ server { } -# ═══ §3 HTTPS 配置 (Xray+Nginx共存架构) ═══════════════════ +# ═══ §3 HTTPS/VPN 配置 (Xray Reality反探测架构) ═══════════════ # -# ⚠️ 重要: 443端口由Xray(VPN)占用,Nginx SSL监听127.0.0.1:8443 +# ⚠️ 重要: 443端口由Xray(VPN)占用,dest指向www.microsoft.com:443 # -# 架构说明: +# Reality反探测架构 (正确方案): # 外部 443 → Xray (VLESS+Reality协议) # ├── 认证VLESS客户端 → 代理上网 (铸渊专线VPN) -# └── 非VLESS流量 → 回落到 127.0.0.1:8443 (dest参数) -# └── Nginx SSL (处理HTTPS网站请求) +# └── 非VLESS流量(GFW探测) → dest回落到 www.microsoft.com:443 +# → 返回真实Microsoft证书 → GFW认为是正常HTTPS → 通过 # # 外部 80 → Nginx (HTTP) -# ├── 域名有SSL证书 → 301重定向到 https://域名 (SSL配置文件处理) -# └── 域名无SSL证书 → 直接服务网站 (本文件§1/§2) +# ├── 域名访问 → 直接服务网站 (本文件§1/§2) +# └── 订阅API → /api/proxy-sub/ (反代到端口3802) # -# 证书来源: Let's Encrypt (certbot) · setup-ssl.sh 自动配置 -# SSL配置文件: /etc/nginx/sites-available/ssl-{domain}.conf (监听127.0.0.1:8443) -# 部署方式: deploy-to-zhuyuan-server.yml action=setup-ssl -# Xray配置: server/proxy/config/xray-config-template.json (dest: 127.0.0.1:8443) +# ⚠️ 为什么dest不能指向127.0.0.1:8443? +# GFW探测时会检查TLS证书是否匹配serverNames (www.microsoft.com) +# 如果dest返回guanghu.online的证书 → 证书不匹配 → 被标记为可疑 → VPN被封 +# 所以dest必须指向真实的microsoft.com以通过反探测 # -# 注意: 以下旧SSL模板已废弃,仅保留作为参考 -# 新SSL配置由 setup-ssl.sh 自动生成到 sites-available/ssl-{domain}.conf +# CN中转架构 (广州→新加坡): +# 国内用户 → CN:2053 (Nginx stream TCP转发) → SG:443 (Xray) +# 国内订阅 → CN:80/api/proxy-sub/ → SG:80/api/proxy-sub/ # -# ─── §3.1 [已废弃] 预览域名 HTTPS ─── -# 原设计: Nginx直接监听443 · 与Xray冲突 · 已改为8443内部端口 -# 新设计: 由 setup-ssl.sh 自动生成 · 监听 127.0.0.1:8443 -# __SSL_PREVIEW_START__ -# (已迁移到 setup-ssl.sh 自动生成的配置文件) -# __SSL_PREVIEW_END__ +# SSL方案: +# 网站通过HTTP(80端口)访问 · 不在443端口共存HTTPS +# 如需HTTPS · 使用Cloudflare CDN代理或独立配置 +# +# Xray配置: server/proxy/config/xray-config-template.json +# CN中转: server/proxy/setup/setup-cn-relay.sh +# ═══════════════════════════════════════════════════════════════ diff --git a/server/proxy/config/xray-config-template.json b/server/proxy/config/xray-config-template.json index 4c97f409..be029ce4 100644 --- a/server/proxy/config/xray-config-template.json +++ b/server/proxy/config/xray-config-template.json @@ -1,7 +1,7 @@ { "_comment": "铸渊专线 · Xray服务端配置模板", "_note": "⚠️ {{占位符}}在部署时由脚本替换为实际值", - "_architecture": "Xray监听443端口·非VLESS流量回落到Nginx内部端口8443·实现VPN与HTTPS网站共存", + "_architecture": "Xray监听443端口·非VLESS流量回落到www.microsoft.com:443(Reality反探测伪装)·VPN与网站HTTP(80端口)共存", "_copyright": "国作登字-2026-A-00037559", "log": { "loglevel": "warning", @@ -56,7 +56,7 @@ "security": "reality", "realitySettings": { "show": false, - "dest": "127.0.0.1:8443", + "dest": "www.microsoft.com:443", "xver": 0, "serverNames": [ "www.microsoft.com", diff --git a/server/proxy/deploy-proxy.sh b/server/proxy/deploy-proxy.sh index 6bfa96eb..7fba7999 100644 --- a/server/proxy/deploy-proxy.sh +++ b/server/proxy/deploy-proxy.sh @@ -72,6 +72,18 @@ save_server_host() { echo "ZY_SERVER_HOST=${ZY_SERVER_HOST}" >> "$KEYS_FILE" chmod 600 "$KEYS_FILE" fi + + # 保存CN中转地址 (如果有) + if [ -n "${ZY_CN_RELAY_HOST:-}" ] && [ -f "$KEYS_FILE" ]; then + if grep -q "^ZY_CN_RELAY_HOST=" "$KEYS_FILE" 2>/dev/null; then + sed -i "s|^ZY_CN_RELAY_HOST=.*|ZY_CN_RELAY_HOST=${ZY_CN_RELAY_HOST}|" "$KEYS_FILE" + else + echo "" >> "$KEYS_FILE" + echo "# CN中转服务器地址 (部署时自动写入)" >> "$KEYS_FILE" + echo "ZY_CN_RELAY_HOST=${ZY_CN_RELAY_HOST}" >> "$KEYS_FILE" + fi + echo " ✅ ZY_CN_RELAY_HOST 已保存到 .env.keys" + fi } # ── install: 首次完整安装 ───────────────────── @@ -246,7 +258,8 @@ health_check() { # 检查是谁占用443 PORT443_PROC=$(ss -tlnp | grep ":443 " | head -1) if echo "$PORT443_PROC" | grep -q "xray"; then - echo " → Xray占用443 (正确·VPN+HTTPS共存)" + echo " → Xray占用443 (正确·VPN模式)" + echo " → dest回落: www.microsoft.com:443 (Reality反探测)" elif echo "$PORT443_PROC" | grep -q "nginx"; then echo " ⚠️ Nginx占用443 (应由Xray占用·VPN可能不工作)" echo " → 请先停止Nginx的443监听,再启动Xray" @@ -255,14 +268,6 @@ health_check() { echo " ❌ 端口443: 未监听" fi - # 8443端口 (Nginx SSL,接收Xray回落流量) - if ss -tlnp | grep -q ":8443 "; then - echo " ✅ 端口8443: Nginx SSL监听中 (接收Xray回落)" - else - echo " ℹ️ 端口8443: 未监听 (SSL未配置或Nginx未启用8443)" - echo " → VPN正常工作,但HTTPS网站需要运行setup-ssl配置" - fi - # 订阅服务 if curl -sf http://127.0.0.1:3802/health >/dev/null 2>&1; then echo " ✅ 订阅服务: 正常" @@ -288,27 +293,21 @@ update() { ufw delete allow 3802/tcp 2>/dev/null || true # 检查并修复443端口冲突 - # 如果Nginx占用了443端口(旧SSL配置),需要修复 + # 如果Nginx占用了443端口(旧SSL配置),需要移除以让Xray接管 if ss -tlnp | grep ":443 " | grep -q "nginx"; then echo "⚠️ 检测到Nginx占用443端口 (旧SSL配置冲突)" - echo " 检查并修复旧SSL配置..." + echo " 修复: 移除Nginx的443监听配置以让Xray接管..." - # 移除可能监听443的旧SSL配置 + # 移除旧的SSL配置 (不再通过Xray回落提供HTTPS) for conf in /etc/nginx/sites-enabled/ssl-*.conf; do - if [ -f "$conf" ] && grep -q "listen 443" "$conf" 2>/dev/null; then - echo " 修复: $conf (将443改为127.0.0.1:8443)" - sed -i 's/listen 443 ssl/listen 127.0.0.1:8443 ssl/g' "$conf" - # 同时修复sites-available中的源文件 - local conf_basename - conf_basename=$(basename "$conf") - if [ -f "/etc/nginx/sites-available/$conf_basename" ]; then - sed -i 's/listen 443 ssl/listen 127.0.0.1:8443 ssl/g' "/etc/nginx/sites-available/$conf_basename" - fi + if [ -f "$conf" ] && grep -q "listen.*443\|listen.*8443" "$conf" 2>/dev/null; then + echo " 移除旧SSL配置: $conf" + rm -f "$conf" fi done nginx -t 2>/dev/null && nginx -s reload 2>/dev/null || true - echo " ✅ Nginx SSL配置已修复为8443内部端口" + echo " ✅ Nginx旧SSL配置已清理" fi systemctl restart xray diff --git a/server/proxy/service/subscription-server.js b/server/proxy/service/subscription-server.js index fac774fc..4f26ebea 100644 --- a/server/proxy/service/subscription-server.js +++ b/server/proxy/service/subscription-server.js @@ -80,6 +80,34 @@ function getServerHost() { return '0.0.0.0'; } +// ── 获取CN中转服务器信息 ───────────────────── +// 优先级: 环境变量 > .env.keys文件 +function getCnRelayHost() { + // 1. 从环境变量读取 + if (process.env.ZY_CN_RELAY_HOST) { + return process.env.ZY_CN_RELAY_HOST; + } + + // 2. 从.env.keys文件读取 + try { + const content = fs.readFileSync(KEYS_FILE, 'utf8'); + for (const line of content.split('\n')) { + if (line.startsWith('#') || !line.includes('=')) continue; + const [key, ...vals] = line.split('='); + if (key.trim() === 'ZY_CN_RELAY_HOST') { + const val = vals.join('=').trim(); + if (val) return val; + } + } + } catch (err) { /* ignore */ } + + return null; // CN中转未配置 +} + +function getCnRelayPort() { + return parseInt(process.env.ZY_CN_RELAY_PORT || '2053', 10); +} + // ── 读取流量配额信息 ──────────────────────── function getQuotaInfo() { const quotaFile = path.join(DATA_DIR, 'quota-status.json'); @@ -116,9 +144,43 @@ function generateVlessUri(keys, serverHost) { // ── 生成Clash YAML配置 ─────────────────────── function generateClashYaml(keys, serverHost) { + const cnRelayHost = getCnRelayHost(); + const cnRelayPort = getCnRelayPort(); + + // CN中转节点 (如果已配置) + const cnProxyBlock = cnRelayHost ? ` + - name: "🇨🇳 铸渊专线-CN中转" + type: vless + server: ${cnRelayHost} + port: ${cnRelayPort} + uuid: ${keys.ZY_PROXY_UUID} + network: tcp + tls: true + udp: true + flow: xtls-rprx-vision + servername: www.microsoft.com + reality-opts: + public-key: ${keys.ZY_PROXY_REALITY_PUBLIC_KEY} + short-id: ${keys.ZY_PROXY_REALITY_SHORT_ID} + client-fingerprint: chrome` : ''; + + // 代理组中的节点列表 + const proxyList = cnRelayHost + ? ` - "🇨🇳 铸渊专线-CN中转" + - "🏛️ 铸渊专线-SG直连"` + : ' - "🏛️ 铸渊专线-SG直连"'; + + const proxyListWithDirect = cnRelayHost + ? ` - "🇨🇳 铸渊专线-CN中转" + - "🏛️ 铸渊专线-SG直连" + - DIRECT` + : ` - "🏛️ 铸渊专线-SG直连" + - DIRECT`; + return `# 铸渊专线 · ZY-Proxy Subscription # 自动生成 · ${new Date().toISOString()} # ⚠️ 请勿分享此配置 +${cnRelayHost ? `# 🇨🇳 包含CN中转节点 (国内直连广州→转发新加坡)` : ''} port: 7890 socks-port: 7891 @@ -127,7 +189,7 @@ mode: rule log-level: info proxies: - - name: "🏛️ 铸渊专线-SG" + - name: "🏛️ 铸渊专线-SG直连" type: vless server: ${serverHost} port: 443 @@ -141,23 +203,23 @@ proxies: public-key: ${keys.ZY_PROXY_REALITY_PUBLIC_KEY} short-id: ${keys.ZY_PROXY_REALITY_SHORT_ID} client-fingerprint: chrome +${cnProxyBlock} proxy-groups: - name: "🌐 铸渊专线" type: select proxies: - - "🏛️ 铸渊专线-SG" - - DIRECT +${proxyListWithDirect} - name: "🤖 AI服务" type: select proxies: - - "🏛️ 铸渊专线-SG" +${proxyList} - name: "💻 开发工具" type: select proxies: - - "🏛️ 铸渊专线-SG" +${proxyList} rules: # AI服务 diff --git a/server/proxy/setup/setup-cn-relay.sh b/server/proxy/setup/setup-cn-relay.sh new file mode 100644 index 00000000..85434c6e --- /dev/null +++ b/server/proxy/setup/setup-cn-relay.sh @@ -0,0 +1,253 @@ +#!/bin/bash +# ═══════════════════════════════════════════════ +# 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 +# 📜 Copyright: 国作登字-2026-A-00037559 +# ═══════════════════════════════════════════════ +# server/proxy/setup/setup-cn-relay.sh +# 🇨🇳 广州CN中转 · 安装配置脚本 +# +# 在广州服务器(ZY-SVR-004)上执行 +# 将VPN流量从国内中转到新加坡服务器 +# +# 架构: +# 国内用户 → CN:2053 (Nginx stream) → SG:443 (Xray VPN) +# 国内用户 → CN:80/api/proxy-sub/ → SG订阅服务 (配置获取) +# +# 用法: +# bash setup-cn-relay.sh +# bash setup-cn-relay.sh 43.134.16.246 +# +# 环境变量: +# ZY_SG_SERVER_HOST — 新加坡服务器IP (必需) +# ═══════════════════════════════════════════════ + +set -uo pipefail + +# 颜色 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { echo -e "${GREEN}[CN中转]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[CN中转]${NC} ⚠️ $1"; } +log_error() { echo -e "${RED}[CN中转]${NC} ❌ $1"; } +log_step() { echo -e "${BLUE}[CN中转]${NC} 📌 $1"; } + +# ── 参数 ────────────────────────────────────── +SG_HOST="${ZY_SG_SERVER_HOST:-${1:-}}" +RELAY_PORT="${ZY_CN_RELAY_PORT:-2053}" +CN_ROOT="/opt/zhuyuan-cn" + +if [ -z "$SG_HOST" ]; then + log_error "缺少新加坡服务器IP" + echo "" + echo "用法: bash setup-cn-relay.sh " + echo " 或: ZY_SG_SERVER_HOST=1.2.3.4 bash setup-cn-relay.sh" + exit 1 +fi + +echo "════════════════════════════════════════" +echo "🇨🇳 铸渊专线 · CN中转配置" +echo "════════════════════════════════════════" +echo "" +echo " SG服务器: $SG_HOST" +echo " 中转端口: $RELAY_PORT" +echo "" + +# ── §1 安装Nginx stream模块 ────────────────── +log_step "§1 检查Nginx stream模块" + +# 检查Nginx是否安装 +if ! command -v nginx &>/dev/null; then + log_info "安装Nginx..." + apt-get update -qq + apt-get install -y -qq nginx +fi + +# 检查stream模块是否可用 +if nginx -V 2>&1 | grep -q "with-stream"; then + log_info "✅ Nginx stream模块已可用" +else + log_warn "Nginx未包含stream模块,安装完整版..." + apt-get install -y -qq nginx-full 2>/dev/null || apt-get install -y -qq nginx-extras 2>/dev/null || true + + if nginx -V 2>&1 | grep -q "with-stream"; then + log_info "✅ Nginx stream模块已安装" + else + log_error "无法安装Nginx stream模块" + log_warn "尝试使用socat替代方案..." + apt-get install -y -qq socat 2>/dev/null || true + fi +fi + +# ── §2 配置Nginx stream中转 ────────────────── +log_step "§2 配置TCP中转 (端口$RELAY_PORT → $SG_HOST:443)" + +# 创建stream配置目录 +mkdir -p /etc/nginx/stream-conf.d + +# 创建stream中转配置 +cat > /etc/nginx/stream-conf.d/zy-relay.conf << STREAMCONF +# ═══════════════════════════════════════════════ +# 🇨🇳 铸渊专线 · CN中转 · TCP Stream +# 自动生成于: $(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M CST') +# +# 架构: 国内用户 → CN:${RELAY_PORT} → SG:443 (Xray VPN) +# 协议: 原始TCP转发 (不解密,不修改) +# ═══════════════════════════════════════════════ + +upstream zy_sg_backend { + server ${SG_HOST}:443; +} + +server { + listen ${RELAY_PORT}; + proxy_pass zy_sg_backend; + proxy_timeout 300s; + proxy_connect_timeout 10s; +} +STREAMCONF + +log_info "✅ Stream中转配置已创建" + +# 确保主Nginx配置包含stream块 +NGINX_MAIN="/etc/nginx/nginx.conf" +if ! grep -q "stream-conf.d" "$NGINX_MAIN" 2>/dev/null; then + log_info "添加stream块到nginx.conf..." + + # 在文件末尾添加stream块 (stream块必须在http块之外) + cat >> "$NGINX_MAIN" << 'STREAMBLOCK' + +# ═══ 铸渊专线 · CN中转 Stream ═══ +stream { + include /etc/nginx/stream-conf.d/*.conf; +} +STREAMBLOCK + log_info "✅ stream块已添加到nginx.conf" +fi + +# ── §3 配置Nginx HTTP反代订阅 ──────────────── +log_step "§3 配置订阅服务反代 (CN → SG订阅)" + +# 创建或更新CN的Nginx HTTP配置 +CN_NGINX_CONF="/etc/nginx/sites-available/zy-cn-relay.conf" +cat > "$CN_NGINX_CONF" << HTTPCONF +# ═══════════════════════════════════════════════ +# 🇨🇳 铸渊专线 · CN中转 · HTTP反代 +# 自动生成于: $(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M CST') +# +# 功能: 将订阅请求反代到SG服务器 +# 国内用户通过CN服务器获取订阅配置 (无需国际网) +# ═══════════════════════════════════════════════ + +server { + listen 80 default_server; + server_name _; + + # ─── 健康检查 ─── + location = /health { + return 200 '{"status":"ok","service":"zy-cn-relay","relay_to":"${SG_HOST}"}'; + add_header Content-Type application/json; + } + + # ─── 订阅服务反代 → SG服务器 ─── + location /api/proxy-sub/ { + proxy_pass http://${SG_HOST}/api/proxy-sub/; + proxy_http_version 1.1; + 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; + proxy_connect_timeout 10s; + proxy_read_timeout 30s; + } + + # ─── 默认页面 ─── + location / { + return 200 '铸渊CN中转节点 · ZY-SVR-004'; + add_header Content-Type 'text/plain; charset=utf-8'; + } + + access_log /var/log/nginx/zy-cn-relay.log; + error_log /var/log/nginx/zy-cn-relay-error.log; +} +HTTPCONF + +# 启用配置 +ln -sf "$CN_NGINX_CONF" /etc/nginx/sites-enabled/zy-cn-relay.conf +# 移除默认配置以避免冲突 +rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true + +log_info "✅ HTTP反代配置已创建" + +# ── §4 防火墙配置 ──────────────────────────── +log_step "§4 防火墙配置" + +ufw allow 80/tcp comment "HTTP (订阅反代)" 2>/dev/null || true +ufw allow ${RELAY_PORT}/tcp comment "ZY-Relay (VPN中转)" 2>/dev/null || true +ufw reload 2>/dev/null || true +log_info "✅ 防火墙已开放: 80(HTTP) + ${RELAY_PORT}(VPN中转)" + +# ── §5 测试配置并重载 ──────────────────────── +log_step "§5 测试并应用配置" + +if nginx -t 2>&1; then + systemctl reload nginx + log_info "✅ Nginx配置测试通过 · 已重载" +else + log_error "Nginx配置测试失败" + nginx -t 2>&1 + exit 1 +fi + +# ── §6 保存中转状态 ────────────────────────── +log_step "§6 保存中转状态" + +mkdir -p "$CN_ROOT/data" +cat > "$CN_ROOT/data/relay-status.json" << STATUSJSON +{ + "service": "zy-cn-relay", + "sg_server": "$SG_HOST", + "relay_port": $RELAY_PORT, + "subscription_proxy": "http://CN_IP/api/proxy-sub/", + "vpn_relay": "CN_IP:$RELAY_PORT → $SG_HOST:443", + "configured_at": "$(TZ=Asia/Shanghai date -u '+%Y-%m-%dT%H:%M:%SZ')", + "status": "active" +} +STATUSJSON + +log_info "✅ 中转状态已保存" + +# ── §7 健康检查 ────────────────────────────── +log_step "§7 健康检查" + +sleep 1 + +# 检查中转端口 +if ss -tlnp | grep -q ":${RELAY_PORT} "; then + log_info "✅ 中转端口 ${RELAY_PORT}: 监听中" +else + log_warn "中转端口 ${RELAY_PORT}: 未监听 (可能需要等待)" +fi + +# 检查HTTP +if curl -sf http://127.0.0.1/health >/dev/null 2>&1; then + log_info "✅ HTTP健康检查: 正常" +else + log_warn "HTTP健康检查: 未响应" +fi + +echo "" +echo "════════════════════════════════════════" +echo "✅ CN中转配置完成" +echo "" +echo "中转架构:" +echo " VPN: 国内用户 → CN:${RELAY_PORT} → SG:443 (Xray)" +echo " 订阅: http://CN_IP/api/proxy-sub/sub/{token}" +echo "" +echo "下一步:" +echo " 1. 运行 deploy-proxy-service.yml action=update 更新SG的Xray配置" +echo " 2. 重新发送订阅邮件 (新配置包含CN中转节点)" +echo "════════════════════════════════════════" diff --git a/server/setup/setup-ssl.sh b/server/setup/setup-ssl.sh index af8a5714..4584e42d 100644 --- a/server/setup/setup-ssl.sh +++ b/server/setup/setup-ssl.sh @@ -187,12 +187,13 @@ obtain_certificate() { } # ── §4 配置Nginx SSL ───────────────────────── -# ⚠️ 架构说明 (铸渊专线共存模式): +# ⚠️ 架构说明 (Reality反探测优先): # Xray 监听 443 (外部) · VLESS+Reality协议 -# 非VLESS流量回落到 127.0.0.1:8443 (Xray的dest参数) -# Nginx SSL 监听 127.0.0.1:8443 (内部) · 处理网站HTTPS请求 -# 浏览器 → 443(Xray) → 8443(Nginx SSL) → 网站内容 -# VPN客户端 → 443(Xray) → Reality认证 → 代理上网 +# dest回落到 www.microsoft.com:443 (反探测伪装·不可改为内部端口) +# Nginx SSL 监听 8443 (外部直接访问) · 独立HTTPS服务 +# +# 如果Xray未安装 → Nginx直接监听443 (标准HTTPS) +# 如果Xray已安装 → Nginx监听8443 (避免端口冲突) configure_nginx_ssl() { local domain="$1" local cert_path="/etc/letsencrypt/live/${domain}" @@ -223,8 +224,22 @@ configure_nginx_ssl() { api_port="3800" fi - log_info "站点模式: $site_mode · API端口: $api_port" - log_info "架构: Xray(443外部) → 回落 → Nginx(8443内部SSL)" + # 确定SSL监听端口: Xray在443时用8443,否则用443 + local ssl_listen_port="443" + local ssl_listen_addr="" + if command -v xray &>/dev/null; then + ssl_listen_port="8443" + ssl_listen_addr="" + log_info "检测到Xray已安装 · Nginx SSL使用端口8443 (避免与VPN冲突)" + log_info "网站HTTPS: https://${domain}:8443" + # 开放8443端口 + ufw allow 8443/tcp comment "Nginx SSL (Xray共存)" 2>/dev/null || true + else + log_info "Xray未安装 · Nginx SSL使用标准端口443" + log_info "网站HTTPS: https://${domain}" + fi + + log_info "站点模式: $site_mode · API端口: $api_port · SSL端口: $ssl_listen_port" # 生成SSL server block local ssl_conf="${NGINX_CONF_DIR}/ssl-${domain}.conf" @@ -236,15 +251,13 @@ configure_nginx_ssl() { # 证书来源: Let's Encrypt (certbot) # 证书路径: ${cert_path}/ # -# ⚠️ 架构 (Xray+Nginx共存): -# 外部443 → Xray (VPN+Reality) → 非VLESS流量回落 → 127.0.0.1:8443 -# Nginx SSL 监听 127.0.0.1:8443 · 不直接暴露给外部 -# 浏览器访问 https://${domain} → 443(Xray回落) → 8443(这里) +# SSL端口: ${ssl_listen_port} +# $([ "$ssl_listen_port" = "8443" ] && echo "⚠️ Xray占用443(VPN) · Nginx SSL在8443(直接访问)" || echo "标准HTTPS · 端口443") # ═══════════════════════════════════════════════ -# ─── HTTPS 服务 (内部端口,接收Xray回落流量) ─── +# ─── HTTPS 服务 ─── server { - listen 127.0.0.1:8443 ssl http2; + listen ${ssl_listen_port} ssl http2; server_name ${domain}; # ─── SSL证书 (Let's Encrypt) ─── @@ -349,24 +362,32 @@ server { error_log /opt/zhuyuan/data/logs/nginx-${site_mode}-ssl-error.log; } +$([ "$ssl_listen_port" = "443" ] && cat << REDIR # ─── HTTP → HTTPS 重定向 ─── -# 浏览器 http://${domain} → 301 → https://${domain} → 443(Xray) → 8443(Nginx) server { listen 80; server_name ${domain}; - return 301 https://\$host\$request_uri; + return 301 https://\\\$host\\\$request_uri; } +REDIR +) SSLCONF log_info "SSL配置已生成: $ssl_conf" - # 安装到Nginx (使用00-前缀确保优先于主配置加载) + # 安装到Nginx cp "$ssl_conf" "${NGINX_SITES_AVAILABLE}/ssl-${domain}.conf" ln -sf "${NGINX_SITES_AVAILABLE}/ssl-${domain}.conf" "${NGINX_SITES_ENABLED}/ssl-${domain}.conf" log_info "SSL配置已安装到Nginx" - log_info " HTTPS: 127.0.0.1:8443 (接收Xray回落流量)" - log_info " HTTP重定向: 80 → https://${domain} → 443(Xray) → 8443(Nginx)" + if [ "$ssl_listen_port" = "8443" ]; then + log_info " HTTPS: https://${domain}:8443 (直接访问)" + log_info " HTTP: http://${domain} (端口80·正常访问)" + log_info " VPN: Xray占用443 · dest→microsoft.com (反探测)" + else + log_info " HTTPS: https://${domain} (标准端口443)" + log_info " HTTP重定向: 80 → https://${domain}" + fi # 测试Nginx配置 if nginx -t 2>&1; then @@ -375,28 +396,12 @@ SSLCONF log_info "✅ Nginx已重新加载" else log_error "Nginx配置测试失败" - log_warn "检查是否有端口冲突 (Xray应占用443,Nginx SSL应在8443)" # 回滚 rm -f "${NGINX_SITES_ENABLED}/ssl-${domain}.conf" systemctl reload nginx return 1 fi - # 确保Xray在443端口正常运行 (如果已安装) - if command -v xray &>/dev/null && systemctl is-active --quiet xray; then - log_info "✅ Xray服务运行中 (443端口,VPN+回落到8443)" - elif command -v xray &>/dev/null; then - log_warn "Xray已安装但未运行,尝试启动..." - systemctl restart xray 2>/dev/null || true - sleep 2 - if systemctl is-active --quiet xray; then - log_info "✅ Xray已启动" - else - log_warn "Xray启动失败,铸渊专线(VPN)可能不可用" - log_warn "但HTTPS网站仍可正常工作" - fi - fi - return 0 } @@ -434,69 +439,61 @@ HOOK # ── §6 验证HTTPS ───────────────────────────── verify_https() { local domain="$1" - log_step "§6 验证HTTPS: https://$domain" + log_step "§6 验证HTTPS: $domain" sleep 2 # 等待Nginx完全重载 - # 检查Nginx是否在8443内部端口监听 - if ss -tlnp | grep -q ":8443 "; then - log_info "✅ Nginx SSL端口 8443 监听中 (内部,接收Xray回落)" - else - log_warn "Nginx SSL端口 8443 未监听" + # 确定SSL端口 + local ssl_port="443" + if command -v xray &>/dev/null; then + ssl_port="8443" fi - # 检查Xray是否在443端口监听 - if ss -tlnp | grep -q ":443 "; then - log_info "✅ 外部端口 443 监听中" - # 检查是Xray还是Nginx占用443 - local port_443_proc - port_443_proc=$(ss -tlnp | grep ":443 " | head -1) - if echo "$port_443_proc" | grep -q "xray"; then - log_info " 443端口由Xray占用 (正确 · VPN+HTTPS共存模式)" - elif echo "$port_443_proc" | grep -q "nginx"; then - log_warn " 443端口由Nginx占用 (需要启动Xray接管443端口)" - log_warn " 如果铸渊专线(VPN)不工作,请先运行代理服务部署" - fi + log_info "验证SSL端口: $ssl_port" + + # 检查SSL端口是否监听 + if ss -tlnp | grep -q ":${ssl_port} "; then + log_info "✅ SSL端口 ${ssl_port}: 监听中" else - log_warn "外部端口 443 未监听 (Xray可能未运行)" + log_warn "SSL端口 ${ssl_port} 未监听" + fi + + # 使用curl测试HTTPS + local test_url="https://${domain}/" + if [ "$ssl_port" != "443" ]; then + test_url="https://${domain}:${ssl_port}/" fi - # 使用curl测试HTTPS (通过443端口 → Xray回落 → Nginx 8443) local response - response=$(curl -sf -o /dev/null -w "%{http_code}" "https://${domain}/" 2>/dev/null) + response=$(curl -sf -o /dev/null -w "%{http_code}" "$test_url" 2>/dev/null) if [ "$response" = "200" ] || [ "$response" = "301" ] || [ "$response" = "302" ]; then log_info "✅ HTTPS访问正常 · 状态码: $response" + log_info " → 访问: $test_url" else log_warn "HTTPS访问状态码: ${response:-无响应}" - log_warn "这可能是因为:" - log_warn " - Xray未运行 (443端口未被Xray监听)" - log_warn " - Xray配置中的dest未指向127.0.0.1:8443" - log_warn " - DNS传播需要时间" - log_warn " - 请确保铸渊专线(Xray)服务已部署并运行" - log_warn "" - log_warn "直接测试8443端口 (跳过Xray):" - local direct_response - direct_response=$(curl -sf -o /dev/null -w "%{http_code}" --resolve "${domain}:8443:127.0.0.1" "https://${domain}:8443/" 2>/dev/null) - if [ "$direct_response" = "200" ] || [ "$direct_response" = "301" ] || [ "$direct_response" = "302" ]; then - log_info "✅ 直连8443端口正常 · 状态码: $direct_response" - log_info " → Nginx SSL配置正确,等Xray运行后HTTPS即可正常" - else - log_warn " 直连8443也失败 · 状态码: ${direct_response:-无响应}" - fi + log_warn " → 尝试访问: $test_url" + log_warn " → 可能需要等待DNS传播" fi # 检查证书信息 - echo | openssl s_client -servername "$domain" -connect "${domain}:443" 2>/dev/null | \ + echo | openssl s_client -servername "$domain" -connect "${domain}:${ssl_port}" 2>/dev/null | \ openssl x509 -noout -subject -issuer -dates 2>/dev/null || true - # 测试HTTP→HTTPS重定向 - local redirect - redirect=$(curl -sf -o /dev/null -w "%{redirect_url}" "http://${domain}/" 2>/dev/null) - if echo "$redirect" | grep -q "https"; then - log_info "✅ HTTP→HTTPS重定向正常" + # 如果是标准443端口,测试HTTP→HTTPS重定向 + if [ "$ssl_port" = "443" ]; then + local redirect + redirect=$(curl -sf -o /dev/null -w "%{redirect_url}" "http://${domain}/" 2>/dev/null) + if echo "$redirect" | grep -q "https"; then + log_info "✅ HTTP→HTTPS重定向正常" + else + log_warn "HTTP→HTTPS重定向未生效 (可能需要等待DNS)" + fi else - log_warn "HTTP→HTTPS重定向未生效 (可能需要等待DNS)" + log_info "ℹ️ Xray占用443端口,HTTP不会重定向到HTTPS" + log_info " → 网站HTTP访问: http://${domain} (端口80)" + log_info " → 网站HTTPS访问: https://${domain}:${ssl_port}" + log_info " → VPN: 通过Xray端口443正常工作" fi }