guanghulab/server/proxy/setup/setup-cn-relay.sh

406 lines
14 KiB
Bash
Raw Permalink Normal View History

#!/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 "════════════════════════════════════════"