guanghulab/server/proxy/setup/setup-cn-relay.sh
2026-05-10 13:12:44 +08:00

406 lines
14 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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
# 注意: 不使用 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 <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
# 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 "════════════════════════════════════════"