fix: restore Reality dest to microsoft.com + add CN relay architecture

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>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-31 08:24:52 +00:00 committed by GitHub
parent a3b30d50b9
commit eaa6c89864
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 555 additions and 145 deletions

View File

@ -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

View File

@ -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*

View File

@ -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
# ═══════════════════════════════════════════════════════════════

View File

@ -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",

View File

@ -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

View File

@ -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服务

View File

@ -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 <SG_SERVER_IP>
# 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 <SG_SERVER_IP>"
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 "════════════════════════════════════════"

View File

@ -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应占用443Nginx 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
}