watchdog: 云端健康检查改 sshd banner 探测(TCP 握手是假阳性——黑洞特征 TCP 开但应用断, 会误拉 L2 劫持到死通道); 云端不可达自动撤 L2 规则并停 proxy, 只保 L1

This commit is contained in:
lou
2026-09-09 18:54:20 +08:00
parent b55c0cb7bb
commit e6d4dc17cd
+71 -29
View File
@@ -2,20 +2,37 @@
# 伪装器看门狗(网关侧)
# 每 3 分钟由 crond 调用; 正常时静默, 异常自愈并写日志
# 幂等: 所有动作先查已有实例/规则, 绝不重复拉起
# 可调: BASE_DIR / WAN_IF / LAN_IF / QUEUE / 端口
# 可调: BASE_DIR / WAN_IF / LAN_IF / QUEUE / 端口 / CHANNEL_HOST
BASE_DIR=${BASE_DIR:-/opt/masquerader}
WAN_IF=${WAN_IF:-eth0}
LAN_IF=${LAN_IF:-br-lan}
QUEUE=100
HTTP_PORT=8080
DNS_PORT=8053
CHANNEL_HOST=${CHANNEL_HOST:-8.136.202.225} # 云服务器(部署时改为真实地址)
CHANNEL_PORT=${CHANNEL_PORT:-8444}
LOG=/tmp/watchdog.log
ts() { date '+%F %T'; }
log() { echo "$(ts) $1" >> "$LOG"; }
# ── 通道健康检查(云端 OS 活着才允许 L2 存在) ──
# 判据: sshd banner 可读 = 云端 OS 正常响应
# (黑洞/宕机特征: TCP 握手成功但 banner 读不到 - 单纯测 TCP 是假阳性!)
cloud_up() {
python3 -c "
import socket,sys
try:
s = socket.create_connection(('$CHANNEL_HOST', 22), timeout=3)
s.settimeout(2)
b = s.recv(64)
sys.exit(0 if b.startswith(b'SSH-') else 1)
except Exception:
sys.exit(1)"
}
need_restart=0
# ── L1: worker 进程 ──
# ── L1: worker 进程(不依赖云端, 始终保活) ──
if [ -z "$(ps w | grep '[m]asquerader.worker')" ]; then
log "worker 不在, 拉起"
cd "${BASE_DIR}" || exit 1
@@ -27,20 +44,43 @@ if [ -z "$(ps w | grep '[m]asquerader.worker')" ]; then
need_restart=1
fi
# ── L2: proxy 进程 ──
if [ -z "$(ps w | grep '[c]hannel.proxy_http')" ]; then
log "proxy_http 不在, 拉起"
cd "${BASE_DIR}" || exit 1
setsid python3 -u -m channel.proxy_http config/channel.json \
> /tmp/proxy_http.log 2>&1 < /dev/null &
need_restart=1
fi
if [ -z "$(ps w | grep '[c]hannel.proxy_dns')" ]; then
log "proxy_dns 不在, 拉起"
cd "${BASE_DIR}" || exit 1
setsid python3 -u -m channel.proxy_dns config/channel.json \
> /tmp/proxy_dns.log 2>&1 < /dev/null &
need_restart=1
# ── L2: 仅当云端可达时保活 proxy 与套壳规则 ──
if cloud_up; then
if [ -z "$(ps w | grep '[c]hannel.proxy_http')" ]; then
log "proxy_http 不在, 拉起"
cd "${BASE_DIR}" || exit 1
setsid python3 -u -m channel.proxy_http config/channel.json \
> /tmp/proxy_http.log 2>&1 < /dev/null &
need_restart=1
fi
if [ -z "$(ps w | grep '[c]hannel.proxy_dns')" ]; then
log "proxy_dns 不在, 拉起"
cd "${BASE_DIR}" || exit 1
setsid python3 -u -m channel.proxy_dns config/channel.json \
> /tmp/proxy_dns.log 2>&1 < /dev/null &
need_restart=1
fi
else
# 云端不可达: 撤掉 L2 劫持规则并停 proxy(否则流量被劫到死通道)
if iptables -t nat -C PREROUTING -i "$LAN_IF" -p tcp --dport 80 \
-j REDIRECT --to-ports "$HTTP_PORT" 2>/dev/null; then
log "云端不可达, 撤 L2 HTTP 规则"
iptables -t nat -D PREROUTING -i "$LAN_IF" -p tcp --dport 80 \
-j REDIRECT --to-ports "$HTTP_PORT" 2>/dev/null
fi
if iptables -t nat -C PREROUTING -i "$LAN_IF" -p udp --dport 53 \
-j REDIRECT --to-ports "$DNS_PORT" 2>/dev/null; then
log "云端不可达, 撤 L2 DNS 规则"
iptables -t nat -D PREROUTING -i "$LAN_IF" -p udp --dport 53 \
-j REDIRECT --to-ports "$DNS_PORT" 2>/dev/null
fi
for P in proxy_http proxy_dns; do
PID=$(ps w | grep "[c]hannel.$P" | awk '{print $1}' | head -1)
if [ -n "$PID" ]; then
log "云端不可达, 停 $P($PID)"
kill "$PID" 2>/dev/null
fi
done
fi
# ── 规则自愈(进程刚拉起时挂规则可能抢跑, 统一延迟统一挂) ──
@@ -54,20 +94,22 @@ if ! iptables -t mangle -C FORWARD -o "$WAN_IF" -p tcp --syn \
-j NFQUEUE --queue-num "$QUEUE" --queue-bypass 2>/dev/null
fi
# L2 HTTP 套壳规则(只劫 LAN 入站)
if ! iptables -t nat -C PREROUTING -i "$LAN_IF" -p tcp --dport 80 \
-j REDIRECT --to-ports "$HTTP_PORT" 2>/dev/null; then
log "L2 HTTP 规则缺失, 重挂"
iptables -t nat -A PREROUTING -i "$LAN_IF" -p tcp --dport 80 \
-j REDIRECT --to-ports "$HTTP_PORT" 2>/dev/null
# L2 HTTP 套壳规则(仅云端可达且已拉 proxy 时挂)
if cloud_up && [ -n "$(ps w | grep '[c]hannel.proxy_http')" ]; then
if ! iptables -t nat -C PREROUTING -i "$LAN_IF" -p tcp --dport 80 \
-j REDIRECT --to-ports "$HTTP_PORT" 2>/dev/null; then
log "L2 HTTP 规则缺失, 重挂"
iptables -t nat -A PREROUTING -i "$LAN_IF" -p tcp --dport 80 \
-j REDIRECT --to-ports "$HTTP_PORT" 2>/dev/null
fi
fi
# L2 DNS 套壳规则
if ! iptables -t nat -C PREROUTING -i "$LAN_IF" -p udp --dport 53 \
-j REDIRECT --to-ports "$DNS_PORT" 2>/dev/null; then
log "L2 DNS 规则缺失, 重挂"
iptables -t nat -A PREROUTING -i "$LAN_IF" -p udp --dport 53 \
-j REDIRECT --to-ports "$DNS_PORT" 2>/dev/null
if cloud_up && [ -n "$(ps w | grep '[c]hannel.proxy_dns')" ]; then
if ! iptables -t nat -C PREROUTING -i "$LAN_IF" -p udp --dport 53 \
-j REDIRECT --to-ports "$DNS_PORT" 2>/dev/null; then
log "L2 DNS 规则缺失, 重挂"
iptables -t nat -A PREROUTING -i "$LAN_IF" -p udp --dport 53 \
-j REDIRECT --to-ports "$DNS_PORT" 2>/dev/null
fi
fi
exit 0