Compare commits
3 Commits
324483eabd
...
b64997ae17
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b64997ae17 | ||
|
|
4f6c5d7b5c | ||
|
|
91600c4abe |
104
antigravity/maintenance/setup-node1-shanghai.sh
Executable file
104
antigravity/maintenance/setup-node1-shanghai.sh
Executable file
@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
# =============================================================
|
||||
# 节点 1:上海服务器
|
||||
# 部署:sub2api + node-tls-proxy + postgres + redis
|
||||
# =============================================================
|
||||
# 用法:bash setup-node1-shanghai.sh
|
||||
# 前置:已安装 Docker,已克隆仓库到当前目录
|
||||
|
||||
set -euo pipefail
|
||||
GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m'
|
||||
ok() { echo -e "${GREEN}✅ $*${NC}"; }
|
||||
info() { echo -e "${YELLOW}ℹ $*${NC}"; }
|
||||
|
||||
echo "================================================"
|
||||
echo " 节点1:上海服务器 部署"
|
||||
echo "================================================"
|
||||
|
||||
# ── 1. 检查 Docker ─────────────────────────────────
|
||||
if ! command -v docker &>/dev/null; then
|
||||
info "安装 Docker..."
|
||||
curl -fsSL https://get.docker.com | bash
|
||||
systemctl enable docker && systemctl start docker
|
||||
fi
|
||||
ok "Docker 已就绪"
|
||||
|
||||
# ── 2. 进入 deploy 目录 ─────────────────────────────
|
||||
# 兼容从仓库根目录执行(/root/sub2api/)或脚本原始位置(antigravity/maintenance/)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -d "$SCRIPT_DIR/deploy" ]; then
|
||||
DEPLOY_DIR="$SCRIPT_DIR/deploy"
|
||||
elif [ -d "$(dirname "$SCRIPT_DIR")/deploy" ]; then
|
||||
DEPLOY_DIR="$(dirname "$SCRIPT_DIR")/deploy"
|
||||
elif [ -d "$(dirname "$(dirname "$SCRIPT_DIR")")/deploy" ]; then
|
||||
DEPLOY_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")/deploy"
|
||||
elif [ -d "$(pwd)/deploy" ]; then
|
||||
DEPLOY_DIR="$(pwd)/deploy"
|
||||
else
|
||||
echo "❌ 找不到 deploy/ 目录,请在仓库根目录执行脚本"
|
||||
exit 1
|
||||
fi
|
||||
cd "$DEPLOY_DIR"
|
||||
ok "工作目录: $DEPLOY_DIR"
|
||||
|
||||
# ── 3. 生成 .env(如不存在)──────────────────────────
|
||||
if [ ! -f .env ]; then
|
||||
cat > .env << EOF
|
||||
# ========== 必填 ==========
|
||||
POSTGRES_PASSWORD=$(openssl rand -hex 16)
|
||||
ADMIN_EMAIL=admin@sub2api.local
|
||||
ADMIN_PASSWORD=$(openssl rand -hex 8)
|
||||
JWT_SECRET=$(openssl rand -hex 32)
|
||||
TOTP_ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
|
||||
# ========== 时区(上海)==========
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
# ========== node-tls-proxy 指向 CN中转机 ==========
|
||||
# 上海的 sub2api 通过 GOST 把 TLS 流量送到 CN中转,
|
||||
# 中转再转发到美国落地,最终到 Anthropic/Google
|
||||
# 这里填 CN中转机 IP + GOST 暴露给上海的端口
|
||||
GATEWAY_NODE_TLS_PROXY_ENABLED=true
|
||||
GATEWAY_NODE_TLS_PROXY_LISTEN_HOST=<CN中转机IP>
|
||||
GATEWAY_NODE_TLS_PROXY_LISTEN_PORT=3456
|
||||
|
||||
# ========== Gemini OAuth(如有)==========
|
||||
GEMINI_CLI_OAUTH_CLIENT_SECRET=
|
||||
ANTIGRAVITY_OAUTH_CLIENT_SECRET=
|
||||
EOF
|
||||
ok ".env 已生成,请编辑填入 CN中转机 IP"
|
||||
echo ""
|
||||
echo " → 编辑: nano $DEPLOY_DIR/.env"
|
||||
echo " → 修改 GATEWAY_NODE_TLS_PROXY_LISTEN_HOST=<CN中转机IP>"
|
||||
echo ""
|
||||
read -rp "填完后按 Enter 继续..." _
|
||||
fi
|
||||
|
||||
# ── 4. 启动服务 ─────────────────────────────────────
|
||||
info "启动 sub2api + node-tls-proxy..."
|
||||
docker compose -f docker-compose.yml \
|
||||
-f docker-compose.tls-proxy.yml \
|
||||
pull
|
||||
docker compose -f docker-compose.yml \
|
||||
-f docker-compose.tls-proxy.yml \
|
||||
up -d
|
||||
|
||||
ok "服务启动完成"
|
||||
|
||||
# ── 5. 验证 ────────────────────────────────────────
|
||||
sleep 10
|
||||
echo ""
|
||||
echo "【验证】"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
echo ""
|
||||
if curl -sf http://127.0.0.1:8080/health >/dev/null 2>&1; then
|
||||
ok "sub2api 健康检查通过(端口 8080)"
|
||||
else
|
||||
echo "⏳ sub2api 还在启动,等 30 秒后手动检查..."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo " 节点1 部署完成"
|
||||
echo " 管理面板: http://$(curl -sf ipinfo.io/ip 2>/dev/null || echo '<服务器IP>'):8080"
|
||||
echo "================================================"
|
||||
96
antigravity/maintenance/setup-node2-cn-relay.sh
Executable file
96
antigravity/maintenance/setup-node2-cn-relay.sh
Executable file
@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
# =============================================================
|
||||
# 节点 2:海外 CN 中转机
|
||||
# 部署:GOST 双向中转
|
||||
# 接收上海: relay+tls :3456 → 转发到美国落地 :8443
|
||||
# =============================================================
|
||||
# 用法:bash setup-node2-cn-relay.sh
|
||||
|
||||
set -euo pipefail
|
||||
GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m'
|
||||
ok() { echo -e "${GREEN}✅ $*${NC}"; }
|
||||
info() { echo -e "${YELLOW}ℹ $*${NC}"; }
|
||||
fail() { echo -e "${RED}❌ $*${NC}"; }
|
||||
|
||||
# ── 配置(修改这里)──────────────────────────────────
|
||||
US_LANDING_IP="${US_LANDING_IP:-}" # 美国落地机 IP
|
||||
GOST_USER="${GOST_USER:-gostuser}"
|
||||
GOST_PASS="${GOST_PASS:-$(openssl rand -hex 8)}"
|
||||
LISTEN_PORT_FROM_SH="${LISTEN_PORT_FROM_SH:-3456}" # 接收上海的端口
|
||||
LISTEN_PORT_TO_US="${LISTEN_PORT_TO_US:-8443}" # 美国落地机监听端口
|
||||
|
||||
echo "================================================"
|
||||
echo " 节点2:海外CN中转机 部署"
|
||||
echo "================================================"
|
||||
|
||||
# 检查必填
|
||||
if [ -z "$US_LANDING_IP" ]; then
|
||||
read -rp "请输入美国落地机 IP: " US_LANDING_IP
|
||||
fi
|
||||
|
||||
# ── 1. 安装 GOST ────────────────────────────────────
|
||||
if ! command -v gost &>/dev/null; then
|
||||
info "安装 GOST..."
|
||||
ARCH=$(uname -m)
|
||||
[ "$ARCH" = "x86_64" ] && GARCH="amd64" || GARCH="arm64"
|
||||
LATEST=$(curl -sf https://api.github.com/repos/go-gost/gost/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
|
||||
wget -qO /tmp/gost.tar.gz \
|
||||
"https://github.com/go-gost/gost/releases/download/${LATEST}/gost_linux_${GARCH}.tar.gz"
|
||||
tar xzf /tmp/gost.tar.gz -C /tmp/
|
||||
mv /tmp/gost /usr/local/bin/gost
|
||||
chmod +x /usr/local/bin/gost
|
||||
fi
|
||||
ok "GOST $(gost -V 2>/dev/null | head -1 || echo '已安装')"
|
||||
|
||||
# ── 2. 创建 Systemd 服务 ────────────────────────────
|
||||
# 中转机职责:
|
||||
# - 接收上海 sub2api 发来的 relay+tls 连接(:3456)
|
||||
# - 将流量通过 relay+tls 转发到美国落地机(:8443)
|
||||
cat > /etc/systemd/system/gost-relay.service << EOF
|
||||
[Unit]
|
||||
Description=GOST CN Relay - 接收上海转发到美国落地
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=nobody
|
||||
ExecStart=/usr/local/bin/gost \\
|
||||
-L "relay+tls://${GOST_USER}:${GOST_PASS}@:${LISTEN_PORT_FROM_SH}" \\
|
||||
-F "relay+tls://${GOST_USER}:${GOST_PASS}@${US_LANDING_IP}:${LISTEN_PORT_TO_US}"
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable gost-relay
|
||||
systemctl restart gost-relay
|
||||
sleep 2
|
||||
ok "GOST 中转服务已启动"
|
||||
|
||||
# ── 3. 防火墙开放端口 ───────────────────────────────
|
||||
if command -v ufw &>/dev/null; then
|
||||
ufw allow "${LISTEN_PORT_FROM_SH}/tcp" comment "GOST from Shanghai" 2>/dev/null || true
|
||||
ufw allow ssh 2>/dev/null || true
|
||||
ok "ufw 端口已开放"
|
||||
fi
|
||||
|
||||
# ── 4. 输出上海配置 ─────────────────────────────────
|
||||
MY_IP=$(curl -sf ipinfo.io/ip 2>/dev/null || echo '<本机IP>')
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo " 节点2 部署完成"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "【上海服务器 .env 填写以下值】"
|
||||
echo " GATEWAY_NODE_TLS_PROXY_LISTEN_HOST=${MY_IP}"
|
||||
echo " GATEWAY_NODE_TLS_PROXY_LISTEN_PORT=${LISTEN_PORT_FROM_SH}"
|
||||
echo ""
|
||||
echo "【GOST 认证信息(勿泄露)】"
|
||||
echo " 用户名: ${GOST_USER}"
|
||||
echo " 密码: ${GOST_PASS}"
|
||||
echo ""
|
||||
systemctl status gost-relay --no-pager -l | tail -5
|
||||
143
antigravity/maintenance/setup-node3-us-landing.sh
Executable file
143
antigravity/maintenance/setup-node3-us-landing.sh
Executable file
@ -0,0 +1,143 @@
|
||||
#!/bin/bash
|
||||
# =============================================================
|
||||
# 节点 3:美国落地机(Debian 12,洛杉矶)
|
||||
# 部署:GOST 出口 + TCP 指纹伪装
|
||||
# 接收 CN中转 relay+tls :8443 → 直连 Anthropic/Google
|
||||
# =============================================================
|
||||
# 用法:sudo bash setup-node3-us-landing.sh
|
||||
|
||||
set -euo pipefail
|
||||
GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m'
|
||||
ok() { echo -e "${GREEN}✅ $*${NC}"; }
|
||||
info() { echo -e "${YELLOW}ℹ $*${NC}"; }
|
||||
fail() { echo -e "${RED}❌ $*${NC}"; }
|
||||
|
||||
GOST_USER="${GOST_USER:-gostuser}"
|
||||
GOST_PASS="${GOST_PASS:-}" # 与 CN中转机相同,启动时填写
|
||||
LISTEN_PORT="${LISTEN_PORT:-8443}"
|
||||
|
||||
echo "================================================"
|
||||
echo " 节点3:美国落地机 部署(Debian 12 / LA)"
|
||||
echo "================================================"
|
||||
|
||||
[ "$(id -u)" != "0" ] && { fail "请用 sudo 执行"; exit 1; }
|
||||
|
||||
# ── 1. 系统更新 ─────────────────────────────────────
|
||||
info "更新系统包..."
|
||||
apt-get update -qq && apt-get upgrade -y -qq
|
||||
ok "系统已更新"
|
||||
|
||||
# ── 2. TCP 指纹伪装(macOS 特征)──────────────────────
|
||||
info "应用 TCP 指纹伪装..."
|
||||
|
||||
# 实时生效
|
||||
sysctl -w net.ipv4.ip_default_ttl=64 # TTL=64(macOS 标准)
|
||||
sysctl -w net.ipv4.tcp_timestamps=0 # 禁用 TCP 时间戳(防 uptime 推算)
|
||||
sysctl -w net.ipv4.tcp_window_scaling=1 # 窗口扩展(macOS 开启)
|
||||
sysctl -w net.ipv4.tcp_rmem="4096 65535 6291456" # 接收窗口65535(macOS默认)
|
||||
sysctl -w net.ipv4.tcp_wmem="4096 65535 6291456" # 发送窗口65535
|
||||
sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
||||
|
||||
# BBR 拥塞控制(降低丢包,提高吞吐)
|
||||
sysctl -w net.core.default_qdisc=fq
|
||||
sysctl -w net.ipv4.tcp_congestion_control=bbr
|
||||
|
||||
# 持久化到 sysctl.conf
|
||||
cat >> /etc/sysctl.conf << 'EOF'
|
||||
|
||||
# ── Antigravity macOS TCP Fingerprint ──
|
||||
net.ipv4.ip_default_ttl=64
|
||||
net.ipv4.tcp_timestamps=0
|
||||
net.ipv4.tcp_window_scaling=1
|
||||
net.ipv4.tcp_rmem=4096 65535 6291456
|
||||
net.ipv4.tcp_wmem=4096 65535 6291456
|
||||
net.ipv6.conf.all.disable_ipv6=1
|
||||
net.ipv6.conf.default.disable_ipv6=1
|
||||
net.core.default_qdisc=fq
|
||||
net.ipv4.tcp_congestion_control=bbr
|
||||
EOF
|
||||
sysctl -p > /dev/null 2>&1 || true
|
||||
ok "TCP 指纹伪装已应用(TTL=64, Window=65535, 时间戳禁用)"
|
||||
|
||||
# ── 3. 时区(洛杉矶,匹配落地 IP 地理位置)─────────────
|
||||
timedatectl set-timezone America/Los_Angeles
|
||||
ok "时区已设置: $(date)"
|
||||
|
||||
# ── 4. 安装 GOST ────────────────────────────────────
|
||||
if ! command -v gost &>/dev/null; then
|
||||
info "安装 GOST..."
|
||||
ARCH=$(uname -m)
|
||||
[ "$ARCH" = "x86_64" ] && GARCH="amd64" || GARCH="arm64"
|
||||
LATEST=$(curl -sf https://api.github.com/repos/go-gost/gost/releases/latest \
|
||||
| grep '"tag_name"' | cut -d'"' -f4)
|
||||
wget -qO /tmp/gost.tar.gz \
|
||||
"https://github.com/go-gost/gost/releases/download/${LATEST}/gost_linux_${GARCH}.tar.gz"
|
||||
tar xzf /tmp/gost.tar.gz -C /tmp/
|
||||
mv /tmp/gost /usr/local/bin/gost
|
||||
chmod +x /usr/local/bin/gost
|
||||
fi
|
||||
ok "GOST $(gost -V 2>/dev/null | head -1 || echo '已安装')"
|
||||
|
||||
# ── 5. 填写 GOST 密码 ──────────────────────────────
|
||||
if [ -z "$GOST_PASS" ]; then
|
||||
read -rp "请输入 GOST 密码(与 CN中转机相同): " GOST_PASS
|
||||
fi
|
||||
|
||||
# ── 6. 创建 GOST 出口服务 ──────────────────────────
|
||||
# 落地机职责:监听 CN中转机 relay+tls 连接,直接出口到 Anthropic/Google
|
||||
cat > /etc/systemd/system/gost-exit.service << EOF
|
||||
[Unit]
|
||||
Description=GOST US Landing Exit - 接收中转,直连 Anthropic/Google
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=nobody
|
||||
# 监听 CN中转机的连接,透传到最终目标(relay 模式自动解析目标地址)
|
||||
ExecStart=/usr/local/bin/gost -L "relay+tls://${GOST_USER}:${GOST_PASS}@:${LISTEN_PORT}"
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable gost-exit
|
||||
systemctl restart gost-exit
|
||||
sleep 2
|
||||
ok "GOST 出口服务已启动"
|
||||
|
||||
# ── 7. 防火墙 ──────────────────────────────────────
|
||||
if command -v ufw &>/dev/null; then
|
||||
ufw allow ssh
|
||||
ufw allow "${LISTEN_PORT}/tcp" comment "GOST from CN Relay"
|
||||
ufw --force enable
|
||||
ok "防火墙已配置(只开放 SSH + $LISTEN_PORT)"
|
||||
fi
|
||||
|
||||
# ── 8. 验证 ───────────────────────────────────────
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo " 节点3 部署完成"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "【验证指纹伪装】"
|
||||
echo " TTL: $(sysctl -n net.ipv4.ip_default_ttl) (应为 64)"
|
||||
echo " TCP 时间戳: $(sysctl -n net.ipv4.tcp_timestamps) (应为 0)"
|
||||
echo " 时区: $(timedatectl show -p Timezone --value)"
|
||||
echo " 当前时间: $(date)"
|
||||
echo ""
|
||||
echo "【GOST 服务状态】"
|
||||
systemctl status gost-exit --no-pager -l | tail -5
|
||||
echo ""
|
||||
echo "【出口 IP 信息】"
|
||||
curl -sf ipinfo.io 2>/dev/null | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
print(f' IP: {d.get(\"ip\")}')
|
||||
print(f' ISP: {d.get(\"org\")}')
|
||||
print(f' 城市: {d.get(\"city\")}, {d.get(\"region\")}')
|
||||
" || echo " (获取 IP 信息失败)"
|
||||
184
antigravity/maintenance/test-linux.sh
Executable file
184
antigravity/maintenance/test-linux.sh
Executable file
@ -0,0 +1,184 @@
|
||||
#!/bin/bash
|
||||
# test-linux.sh — Linux 服务器全量指纹验证脚本
|
||||
# 用途:验证所有 TCP/OS 层伪装 + Node.js proxy 状态
|
||||
# 运行方式:sudo bash test-linux.sh
|
||||
#
|
||||
# 注意:sysctl 和 iptables 检查需要 sudo
|
||||
|
||||
set -euo pipefail
|
||||
GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' NC='\033[0m'
|
||||
ok() { echo -e "${GREEN}✅ $*${NC}"; }
|
||||
fail() { echo -e "${RED}❌ $*${NC}"; }
|
||||
info() { echo -e "${YELLOW}ℹ $*${NC}"; }
|
||||
|
||||
PROXY_PORT="${PROXY_PORT:-3456}"
|
||||
|
||||
echo "══════════════════════════════════════════════"
|
||||
echo " Linux 服务器指纹伪装验证"
|
||||
echo "══════════════════════════════════════════════"
|
||||
|
||||
# ── 1. 时区 ────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "【1】系统时区"
|
||||
TZ_NOW=$(timedatectl show -p Timezone --value 2>/dev/null || cat /etc/timezone 2>/dev/null || date +%Z)
|
||||
echo " 当前时区: $TZ_NOW"
|
||||
echo " 当前时间: $(date)"
|
||||
if [[ "$TZ_NOW" == "America/New_York" ]]; then
|
||||
ok "时区正确(America/New_York)"
|
||||
else
|
||||
fail "时区错误(应为 America/New_York,当前: $TZ_NOW)"
|
||||
info "修复: sudo timedatectl set-timezone America/New_York"
|
||||
fi
|
||||
|
||||
# ── 2. TCP 时间戳 ──────────────────────────────────────────
|
||||
echo ""
|
||||
echo "【2】TCP 时间戳(防 uptime 推算)"
|
||||
TS=$(sysctl -n net.ipv4.tcp_timestamps 2>/dev/null || echo "unknown")
|
||||
echo " net.ipv4.tcp_timestamps = $TS"
|
||||
if [[ "$TS" == "0" ]]; then
|
||||
ok "TCP 时间戳已禁用"
|
||||
else
|
||||
fail "TCP 时间戳未禁用(当前: $TS,应为 0)"
|
||||
info "修复: sudo sysctl -w net.ipv4.tcp_timestamps=0"
|
||||
fi
|
||||
|
||||
# ── 3. TTL ─────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "【3】出站 TTL(macOS 特征)"
|
||||
TTL=$(sysctl -n net.ipv4.ip_default_ttl 2>/dev/null || echo "unknown")
|
||||
echo " net.ipv4.ip_default_ttl = $TTL"
|
||||
if [[ "$TTL" == "64" ]]; then
|
||||
ok "TTL=64(macOS/Linux 标准值)"
|
||||
else
|
||||
fail "TTL 不为 64(当前: $TTL)"
|
||||
fi
|
||||
|
||||
# ── 4. TCP Window Size ─────────────────────────────────────
|
||||
echo ""
|
||||
echo "【4】TCP Window Size(macOS 特征)"
|
||||
RMEM=$(sysctl -n net.ipv4.tcp_rmem 2>/dev/null || echo "unknown")
|
||||
echo " net.ipv4.tcp_rmem = $RMEM"
|
||||
if [[ "$RMEM" == *"65535"* ]]; then
|
||||
ok "Window Size 包含 65535(macOS 特征)"
|
||||
else
|
||||
fail "Window Size 未伪装(应含 65535,当前: $RMEM)"
|
||||
info "修复: sudo sysctl -w net.ipv4.tcp_rmem='4096 65535 6291456'"
|
||||
fi
|
||||
|
||||
# ── 5. iptables 规则 ───────────────────────────────────────
|
||||
echo ""
|
||||
echo "【5】iptables 指纹防护链"
|
||||
if iptables -L MG_FINGERPRINT -n 2>/dev/null | grep -q "MG:"; then
|
||||
ok "MG_FINGERPRINT 链存在"
|
||||
RULES=$(iptables -L MG_FINGERPRINT -n 2>/dev/null | grep -c "MG:" || echo 0)
|
||||
echo " 规则数: $RULES 条"
|
||||
else
|
||||
fail "MG_FINGERPRINT 链不存在,运行 setup-firewall.sh apply"
|
||||
fi
|
||||
|
||||
if iptables -t mangle -L MG_FINGERPRINT_TTL -n 2>/dev/null | grep -q "TTL"; then
|
||||
ok "TTL mangle 链存在"
|
||||
else
|
||||
fail "TTL mangle 链不存在"
|
||||
fi
|
||||
|
||||
# ── 6. QUIC 阻断验证 ───────────────────────────────────────
|
||||
echo ""
|
||||
echo "【6】QUIC/UDP 阻断"
|
||||
if iptables -L MG_FINGERPRINT -n 2>/dev/null | grep -q "udp.*443.*DROP"; then
|
||||
ok "UDP 443 QUIC 已阻断"
|
||||
else
|
||||
fail "UDP 443 未阻断"
|
||||
fi
|
||||
|
||||
# ── 7. Node.js 版本 ────────────────────────────────────────
|
||||
echo ""
|
||||
echo "【7】Node.js 版本"
|
||||
if command -v node &>/dev/null; then
|
||||
NODE_VER=$(node --version)
|
||||
echo " Node.js: $NODE_VER"
|
||||
if [[ "$NODE_VER" == v22* ]]; then
|
||||
ok "Node.js v22.x — 与 Claude CLI 版本匹配"
|
||||
else
|
||||
fail "Node.js 不是 v22.x(当前: $NODE_VER),JA4 指纹可能不匹配"
|
||||
fi
|
||||
else
|
||||
info "Node.js 未在宿主机安装(Docker 部署无需宿主机 Node)"
|
||||
fi
|
||||
|
||||
# ── 8. node-tls-proxy 健康 ─────────────────────────────────
|
||||
echo ""
|
||||
echo "【8】node-tls-proxy 健康(端口 $PROXY_PORT)"
|
||||
if curl -sf "http://127.0.0.1:${PROXY_PORT}/__health" -o /tmp/health.json 2>/dev/null; then
|
||||
NODE_IN_PROXY=$(python3 -c "import json; d=json.load(open('/tmp/health.json')); print(d.get('node','?'))" 2>/dev/null)
|
||||
SESSIONS=$(python3 -c "import json; d=json.load(open('/tmp/health.json')); print(d.get('sessions',0))" 2>/dev/null)
|
||||
H2HOSTS=$(python3 -c "import json; d=json.load(open('/tmp/health.json')); print(','.join(d.get('h2Hosts',[])))" 2>/dev/null)
|
||||
TELEMETRY=$(python3 -c "import json; d=json.load(open('/tmp/health.json')); print(d.get('telemetry','?'))" 2>/dev/null)
|
||||
ok "Proxy 运行正常"
|
||||
echo " Node版本: $NODE_IN_PROXY"
|
||||
echo " Sessions: $SESSIONS"
|
||||
echo " H2已建立: ${H2HOSTS:-(无,首次请求后会建立)}"
|
||||
echo " 遥测: $TELEMETRY"
|
||||
if [[ "$NODE_IN_PROXY" == v22* ]]; then
|
||||
ok "Proxy 内置 Node.js v22.x ✅"
|
||||
else
|
||||
fail "Proxy 内置 Node 版本: $NODE_IN_PROXY(应为 v22.x)"
|
||||
fi
|
||||
else
|
||||
fail "Proxy 未响应(端口 $PROXY_PORT)"
|
||||
info "检查: docker ps | grep node-tls-proxy"
|
||||
fi
|
||||
|
||||
# ── 9. Node.js JA4 指纹(在 proxy 容器内测) ──────────────
|
||||
echo ""
|
||||
echo "【9】Node.js JA4 TLS 指纹"
|
||||
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "node-tls-proxy"; then
|
||||
JA4=$(docker exec node-tls-proxy node -e "
|
||||
const https = require('https');
|
||||
https.get('https://tls.peet.ws/api/all', res => {
|
||||
let d=''; res.on('data',c=>d+=c);
|
||||
res.on('end',()=>{ try{console.log(JSON.parse(d).tls.ja4);}catch(e){console.log('err');} });
|
||||
}).on('error',e=>console.log('err:'+e.message));
|
||||
" 2>/dev/null || echo "exec_failed")
|
||||
echo " Proxy JA4: $JA4"
|
||||
if [[ "$JA4" == t13* ]]; then
|
||||
ok "JA4 指纹正常(TLS 1.3)"
|
||||
else
|
||||
fail "JA4 获取失败: $JA4"
|
||||
fi
|
||||
elif command -v node &>/dev/null; then
|
||||
JA4=$(node -e "
|
||||
const https = require('https');
|
||||
https.get('https://tls.peet.ws/api/all', res => {
|
||||
let d=''; res.on('data',c=>d+=c);
|
||||
res.on('end',()=>{ try{console.log(JSON.parse(d).tls.ja4);}catch(e){console.log('err');} });
|
||||
}).on('error',e=>console.log('err:'+e.message));
|
||||
" 2>/dev/null || echo "err")
|
||||
echo " 宿主机 JA4: $JA4"
|
||||
[[ "$JA4" == t13* ]] && ok "JA4 正常" || fail "JA4 失败"
|
||||
else
|
||||
info "跳过 JA4 测(无 docker exec 也无宿主机 node)"
|
||||
fi
|
||||
|
||||
# ── 10. 出口 IP 验证 ───────────────────────────────────────
|
||||
echo ""
|
||||
echo "【10】出口 IP 信息"
|
||||
IP_INFO=$(curl -sf --max-time 5 "https://ipinfo.io/json" 2>/dev/null || echo '{}')
|
||||
IP=$(echo "$IP_INFO" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('ip','?'))" 2>/dev/null)
|
||||
ORG=$(echo "$IP_INFO" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('org','?'))" 2>/dev/null)
|
||||
CITY=$(echo "$IP_INFO" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('city','?')+', '+d.get('region','?'))" 2>/dev/null)
|
||||
echo " IP: $IP"
|
||||
echo " ISP: $ORG"
|
||||
echo " 城市: $CITY"
|
||||
if echo "$ORG" | grep -qiE "residential|comcast|verizon|optimum|spectrum|altice|fios|att|xfinity"; then
|
||||
ok "ISP 看起来是住宅宽带 ✅"
|
||||
elif echo "$ORG" | grep -qiE "datacenter|hosting|cloud|amazon|google|microsoft|linode|vultr|digital"; then
|
||||
fail "ISP 是数据中心 IP,建议换住宅宽带"
|
||||
else
|
||||
info "ISP 未能自动判断,请人工核查: $ORG"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "══════════════════════════════════════════════"
|
||||
echo " 验证完成"
|
||||
echo "══════════════════════════════════════════════"
|
||||
117
antigravity/maintenance/test-mac.sh
Executable file
117
antigravity/maintenance/test-mac.sh
Executable file
@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
# test-mac.sh — 本机(Mac)指纹验证脚本
|
||||
# 用途:验证 Node.js TLS 指纹、H2 连通性
|
||||
# 运行方式:bash test-mac.sh
|
||||
|
||||
set -euo pipefail
|
||||
GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' NC='\033[0m'
|
||||
ok() { echo -e "${GREEN}✅ $*${NC}"; }
|
||||
fail() { echo -e "${RED}❌ $*${NC}"; }
|
||||
info() { echo -e "${YELLOW}ℹ $*${NC}"; }
|
||||
|
||||
echo "══════════════════════════════════════"
|
||||
echo " MAC 本地指纹验证"
|
||||
echo "══════════════════════════════════════"
|
||||
|
||||
# ── 1. Node.js 版本 ────────────────────────────────────────
|
||||
echo ""
|
||||
echo "【1】Node.js 版本"
|
||||
NODE_VER=$(node --version 2>/dev/null || echo "未安装")
|
||||
echo " 本机 Node: $NODE_VER"
|
||||
if [[ "$NODE_VER" == v22* ]]; then
|
||||
ok "Node.js v22.x — 与 Claude CLI 内置版本匹配"
|
||||
else
|
||||
fail "Node.js 版本不是 v22.x(当前: $NODE_VER),JA4 指纹可能不一致"
|
||||
info "安装 v22: nvm install 22 && nvm use 22"
|
||||
fi
|
||||
|
||||
# ── 2. Node.js JA4 TLS 指纹 ────────────────────────────────
|
||||
echo ""
|
||||
echo "【2】Node.js TLS JA4 指纹(服务器实际产生的指纹)"
|
||||
JA4=$(node -e "
|
||||
const https = require('https');
|
||||
https.get('https://tls.peet.ws/api/all', res => {
|
||||
let d=''; res.on('data',c=>d+=c);
|
||||
res.on('end',()=>{
|
||||
try { console.log(JSON.parse(d).tls.ja4); } catch(e){ console.log('parse_error'); }
|
||||
});
|
||||
}).on('error', e => console.log('error:'+e.message));
|
||||
" 2>/dev/null)
|
||||
echo " JA4: $JA4"
|
||||
if [[ "$JA4" == t13* ]]; then
|
||||
ok "TLS 1.3,看起来是合法的 Node.js 指纹"
|
||||
else
|
||||
fail "JA4 获取失败或格式异常: $JA4"
|
||||
fi
|
||||
|
||||
# ── 3. H2 连接 api.anthropic.com ───────────────────────────
|
||||
echo ""
|
||||
echo "【3】HTTP/2 连通性(Anthropic)"
|
||||
H2_RESULT=$(node -e "
|
||||
const http2 = require('http2');
|
||||
const s = http2.connect('https://api.anthropic.com', {}, () => {
|
||||
console.log('ok:' + s.socket.alpnProtocol);
|
||||
s.close();
|
||||
});
|
||||
s.on('error', e => { console.log('err:'+e.message); process.exit(0); });
|
||||
setTimeout(()=>{ console.log('timeout'); process.exit(0); }, 5000);
|
||||
" 2>/dev/null)
|
||||
if [[ "$H2_RESULT" == ok:h2 ]]; then
|
||||
ok "Anthropic H2 连接成功(alpnProtocol: h2)"
|
||||
else
|
||||
fail "H2 连接失败: $H2_RESULT"
|
||||
fi
|
||||
|
||||
# ── 4. H2 连接 googleapis.com ──────────────────────────────
|
||||
echo ""
|
||||
echo "【4】HTTP/2 连通性(Google / Gemini)"
|
||||
H2G=$(node -e "
|
||||
const http2 = require('http2');
|
||||
const s = http2.connect('https://generativelanguage.googleapis.com', {}, () => {
|
||||
console.log('ok:' + s.socket.alpnProtocol);
|
||||
s.close();
|
||||
});
|
||||
s.on('error', e => { console.log('err:'+e.message); process.exit(0); });
|
||||
setTimeout(()=>{ console.log('timeout'); process.exit(0); }, 5000);
|
||||
" 2>/dev/null)
|
||||
if [[ "$H2G" == ok:h2 ]]; then
|
||||
ok "Google API H2 连接成功"
|
||||
else
|
||||
fail "Google API H2 连接失败: $H2G"
|
||||
fi
|
||||
|
||||
# ── 5. 本地 proxy 健康检查(如果本地起了) ─────────────────
|
||||
echo ""
|
||||
echo "【5】本地 node-tls-proxy 健康(端口 3456)"
|
||||
PROXY_PORT="${PROXY_PORT:-3456}"
|
||||
if curl -sf "http://127.0.0.1:${PROXY_PORT}/__health" -o /tmp/proxy_health.json 2>/dev/null; then
|
||||
SESSIONS=$(python3 -c "import json,sys; d=json.load(open('/tmp/proxy_health.json')); print(d.get('sessions',0))" 2>/dev/null || echo "?")
|
||||
H2HOSTS=$(python3 -c "import json,sys; d=json.load(open('/tmp/proxy_health.json')); print(','.join(d.get('h2Hosts',[])))" 2>/dev/null || echo "?")
|
||||
ok "Proxy 运行中 | sessions=$SESSIONS | h2Hosts=$H2HOSTS"
|
||||
else
|
||||
info "本地未运行 node-tls-proxy(端口 $PROXY_PORT),跳过"
|
||||
fi
|
||||
|
||||
# ── 6. Jitter 延迟分布(如果 proxy 运行中)────────────────
|
||||
echo ""
|
||||
echo "【6】Jitter 延迟测试(5次请求,通过本地 proxy)"
|
||||
if curl -sf "http://127.0.0.1:${PROXY_PORT:-3456}/__health" -o /dev/null 2>/dev/null; then
|
||||
for i in {1..5}; do
|
||||
START=$(date +%s%3N)
|
||||
curl -sf -X POST "http://127.0.0.1:${PROXY_PORT:-3456}/v1/messages" \
|
||||
-H "x-forwarded-host: api.anthropic.com" \
|
||||
-H "content-type: application/json" \
|
||||
-d '{"model":"claude-opus-4-5","max_tokens":1}' -o /dev/null 2>/dev/null || true
|
||||
ELAPSED=$(($(date +%s%3N) - START))
|
||||
echo " 请求 $i: ${ELAPSED}ms"
|
||||
done
|
||||
ok "延迟应在 80-1200ms 之间,非均匀分布"
|
||||
else
|
||||
info "本地未运行 proxy,跳过 Jitter 测试"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "══════════════════════════════════════"
|
||||
echo " Mac 验证完成"
|
||||
echo " 关键指纹: $JA4"
|
||||
echo "══════════════════════════════════════"
|
||||
Loading…
x
Reference in New Issue
Block a user