firewall.sh: - TCP Window Size 设为 65535(macOS 默认,Linux 服务器默认 29200) - 持久化到 /etc/sysctl.conf maintenance/update-cli-version.sh: - 从 npm registry 获取 @anthropic-ai/claude-code 最新版本 - 自动更新 proxy.js 中的 CLI_VERSION - 支持 --check(仅检查)/ --force VER(强制指定) - 建议 cron 每天 03:00 ET 运行
205 lines
8.2 KiB
Bash
Executable File
205 lines
8.2 KiB
Bash
Executable File
#!/bin/bash
|
||
# sub2api Antigravity — 指纹防泄露 + macOS 特征伪装规则
|
||
#
|
||
# 功能:
|
||
# 1. QUIC/UDP 阻断 — 强制走 TCP/TLS
|
||
# 2. 出站 TCP 443 限制 — 只有 nodeproxy 用户能直连
|
||
# 3. IPv6 阻断 — 消除 IPv6 泄露
|
||
# 4. TCP TTL 伪装 — 改为 64,匹配 macOS/Linux(对抗 OS 识别)
|
||
# 5. TCP 时间戳重写 — 禁用内核时间戳,防止通过 TCP TS 推算 uptime/系统时间
|
||
# 6. 系统时区设置 — 设为 America/Los_Angeles(加州时区,匹配目标用户群)
|
||
#
|
||
# 用法:
|
||
# sudo bash setup-firewall.sh [apply|remove|status|timezone]
|
||
#
|
||
# 前置条件:
|
||
# - Node.js proxy 以专用用户 "nodeproxy" 运行
|
||
# - 创建用户: sudo useradd -r -s /usr/sbin/nologin nodeproxy
|
||
|
||
set -euo pipefail
|
||
|
||
NODE_PROXY_USER="${MG_NODE_PROXY_USER:-nodeproxy}"
|
||
CHAIN_NAME="MG_FINGERPRINT"
|
||
TARGET_TZ="America/New_York"
|
||
|
||
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||
|
||
# ─── 时区设置 ────────────────────────────────────────────────────────
|
||
set_timezone() {
|
||
log "Setting system timezone to $TARGET_TZ ..."
|
||
if command -v timedatectl &>/dev/null; then
|
||
timedatectl set-timezone "$TARGET_TZ"
|
||
log " timedatectl: timezone set to $(timedatectl show -p Timezone --value)"
|
||
elif [ -f "/usr/share/zoneinfo/$TARGET_TZ" ]; then
|
||
ln -sf "/usr/share/zoneinfo/$TARGET_TZ" /etc/localtime
|
||
echo "$TARGET_TZ" > /etc/timezone
|
||
log " /etc/localtime -> $TARGET_TZ"
|
||
else
|
||
log " WARNING: Cannot set timezone — timedatectl not found and zoneinfo missing"
|
||
fi
|
||
}
|
||
|
||
# ─── TCP 时间戳禁用 ──────────────────────────────────────────────────
|
||
# Linux TCP 时间戳会随系统 uptime 线性增长,对方可通过测量 TS 差值
|
||
# 推算服务器启动时间,识破"全天候在线的服务器"特征。
|
||
# 禁用后 TCP TS 选项不再发送,无法通过 TS 推断 uptime。
|
||
disable_tcp_timestamps() {
|
||
log "Disabling TCP timestamps (anti-uptime fingerprinting)..."
|
||
sysctl -w net.ipv4.tcp_timestamps=0 > /dev/null
|
||
# 持久化(防止重启后恢复)
|
||
if ! grep -q "net.ipv4.tcp_timestamps" /etc/sysctl.conf 2>/dev/null; then
|
||
echo "net.ipv4.tcp_timestamps=0" >> /etc/sysctl.conf
|
||
log " Written to /etc/sysctl.conf"
|
||
else
|
||
sed -i 's/net.ipv4.tcp_timestamps=.*/net.ipv4.tcp_timestamps=0/' /etc/sysctl.conf
|
||
log " Updated in /etc/sysctl.conf"
|
||
fi
|
||
log " TCP timestamps: DISABLED"
|
||
}
|
||
|
||
enable_tcp_timestamps() {
|
||
sysctl -w net.ipv4.tcp_timestamps=1 > /dev/null
|
||
sed -i 's/net.ipv4.tcp_timestamps=.*/net.ipv4.tcp_timestamps=1/' /etc/sysctl.conf 2>/dev/null || true
|
||
log " TCP timestamps: ENABLED (restored)"
|
||
}
|
||
|
||
# ─── iptables 规则 ───────────────────────────────────────────────────
|
||
apply_rules() {
|
||
log "Applying fingerprint firewall rules..."
|
||
|
||
# 验证用户存在
|
||
if ! id "$NODE_PROXY_USER" &>/dev/null; then
|
||
log "ERROR: User '$NODE_PROXY_USER' does not exist."
|
||
log "Create it: sudo useradd -r -s /usr/sbin/nologin $NODE_PROXY_USER"
|
||
exit 1
|
||
fi
|
||
|
||
# 创建自定义链(幂等)
|
||
iptables -N "$CHAIN_NAME" 2>/dev/null || iptables -F "$CHAIN_NAME"
|
||
|
||
# === Rule 1: QUIC 阻断 ===
|
||
iptables -A "$CHAIN_NAME" -p udp --dport 443 -j DROP \
|
||
-m comment --comment "MG: block QUIC/HTTP3 UDP 443"
|
||
iptables -A "$CHAIN_NAME" -p udp --dport 4433 -j DROP \
|
||
-m comment --comment "MG: block QUIC alt UDP 4433"
|
||
|
||
# === Rule 2: 允许 nodeproxy 出站 TCP 443 ===
|
||
iptables -A "$CHAIN_NAME" -p tcp --dport 443 \
|
||
-m owner --uid-owner "$NODE_PROXY_USER" -j ACCEPT \
|
||
-m comment --comment "MG: allow nodeproxy TCP 443"
|
||
|
||
# === Rule 3: 阻止其他进程直连 TCP 443 ===
|
||
iptables -A "$CHAIN_NAME" -p tcp --dport 443 -j REJECT --reject-with tcp-reset \
|
||
-m comment --comment "MG: block non-proxy TCP 443"
|
||
|
||
# 挂载到 OUTPUT(幂等)
|
||
if ! iptables -C OUTPUT -j "$CHAIN_NAME" 2>/dev/null; then
|
||
iptables -A OUTPUT -j "$CHAIN_NAME"
|
||
fi
|
||
|
||
# === Rule 4: IPv6 全面阻断 ===
|
||
ip6tables -N "${CHAIN_NAME}_V6" 2>/dev/null || ip6tables -F "${CHAIN_NAME}_V6"
|
||
ip6tables -A "${CHAIN_NAME}_V6" -o lo -j ACCEPT \
|
||
-m comment --comment "MG: allow IPv6 loopback"
|
||
ip6tables -A "${CHAIN_NAME}_V6" -j DROP \
|
||
-m comment --comment "MG: block all IPv6 outbound"
|
||
if ! ip6tables -C OUTPUT -j "${CHAIN_NAME}_V6" 2>/dev/null; then
|
||
ip6tables -A OUTPUT -j "${CHAIN_NAME}_V6"
|
||
fi
|
||
|
||
# === Rule 5: TCP TTL 伪装 (macOS TTL = 64) ===
|
||
# macOS 和 Linux 默认 TTL 都是 64,但数据中心 Linux 有些发行版是 128。
|
||
# 强制设为 64 确保一致,并防止"服务器离对方 0 跳"露馅。
|
||
iptables -t mangle -N "${CHAIN_NAME}_TTL" 2>/dev/null || iptables -t mangle -F "${CHAIN_NAME}_TTL"
|
||
iptables -t mangle -A "${CHAIN_NAME}_TTL" -p tcp --dport 443 \
|
||
-j TTL --ttl-set 64 \
|
||
-m comment --comment "MG: spoof TTL=64 (macOS)"
|
||
if ! iptables -t mangle -C OUTPUT -j "${CHAIN_NAME}_TTL" 2>/dev/null; then
|
||
iptables -t mangle -A OUTPUT -j "${CHAIN_NAME}_TTL"
|
||
fi
|
||
|
||
log "Firewall rules applied successfully."
|
||
log " - UDP 443/4433: BLOCKED (QUIC)"
|
||
log " - TCP 443: ONLY '$NODE_PROXY_USER' allowed"
|
||
log " - IPv6 outbound: BLOCKED"
|
||
log " - TCP TTL: FORCED to 64 (macOS spoof)"
|
||
|
||
# === TCP Window Size 伪装 (macOS 特征) ===
|
||
# macOS 初始 TCP 接收窗口约 65535(Linux 服务器默认 29200),
|
||
# 可被 p0f/Akamai 等工具区分。调整为 macOS 典型值。
|
||
log "Spoofing TCP Window Size (macOS: 65535)..."
|
||
sysctl -w net.ipv4.tcp_rmem="4096 65535 6291456" > /dev/null
|
||
sysctl -w net.ipv4.tcp_wmem="4096 65535 6291456" > /dev/null
|
||
# 持久化
|
||
for param in "net.ipv4.tcp_rmem=4096 65535 6291456" "net.ipv4.tcp_wmem=4096 65535 6291456"; do
|
||
key="${param%%=*}"
|
||
if grep -q "$key" /etc/sysctl.conf 2>/dev/null; then
|
||
sed -i "s|${key}=.*|${param}|" /etc/sysctl.conf
|
||
else
|
||
echo "$param" >> /etc/sysctl.conf
|
||
fi
|
||
done
|
||
log " TCP Window Size: SET to 65535 (macOS spoof)"
|
||
|
||
# === TCP 时间戳禁用 ===
|
||
disable_tcp_timestamps
|
||
|
||
# === 时区设置 ===
|
||
set_timezone
|
||
|
||
log ""
|
||
log "=== All anti-fingerprint measures applied ==="
|
||
log " OS Fingerprint: TTL=64, Window=65535 (macOS)"
|
||
log " TCP Timestamps: Disabled (anti-uptime leak)"
|
||
log " Timezone: $TARGET_TZ"
|
||
}
|
||
|
||
remove_rules() {
|
||
log "Removing fingerprint firewall rules..."
|
||
|
||
iptables -D OUTPUT -j "$CHAIN_NAME" 2>/dev/null || true
|
||
ip6tables -D OUTPUT -j "${CHAIN_NAME}_V6" 2>/dev/null || true
|
||
iptables -t mangle -D OUTPUT -j "${CHAIN_NAME}_TTL" 2>/dev/null || true
|
||
|
||
iptables -F "$CHAIN_NAME" 2>/dev/null || true
|
||
iptables -X "$CHAIN_NAME" 2>/dev/null || true
|
||
ip6tables -F "${CHAIN_NAME}_V6" 2>/dev/null || true
|
||
ip6tables -X "${CHAIN_NAME}_V6" 2>/dev/null || true
|
||
iptables -t mangle -F "${CHAIN_NAME}_TTL" 2>/dev/null || true
|
||
iptables -t mangle -X "${CHAIN_NAME}_TTL" 2>/dev/null || true
|
||
|
||
enable_tcp_timestamps
|
||
log "Firewall rules removed."
|
||
}
|
||
|
||
show_status() {
|
||
log "=== IPv4 MG_FINGERPRINT chain ==="
|
||
iptables -L "$CHAIN_NAME" -n -v 2>/dev/null || echo "(not found)"
|
||
echo
|
||
log "=== IPv4 mangle TTL chain ==="
|
||
iptables -t mangle -L "${CHAIN_NAME}_TTL" -n -v 2>/dev/null || echo "(not found)"
|
||
echo
|
||
log "=== IPv6 MG_FINGERPRINT_V6 chain ==="
|
||
ip6tables -L "${CHAIN_NAME}_V6" -n -v 2>/dev/null || echo "(not found)"
|
||
echo
|
||
log "=== TCP Timestamps ==="
|
||
sysctl net.ipv4.tcp_timestamps
|
||
echo
|
||
log "=== System Timezone ==="
|
||
timedatectl show -p Timezone --value 2>/dev/null || cat /etc/timezone 2>/dev/null || echo "(unknown)"
|
||
echo
|
||
log "=== Current TTL (outbound) ==="
|
||
sysctl net.ipv4.ip_default_ttl
|
||
}
|
||
|
||
case "${1:-apply}" in
|
||
apply) apply_rules ;;
|
||
remove) remove_rules ;;
|
||
status) show_status ;;
|
||
timezone) set_timezone ;;
|
||
*)
|
||
echo "Usage: $0 [apply|remove|status|timezone]"
|
||
exit 1
|
||
;;
|
||
esac
|