#!/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 # 注意: 不使用 set -e,因为 grep/nc 等命令在无匹配时返回非零 # 关键步骤手动检查错误 # 颜色 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"; } # 查找ngx_stream_module.so文件路径 find_stream_module_so() { for mod_path in \ /usr/lib/nginx/modules/ngx_stream_module.so \ /usr/lib64/nginx/modules/ngx_stream_module.so \ /etc/nginx/modules/ngx_stream_module.so \ /usr/share/nginx/modules/ngx_stream_module.so; do if [ -f "$mod_path" ]; then echo "$mod_path" return fi done # 使用find作为最后回退手段 (限制深度避免扫描过慢) find /usr/lib /usr/lib64 /usr/share /etc/nginx -maxdepth 3 -name "ngx_stream_module.so" -print -quit 2>/dev/null } # 尝试从modules-available启用stream模块 enable_stream_from_modules_available() { for avail in /usr/share/nginx/modules-available/mod-stream.conf \ /etc/nginx/modules-available/mod-stream.conf; do if [ -f "$avail" ]; then ln -sf "$avail" /etc/nginx/modules-enabled/ log_info "✅ 已从modules-available启用stream模块" return 0 fi done return 1 } # 确保stream模块已加载到nginx配置中 ensure_stream_module_loaded() { local nginx_conf="$1" # 检查modules-enabled是否已自动加载 if [ -d /etc/nginx/modules-enabled ]; then if grep -q "ngx_stream_module" /etc/nginx/modules-enabled/* 2>/dev/null; then log_info "✅ stream模块已通过modules-enabled自动加载" return 0 fi fi # 检查nginx.conf是否已包含load_module指令 if grep -q "ngx_stream_module" "$nginx_conf" 2>/dev/null; then log_info "✅ stream动态模块已在nginx.conf中加载" return 0 fi # 查找并加载.so文件 local module_path module_path=$(find_stream_module_so) if [ -n "$module_path" ]; then log_info "加载stream动态模块: $module_path" sed -i "1i load_module $module_path;" "$nginx_conf" log_info "✅ stream动态模块已加载" return 0 fi # 尝试从modules-available启用 log_warn "未找到ngx_stream_module.so文件" log_warn "尝试启用modules-available中的stream配置..." if enable_stream_from_modules_available; then return 0 fi log_warn "stream模块可能为静态编译,继续配置..." return 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 # Ubuntu/Debian: stream模块通常是独立的动态模块包 (libnginx-mod-stream) # 必须显式安装,即使nginx -V显示--with-stream也只表示编译支持,不代表模块已安装 log_info "确保stream动态模块包已安装..." apt-get install -y -qq libnginx-mod-stream 2>/dev/null || true # 验证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 # 检查是否需要动态加载stream模块 # Ubuntu/Debian的Nginx通常将stream编译为动态模块(.so),需要用load_module显式加载 # 有两种加载机制: # 1. modules-enabled目录: /etc/nginx/modules-enabled/ (Debian/Ubuntu自动加载) # 2. 手动load_module: 在nginx.conf顶部显式加载.so文件 NGINX_MAIN="/etc/nginx/nginx.conf" ensure_stream_module_loaded "$NGINX_MAIN" # ── §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块 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 测试并应用配置" NGINX_TEST_OUTPUT=$(nginx -t 2>&1) if echo "$NGINX_TEST_OUTPUT" | grep -q "test is successful"; then # reload 只能用于已运行的Nginx,如果Nginx未运行则需要 start if systemctl is-active --quiet nginx; then systemctl reload nginx log_info "✅ Nginx配置测试通过 · 已重载" else systemctl enable nginx 2>/dev/null || true systemctl start nginx if systemctl is-active --quiet nginx; then log_info "✅ Nginx配置测试通过 · 已启动" else log_error "Nginx启动失败" systemctl status nginx --no-pager 2>&1 || true exit 1 fi fi else log_warn "Nginx配置测试未通过,尝试自动修复..." echo "$NGINX_TEST_OUTPUT" # 修复: 如果stream指令未知,说明load_module缺失 if echo "$NGINX_TEST_OUTPUT" | grep -q 'unknown directive "stream"'; then log_info "检测到stream指令未识别,尝试修复模块加载..." # 强制安装stream模块包 apt-get update -qq apt-get install -y -qq libnginx-mod-stream 2>/dev/null || true # 使用共享函数确保模块已加载 ensure_stream_module_loaded "$NGINX_MAIN" fi # 重新测试 if nginx -t 2>&1; then if systemctl is-active --quiet nginx; then systemctl reload nginx log_info "✅ 自动修复成功 · Nginx已重载" else systemctl enable nginx 2>/dev/null || true systemctl start nginx log_info "✅ 自动修复成功 · Nginx已启动" fi else log_error "Nginx配置测试失败 (自动修复未能解决)" nginx -t 2>&1 echo "" log_error "请检查以下可能原因:" echo " 1. libnginx-mod-stream 包是否可用: dpkg -l | grep libnginx" echo " 2. 模块文件是否存在: find / -name 'ngx_stream_module.so' 2>/dev/null" echo " 3. nginx.conf内容: cat /etc/nginx/nginx.conf" echo " 4. modules-enabled目录: ls -la /etc/nginx/modules-enabled/" exit 1 fi 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 # 确认Nginx真正在运行 if ! systemctl is-active --quiet nginx; then log_warn "Nginx未运行,尝试启动..." systemctl enable nginx 2>/dev/null || true systemctl start nginx sleep 2 fi HEALTH_OK=true # 检查中转端口 if ss -tlnp | grep -q ":${RELAY_PORT}[[:space:]]"; then log_info "✅ 中转端口 ${RELAY_PORT}: 监听中" else log_error "中转端口 ${RELAY_PORT}: 未监听" HEALTH_OK=false fi # 检查HTTP if curl -sf http://127.0.0.1/health >/dev/null 2>&1; then log_info "✅ HTTP健康检查: 正常" else log_warn "HTTP健康检查: 未响应 (可能80端口被占用)" fi # 如果关键端口未监听,报告Nginx状态用于调试 if [ "$HEALTH_OK" = "false" ]; then log_error "关键服务未就绪 · Nginx诊断信息:" systemctl status nginx --no-pager 2>&1 || true echo "" echo "Nginx错误日志 (最后10行):" tail -10 /var/log/nginx/error.log 2>/dev/null || echo " (无日志)" exit 1 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 "════════════════════════════════════════"