- 新增 Node.js TLS Forward Proxy (tools/node-tls-proxy/) 原生 Node.js TLS 栈发起上游 HTTPS,JA3/JA4 天然匹配 Claude CLI SSE 流式透传,支持上游 HTTP CONNECT 代理 零依赖,Node.js 24.13.0 锁定版本 - Go 集成 (config.go + http_upstream.go) 新增 NodeTLSProxyConfig 配置 DoWithTLS 优先走 Node.js 代理模式,URL 重写 https→http://localhost:3456 - Docker 网络隔离 (docker-compose.tls-proxy.yml) sub2api 容器仅 internal 网络,物理隔离外网 node-tls-proxy 唯一出站通道,IPv6 内核级禁用 - iptables 防泄露脚本 (tools/firewall/) QUIC/UDP 443 全局 DROP,仅 nodeproxy 用户可出站 TCP 443 - 镜像切换为 zfc931912343/ 仓库
105 lines
3.4 KiB
Bash
Executable File
105 lines
3.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# sub2api 指纹防泄露 iptables 规则
|
||
# 确保只有 Node.js TLS Proxy 能直连上游 HTTPS,
|
||
# sub2api Go 进程即使有 bug 也无法绕过。
|
||
#
|
||
# 用法:
|
||
# sudo bash setup-firewall.sh [apply|remove|status]
|
||
#
|
||
# 前置条件:
|
||
# - 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"
|
||
|
||
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||
|
||
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 阻断 — 丢弃所有出站 UDP 443/4433 ===
|
||
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: 允许 Node.js proxy 出站 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"
|
||
# 阻断其他 IPv6 出站
|
||
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
|
||
|
||
log "Firewall rules applied successfully."
|
||
log " - UDP 443/4433: BLOCKED (QUIC)"
|
||
log " - TCP 443: ONLY '$NODE_PROXY_USER' allowed"
|
||
log " - IPv6 outbound: BLOCKED"
|
||
}
|
||
|
||
remove_rules() {
|
||
log "Removing fingerprint firewall rules..."
|
||
|
||
# 从 OUTPUT 移除引用
|
||
iptables -D OUTPUT -j "$CHAIN_NAME" 2>/dev/null || true
|
||
ip6tables -D OUTPUT -j "${CHAIN_NAME}_V6" 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
|
||
|
||
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 "=== IPv6 MG_FINGERPRINT_V6 chain ==="
|
||
ip6tables -L "${CHAIN_NAME}_V6" -n -v 2>/dev/null || echo "(not found)"
|
||
}
|
||
|
||
case "${1:-apply}" in
|
||
apply) apply_rules ;;
|
||
remove) remove_rules ;;
|
||
status) show_status ;;
|
||
*)
|
||
echo "Usage: $0 [apply|remove|status]"
|
||
exit 1
|
||
;;
|
||
esac
|