mirror of
https://github.com/hotyue/IP-Sentinel.git
synced 2026-06-03 15:59:40 +08:00
refactor(docs): 启动架构文档全量重构,统一 V4.1.1 工业级防渗透注释标准,彻底抹除历史版本碎片
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 - 动态锚点版)
|
||||
# 核心功能: 智能防打扰注册、进程自检、模块级路由分发(403拦截)
|
||||
# 脚本名称: agent_daemon.sh
|
||||
# 核心功能: TLS 隧道构建、HMAC 动态鉴权、防重放攻击、模块级零信任路由
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
@@ -12,23 +12,26 @@ IP_CACHE="${INSTALL_DIR}/core/.last_ip"
|
||||
[ ! -f "$CONFIG_FILE" ] && exit 1
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# 如果没有配置 TG,说明未开启联控模式,直接退出
|
||||
# [战术核心] 若未配置司令部凭证,则判定为单机运行模式,主动进入休眠
|
||||
[ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ] && exit 0
|
||||
|
||||
# 默认 Webhook 监听端口
|
||||
AGENT_PORT=${AGENT_PORT:-9527}
|
||||
# [v3.5.2 核心] 载入不可变主键与可变展示名 (双轨身份)
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# [身份锚定] 载入不可变主键与展示别名 (双轨身份映射)
|
||||
# ----------------------------------------------------------
|
||||
if [ -z "$NODE_NAME" ]; then
|
||||
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||
NODE_NAME="$(hostname | tr -cd 'a-zA-Z0-9' | cut -c 1-10)-${IP_HASH}"
|
||||
fi
|
||||
NODE_ALIAS="${NODE_ALIAS:-$NODE_NAME}"
|
||||
|
||||
|
||||
# 1. 尝试获取实时公网 IP
|
||||
# ----------------------------------------------------------
|
||||
# [网络侦测] 实时公网 IP 嗅探与静默状态更新
|
||||
# ----------------------------------------------------------
|
||||
RAW_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
|
||||
|
||||
# [v3.3.1 修改] 为新获取到的 v6 自动加方括号;如果网络波动没抓到,强制信任本地 config 中的公网面孔
|
||||
# [防线/容灾] 为 IPv6 自动装载方括号护甲;API 失效时退回静态配置锚点
|
||||
if [ -n "$RAW_IP" ]; then
|
||||
if [[ "$RAW_IP" == *":"* ]] && [[ "$RAW_IP" != *"["* ]]; then
|
||||
AGENT_IP="[${RAW_IP}]"
|
||||
@@ -44,7 +47,7 @@ if [ -n "$AGENT_IP" ]; then
|
||||
[ -f "$IP_CACHE" ] && LAST_IP=$(cat "$IP_CACHE" | tr -d '[:space:]')
|
||||
|
||||
if [ "$AGENT_IP" != "$LAST_IP" ]; then
|
||||
# [静音手术] 仅在本地静默更新 IP 缓存,彻底切除重复的 TG 发信逻辑,做沉默的守夜人
|
||||
# [底层交互] 仅执行本地缓存重写,切除高频发信逻辑,保持静默侦听
|
||||
echo "$AGENT_IP" > "$IP_CACHE"
|
||||
echo "ℹ️ [Agent] 发现本地 IP 变动,已静默更新缓存: $AGENT_IP"
|
||||
else
|
||||
@@ -52,8 +55,9 @@ if [ -n "$AGENT_IP" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.6.3 新增: 自动生成自签名 TLS 加密证书] ==================
|
||||
# [修复] 彻底废除官方网关免 TLS 的裸奔逻辑,全网强制生成证书装甲
|
||||
# ==========================================================
|
||||
# [加密通信] 强制构建自签名 TLS 装甲,屏蔽中间人嗅探
|
||||
# ==========================================================
|
||||
CERT_FILE="${INSTALL_DIR}/core/cert.pem"
|
||||
KEY_FILE="${INSTALL_DIR}/core/key.pem"
|
||||
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
|
||||
@@ -62,9 +66,10 @@ if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
|
||||
-keyout "$KEY_FILE" -out "$CERT_FILE" \
|
||||
-subj "/C=US/O=IP-Sentinel/CN=Agent-Sec" >/dev/null 2>&1 || true
|
||||
fi
|
||||
# ==============================================================================
|
||||
|
||||
# 3. 启动轻量级 Python3 Webhook 监听服务 (v3.0.4 动态 HMAC 签名防重放)
|
||||
# ==========================================================
|
||||
# [引擎核心] Python3 高并发 Webhook 侦听与路由枢纽
|
||||
# ==========================================================
|
||||
cat > "${INSTALL_DIR}/core/webhook.py" << 'EOF'
|
||||
import http.server
|
||||
import socketserver
|
||||
@@ -72,26 +77,26 @@ import subprocess
|
||||
import sys
|
||||
import os
|
||||
import html
|
||||
# ================== [v3.0.4 新增密码学与解析依赖] ==================
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import hmac
|
||||
import hashlib
|
||||
import time
|
||||
# ====================================================================
|
||||
|
||||
PORT = int(sys.argv[1])
|
||||
|
||||
# 🛡️ 防重放攻击 (Nonce 缓存池)
|
||||
# ----------------------------------------------------------
|
||||
# [防御矩阵] Nonce 缓存池防重放攻击 (Replay Attack)
|
||||
# ----------------------------------------------------------
|
||||
USED_SIGNS = {}
|
||||
def clean_used_signs():
|
||||
now = time.time()
|
||||
# 清理过期签名 (超 60 秒的安全窗口)
|
||||
# [安全策略] 滑动清理超 65 秒过期签名,保障内存健康
|
||||
expired = [s for s, t in USED_SIGNS.items() if now - t > 65]
|
||||
for s in expired:
|
||||
del USED_SIGNS[s]
|
||||
|
||||
# 🛡️ 提取全局鉴权 Token (利用 CHAT_ID 作为 PSK 预共享密钥)
|
||||
# [权限鉴权] 提取 CHAT_ID 作为 PSK 预共享密钥
|
||||
AUTH_TOKEN = ""
|
||||
if os.path.exists('/opt/ip_sentinel/config.conf'):
|
||||
with open('/opt/ip_sentinel/config.conf', 'r') as f:
|
||||
@@ -103,7 +108,7 @@ if os.path.exists('/opt/ip_sentinel/config.conf'):
|
||||
|
||||
class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
# 🛡️ [v3.0.4 核心] URL 解析与动态 HMAC-SHA256 签名校验
|
||||
# [权限校验] 路径解析与 HMAC-SHA256 动态签名核验
|
||||
parsed = urllib.parse.urlparse(self.path)
|
||||
req_path = parsed.path
|
||||
|
||||
@@ -112,7 +117,6 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
req_t = query.get('t', [''])[0]
|
||||
req_sign = query.get('sign', [''])[0]
|
||||
|
||||
# 校验 1:参数是否齐全
|
||||
if not req_t or not req_sign:
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
@@ -121,7 +125,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
|
||||
try:
|
||||
current_time = int(time.time())
|
||||
# 校验 2:时间戳防重放 (误差 ±60秒 内有效,拒绝隔夜抓包重放)
|
||||
# [防重放 1] 校验时间戳防偏离 (±60秒窗口,免疫隔夜抓包重放)
|
||||
if abs(current_time - int(req_t)) > 60:
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
@@ -132,7 +136,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
# 校验 2.5:基于 60秒 窗口的精确重放拦截 (拦截 MITM 并发洗劫)
|
||||
# [防重放 2] Nonce 精确核对 (拦截 60 秒内的 MITM 并发重放洗劫)
|
||||
clean_used_signs()
|
||||
if req_sign in USED_SIGNS:
|
||||
self.send_response(401)
|
||||
@@ -140,21 +144,22 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.wfile.write(b"401 Unauthorized: Replay Attack Detected\n")
|
||||
return
|
||||
|
||||
# 校验 3:HMAC 数据完整性与身份合法性校验
|
||||
# [身份核验] 数据完整性校验,使用 compare_digest 免疫时序探测攻击
|
||||
msg = f"{req_path}:{req_t}".encode('utf-8')
|
||||
expected_sign = hmac.new(AUTH_TOKEN.encode('utf-8'), msg, hashlib.sha256).hexdigest()
|
||||
|
||||
# 使用 compare_digest 防御时序攻击
|
||||
if not hmac.compare_digest(expected_sign, req_sign):
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"401 Unauthorized: Signature Mismatch\n")
|
||||
return
|
||||
|
||||
# 鉴权通过,记录该签名至防重放内存池
|
||||
# 鉴权通过,登记 Nonce 载荷
|
||||
USED_SIGNS[req_sign] = current_time
|
||||
|
||||
# ================== 路由分发 (恢复为安全的精确匹配) ==================
|
||||
# ==========================================================
|
||||
# [指令分发] 模块级业务路由矩阵 (精确匹配策略)
|
||||
# ==========================================================
|
||||
|
||||
# 路由 0: 全局统筹调度
|
||||
if req_path == '/trigger_run':
|
||||
@@ -168,7 +173,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
# 路由 1: Google 区域纠偏
|
||||
# 路由 1: Google 区域纠偏探测
|
||||
elif req_path == '/trigger_google':
|
||||
if os.path.exists('/opt/ip_sentinel/core/mod_google.sh'):
|
||||
self.send_response(200)
|
||||
@@ -182,7 +187,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: Google Module Disabled\n")
|
||||
|
||||
# 路由 2: IP 信用净化
|
||||
# 路由 2: IP 信用数据清洗
|
||||
elif req_path == '/trigger_trust':
|
||||
if os.path.exists('/opt/ip_sentinel/core/mod_trust.sh'):
|
||||
self.send_response(200)
|
||||
@@ -196,7 +201,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: Trust Module Disabled\n")
|
||||
|
||||
# 路由 3: 触发战报推送
|
||||
# 路由 3: 触发异步战报生成
|
||||
elif req_path == '/trigger_report':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
@@ -204,7 +209,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.wfile.write(b"Action Accepted: tg_report\n")
|
||||
os.system("nohup bash /opt/ip_sentinel/core/tg_report.sh >/dev/null 2>&1 &")
|
||||
|
||||
# 路由 4: 抓取并回传实时日志
|
||||
# 路由 4: 获取并回传实时日志切片
|
||||
elif req_path == '/trigger_log':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
@@ -229,13 +234,13 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
if lines:
|
||||
log_data = html.escape("".join(lines[-15:]))
|
||||
|
||||
# [v3.5.2 核心] 获取版本与节点展示别名
|
||||
# 动态提取终端状态以构建回传信息
|
||||
local_ver = config.get('AGENT_VERSION', '未知')
|
||||
node_alias = config.get('NODE_ALIAS', config.get('NODE_NAME', 'Unknown-Node'))
|
||||
|
||||
text_msg = f"📄 <b>[{node_alias}] 实时日志 (v{local_ver}):</b>\n<pre><code>{log_data}</code></pre>"
|
||||
|
||||
# [v4.0.3 体验升级] 引入 json 模块并改用 JSON Payload,挂载返回控制台按钮
|
||||
# [交互反馈] 构建内联 JSON Payload 回调指令
|
||||
import json
|
||||
node_name_cb = config.get('NODE_NAME', 'Unknown')
|
||||
payload = {
|
||||
@@ -251,7 +256,6 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
req = urllib.request.Request(
|
||||
config.get('TG_API_URL', ''),
|
||||
data=data,
|
||||
# [动态化] 彻底消灭硬编码,使用运行态版本号,并声明 JSON 头
|
||||
headers={
|
||||
'User-Agent': f'IP-Sentinel-Agent/{local_ver}',
|
||||
'Content-Type': 'application/json'
|
||||
@@ -262,7 +266,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
except Exception as e:
|
||||
print(f"Log transmission failed: {e}")
|
||||
|
||||
# ================== [v4.0.0 新增: 触发深海声呐] ==================
|
||||
# 路由 5: 深海声呐模块触发
|
||||
elif req_path == '/trigger_quality':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
@@ -271,10 +275,8 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
|
||||
if os.path.exists('/opt/ip_sentinel/core/mod_quality.sh'):
|
||||
os.system("nohup bash /opt/ip_sentinel/core/mod_quality.sh >/dev/null 2>&1 &")
|
||||
# =================================================================
|
||||
|
||||
|
||||
# 路由 5: 节点重命名展示别名同步接口 (Base64 终极防御版)
|
||||
# 路由 6: 节点展示别名热修改 (全量 WAF 防护)
|
||||
elif req_path == '/trigger_rename':
|
||||
b64_alias = query.get('b64', [''])[0]
|
||||
if not b64_alias:
|
||||
@@ -286,19 +288,19 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
import re
|
||||
import base64
|
||||
try:
|
||||
# 1. 还原 URL 安全的 Base64 字符并解码 (杜绝乱码与 WAF 拦截)
|
||||
# [防线/容灾] 还原安全 Base64 编码,屏蔽乱码级注入风险
|
||||
pad = len(b64_alias) % 4
|
||||
if pad > 0:
|
||||
b64_alias += '=' * (4 - pad)
|
||||
b64_alias = b64_alias.replace('-', '+').replace('_', '/')
|
||||
raw_alias = base64.b64decode(b64_alias).decode('utf-8', errors='ignore')
|
||||
|
||||
# 2. 强清洗:杜绝 TG Markdown 崩溃,严格限制中英数,最大20字符
|
||||
# 强格式清洗:剔除潜在非法字符,保护 TG 面板不被恶意解析撑爆
|
||||
decoded_alias = raw_alias.replace('_', '-')
|
||||
safe_alias = re.sub(r'[^a-zA-Z0-9\-\u4e00-\u9fa5]', '', decoded_alias)[:20]
|
||||
|
||||
if safe_alias:
|
||||
# 3. 强容错读写 config.conf (引入 fcntl 排他锁与 r+ 模式防并发清空)
|
||||
# [底层交互] 利用 fcntl 独占锁执行安全写操作,防止并发数据被截断
|
||||
config_path = '/opt/ip_sentinel/config.conf'
|
||||
import fcntl
|
||||
with open(config_path, 'r+', encoding='utf-8', errors='ignore') as f:
|
||||
@@ -320,7 +322,6 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
f.truncate()
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
# [v3.5.2 极致丝滑] 移除向 TG 推送冗余报文的逻辑,直接向 Master 回执成功状态即可
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
@@ -336,7 +337,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(b"400 Bad Request: Invalid Characters\n")
|
||||
|
||||
# ================== [v3.5.3 新增: 模块动态启停接口] ==================
|
||||
# 路由 7: 功能模块动态起停 (Feature Flag API)
|
||||
elif req_path == '/trigger_toggle':
|
||||
mod_name = query.get('mod', [''])[0]
|
||||
target_state = query.get('state', [''])[0].lower()
|
||||
@@ -382,10 +383,9 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
|
||||
|
||||
# ================== [v3.6.0 新增: 零信任 OTA 远程静默升级路由] ==================
|
||||
# 路由 8: 零信任 OTA 远程热更新链路
|
||||
elif req_path == '/trigger_ota':
|
||||
try:
|
||||
# 动态读取最新 config 内存态
|
||||
config_mem = {}
|
||||
config_path = '/opt/ip_sentinel/config.conf'
|
||||
if os.path.exists(config_path):
|
||||
@@ -396,30 +396,28 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
key, val = line.split('=', 1)
|
||||
config_mem[key] = val.strip('"\'')
|
||||
|
||||
# 🛡️ 熔断校验 1: Agent 本地是否开启了 OTA 授权
|
||||
# [OTA 熔断器 1] 核验 Agent 本地策略是否授予了更新权限
|
||||
if config_mem.get('ENABLE_OTA', 'false').lower() != 'true':
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: OTA Upgrade Disabled locally\n")
|
||||
return
|
||||
|
||||
# 🛡️ 熔断校验 2: 是否处于官方公共网关下 (强行硬编码拦截)
|
||||
# [OTA 熔断器 2] 检测官方网关硬编码限制,防范越权投毒
|
||||
if config_mem.get('TG_TOKEN', '') == 'OFFICIAL_GATEWAY_MODE':
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: OTA strictly disabled under Public Gateway mode\n")
|
||||
return
|
||||
|
||||
# 校验通过,立即返回 200 回执,释放 Master 连接池
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: trigger_ota\n")
|
||||
|
||||
# [修复] 逃逸 Systemd Cgroup,并引入 bash -n 语法树校验防砖机制
|
||||
# [防线/容灾] 逃逸 Cgroup 隔离沙盒,并引入前置脚本语法校验防砖
|
||||
import shutil
|
||||
import base64
|
||||
# 动态提取部署时的源地址,废除强制写死 main 分支,保障隔离测试环境
|
||||
repo_url = "https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
if os.path.exists('/opt/ip_sentinel/core/install.sh'):
|
||||
with open('/opt/ip_sentinel/core/install.sh', 'r') as f:
|
||||
@@ -428,15 +426,13 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
repo_url = line.split('=', 1)[1].strip('"\'')
|
||||
break
|
||||
|
||||
# 动态构建报错回执文本 (第一层 Base64 隔离换行与特殊字符)
|
||||
err_msg = f"❌ **OTA 熔断告警**\n📍 节点: `{config_mem.get('NODE_ALIAS', '未知')}`\n⚠️ 原因: 脚本语法校验(bash -n)未通过,下载可能不完整。\n🚀 状态: 升级已取消,节点安全。"
|
||||
err_msg_b64 = base64.b64encode(err_msg.encode('utf-8')).decode('utf-8')
|
||||
|
||||
tg_url = config_mem.get('TG_API_URL', '')
|
||||
chat_id = config_mem.get('CHAT_ID', '')
|
||||
|
||||
# [v3.6.3 究极防御] 采用 Base64 将整个 OTA 执行脚本封装 (第二层隔离)
|
||||
# 彻底免疫因为 python 变量掺杂引号而导致的 shell 注入或截断
|
||||
# 将升级逻辑进行 Base64 深层封装,免疫 Popen 或 Systemd 传递带来的指令注入风险
|
||||
ota_script = f"""
|
||||
export SILENT_OTA="true"
|
||||
curl -fsSL {repo_url}/core/install.sh -o /tmp/ota_agent.sh
|
||||
@@ -450,13 +446,11 @@ fi
|
||||
"""
|
||||
ota_script_b64 = base64.b64encode(ota_script.encode('utf-8')).decode('utf-8')
|
||||
|
||||
# 安全解包并执行
|
||||
if shutil.which("systemd-run"):
|
||||
full_cmd = f"systemd-run --quiet --no-block bash -c \"echo '{ota_script_b64}' | base64 -d | bash\""
|
||||
else:
|
||||
full_cmd = f"nohup bash -c \"echo '{ota_script_b64}' | base64 -d | bash\" >/dev/null 2>&1 &"
|
||||
|
||||
# 彻底统一为 os.system,消灭最后一个可能游离的 Popen 僵尸进程
|
||||
os.system(full_cmd)
|
||||
|
||||
except Exception as e:
|
||||
@@ -472,12 +466,13 @@ fi
|
||||
pass
|
||||
|
||||
import socket
|
||||
# ================== [v3.0.3 变更: 引入多线程模型抵抗 Slowloris 攻击] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [核心架构] 多线程非阻塞 Socket 模型 (抵抗 Slowloris 及阻塞攻击)
|
||||
# ----------------------------------------------------------
|
||||
class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
allow_reuse_address = True # 开启端口复用,防止热重启时端口冲突
|
||||
allow_reuse_address = True
|
||||
|
||||
# [终极修复 Issue #53] 废除极易引发 LXC 容器 "IPv4 耳聋" 的模糊双栈监听
|
||||
# 改为精准探底:直接读取配置文件中的公网 IP 类型,动态决定单一监听协议
|
||||
# 精准探底协议栈:根据配置的 IP 类型动态执行 AF_INET/AF_INET6 单轨监听
|
||||
bind_addr = "0.0.0.0"
|
||||
ThreadedServer.address_family = socket.AF_INET
|
||||
|
||||
@@ -487,7 +482,6 @@ if os.path.exists(config_path):
|
||||
for line in f:
|
||||
if line.startswith('PUBLIC_IP='):
|
||||
pub_ip = line.split('=', 1)[1].strip('"\'')
|
||||
# 如果注册的是 IPv6 节点,则精准监听 IPv6,否则一律兜底监听 IPv4
|
||||
if ':' in pub_ip:
|
||||
bind_addr = "::"
|
||||
ThreadedServer.address_family = socket.AF_INET6
|
||||
@@ -495,12 +489,13 @@ if os.path.exists(config_path):
|
||||
|
||||
httpd = ThreadedServer((bind_addr, PORT), AgentHandler)
|
||||
|
||||
# ================== [v3.6.3 核心: 挂载 TLS 加密隧道 (强制装甲版)] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [加密通信] 强制全网挂载 TLS 加密隧道上下文
|
||||
# ----------------------------------------------------------
|
||||
import ssl
|
||||
cert_path = '/opt/ip_sentinel/core/cert.pem'
|
||||
key_path = '/opt/ip_sentinel/core/key.pem'
|
||||
|
||||
# 全网强制启用 TLS 装甲,彻底消灭 HTTP 裸奔漏洞
|
||||
if os.path.exists(cert_path) and os.path.exists(key_path):
|
||||
try:
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
@@ -508,15 +503,12 @@ if os.path.exists(cert_path) and os.path.exists(key_path):
|
||||
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
|
||||
except Exception as e:
|
||||
print(f"SSL 隧道构建失败,退化为 HTTP: {e}")
|
||||
# ======================================================================================
|
||||
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except Exception as e:
|
||||
sys.exit(1)
|
||||
# ====================================================================================
|
||||
EOF
|
||||
|
||||
# --- [重点升级 3: 移交系统级守护进程接管 (阻塞模式)] ---
|
||||
echo "🚀 [Agent] 正在启动 Webhook 监听服务 (端口: $AGENT_PORT)..."
|
||||
exec python3 "${INSTALL_DIR}/core/webhook.py" "$AGENT_PORT"
|
||||
203
core/install.sh
203
core/install.sh
@@ -1,12 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 - 动态锚点版)
|
||||
# 核心功能: 战区分组菜单、模块按需开启、官方机器人一键配置、版本状态机路由
|
||||
# 脚本名称: install.sh
|
||||
# 核心功能: 动态环境解析、无感原子交接、防砖部署、沙盒隔离
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# [权限鉴权] 严格防范低权限执行导致的组件缺失
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 部署 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
@@ -14,13 +14,12 @@ if [ "$EUID" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 🟢 [防劫持沙盒] 创建具备随机哈希且仅 root 可见的专属安全工作区
|
||||
# [沙盒机制] 创建含高强度熵值的安全挂载点,并在异常断开时确保物理覆写销毁
|
||||
SECURE_TMP=$(mktemp -d /tmp/ips_install.XXXXXX)
|
||||
# 确保脚本退出、异常中断或被强杀时,自动销毁沙盒,不留痕迹
|
||||
trap 'rm -rf "$SECURE_TMP"' EXIT HUP INT QUIT TERM
|
||||
|
||||
# ==========================================================
|
||||
# 🔍 核心探针: 系统环境与虚拟化检测 (v4.0.13 架构升级)
|
||||
# [环境侦测] 靶机架构预检与调度器降级决策
|
||||
# ==========================================================
|
||||
is_systemd() {
|
||||
command -v systemctl >/dev/null 2>&1 || return 1
|
||||
@@ -62,56 +61,44 @@ fi
|
||||
echo -e "======================================\n"
|
||||
sleep 1
|
||||
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
|
||||
# [核心: 动态提取 Agent 专属版本锚点 (KV 解析法)]
|
||||
# [红警修复] 增加 -f 与 --retry 护甲,防御 404 被当作脚本下载执行的毁灭级 Bug
|
||||
# [网络容灾] 挂载双栈并利用防抖重试护甲,从远端解析运行态版本约束
|
||||
TARGET_VERSION=$( (curl -fsSL --connect-timeout 5 --retry 2 "${REPO_RAW_URL}/version.txt" || curl -4 -fsSL --connect-timeout 5 --retry 2 "${REPO_RAW_URL}/version.txt") 2>/dev/null | grep "^AGENT_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
# 🛡️ 兜底防线:如果网络波动拉取失败,启用内置的安全兜底版本
|
||||
TARGET_VERSION=${TARGET_VERSION:-"4.0.6"}
|
||||
TARGET_VERSION=${TARGET_VERSION:-"4.1.1"}
|
||||
|
||||
# 轻量级版本号比对函数 (例如: version_lt "3.3.1" "3.4.0" 返回 true)
|
||||
version_lt() {
|
||||
test "$(printf '%s\n' "$1" "$2" | sort -V | head -n 1)" = "$1" && test "$1" != "$2"
|
||||
}
|
||||
|
||||
# 1. 依赖检查与智能安装 (v3.5.4 兼容性升级: 支持 Alpine, Arch 及更完善的依赖链)
|
||||
# ==========================================================
|
||||
# [依赖装甲] 多分支包管理器嗅探与极简系统补全
|
||||
# ==========================================================
|
||||
echo -e "\n[1/7] 正在探测并安装基础环境依赖 (curl, jq, cron, procps, python3)..."
|
||||
|
||||
# 定义必须检测的核心命令
|
||||
REQUIRED_CMDS=("curl" "jq" "crontab" "pgrep" "python3" "openssl")
|
||||
MISSING_CMDS=()
|
||||
|
||||
# 基础探测:预检查缺失的命令
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
MISSING_CMDS+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
# 如果有缺失,执行智能安装逻辑
|
||||
if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
echo "⏳ 发现缺失依赖: ${MISSING_CMDS[*]},正在尝试自动补齐..."
|
||||
|
||||
# 嗅探包管理器
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
# Debian / Ubuntu 系列
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
# [v3.6.3 抽脂级优化] 注入 --no-install-recommends 拒绝捆绑销售,大幅节省磁盘与内存
|
||||
apt-get install -y --no-install-recommends curl jq cron procps python3 openssl >/dev/null 2>&1
|
||||
systemctl enable cron >/dev/null 2>&1 && systemctl start cron >/dev/null 2>&1
|
||||
|
||||
elif command -v yum >/dev/null 2>&1 || command -v dnf >/dev/null 2>&1 || command -v microdnf >/dev/null 2>&1; then
|
||||
# RHEL / CentOS / AlmaLinux / Rocky 系列 (含极简 microdnf 镜像)
|
||||
PKG_MGR="yum"
|
||||
OPT_ARGS=""
|
||||
if command -v dnf >/dev/null 2>&1; then
|
||||
PKG_MGR="dnf"
|
||||
# [v3.6.3 抽脂级优化] 强行关闭 DNF 的弱依赖拉取
|
||||
OPT_ARGS="--setopt=install_weak_deps=False"
|
||||
elif command -v microdnf >/dev/null 2>&1; then
|
||||
PKG_MGR="microdnf"
|
||||
@@ -121,38 +108,31 @@ if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
$PKG_MGR install -y epel-release >/dev/null 2>&1 || true
|
||||
|
||||
echo -e "\033[90m (正在拉取核心组件...)\033[0m"
|
||||
# [核心修复] 移除屏蔽符,暴露真实执行过程,打破“没有调用”的假象
|
||||
$PKG_MGR install -y $OPT_ARGS curl jq cronie procps-ng python3 openssl
|
||||
systemctl enable crond >/dev/null 2>&1 && systemctl start crond >/dev/null 2>&1
|
||||
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
# Alpine 本身就是极致精简,无需特殊参数
|
||||
echo "Alpine 探测到系统类型为 Alpine Linux,正在执行轻量级安装..."
|
||||
# [修复] 新版 Alpine 已废弃 dcron。优先尝试 cronie,若失败则信任自带 busybox-cron,并移除屏蔽以便暴露报错
|
||||
apk add --no-cache curl jq cronie procps python3 bash openssl || apk add --no-cache curl jq procps python3 bash openssl
|
||||
mkdir -p /var/spool/cron/crontabs
|
||||
rc-update add crond default >/dev/null 2>&1
|
||||
service crond start >/dev/null 2>&1
|
||||
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
# Arch Linux 系列 (采用 --needed 防重复,剥离 -y 防部分升级炸系统)
|
||||
pacman -S --needed --noconfirm curl jq cronie procps-ng python openssl >/dev/null 2>&1
|
||||
mkdir -p /root/.cache/crontab 2>/dev/null
|
||||
systemctl enable cronie >/dev/null 2>&1 && systemctl start cronie >/dev/null 2>&1
|
||||
|
||||
else
|
||||
# 无法识别的系统:退出并给出清晰的引导信息 (同步更新防捆绑参数)
|
||||
echo -e "\033[31m❌ 自动安装失败:系统未知的包管理器。\033[0m"
|
||||
echo -e "\033[33m⚠️ 请根据您的操作系统,手动执行以下安装命令后重新运行本脚本:\033[0m"
|
||||
echo -e " Debian/Ubuntu: \033[36mapt-get update && apt-get install -y --no-install-recommends curl jq cron procps python3 openssl\033[0m"
|
||||
echo -e " CentOS/RHEL: \033[36myum install -y curl jq cronie procps-ng python3 openssl\033[0m"
|
||||
echo -e " Alpine Linux: \033[36mapk add --no-cache curl jq cronie procps python3 bash openssl\033[0m"
|
||||
# Arch 用户,如果出问题,应该用 -Syu 进行全系统安全更新
|
||||
echo -e " Arch Linux: \033[36mpacman -Syu --needed curl jq cronie procps-ng python openssl\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 安装后二次复检
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo -e "\033[31m❌ 致命错误:核心命令 '$cmd' 仍未找到!\033[0m"
|
||||
@@ -164,7 +144,9 @@ if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
fi
|
||||
echo -e "\033[32m✅ 基础环境检测通过。\033[0m"
|
||||
|
||||
# 2. 交互式引导与动态地图解析 (v3.0 全球网络)
|
||||
# ----------------------------------------------------------
|
||||
# [交互中枢] LBS 地理图谱树预载
|
||||
# ----------------------------------------------------------
|
||||
echo -e "\n[2/7] 正在连线云端,拉取全球节点地图..."
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/data/map.json" -o "${SECURE_TMP}/map.json"
|
||||
if [ ! -s "${SECURE_TMP}/map.json" ]; then
|
||||
@@ -172,9 +154,7 @@ if [ ! -s "${SECURE_TMP}/map.json" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# [v3.6.0 核心] 拦截静默 OTA 升级模式 (强行接管执行流,跳过人工交互)
|
||||
# ==========================================================
|
||||
# [自动化架构] 拦截交互菜单,接受云端重载指令直接执行 OTA
|
||||
if [ "$SILENT_OTA" == "true" ]; then
|
||||
echo -e "\n⏳ [OTA] 静默升级指令已确认,正在剥离控制台交互..."
|
||||
ACTION_CHOICE=1
|
||||
@@ -187,7 +167,6 @@ else
|
||||
echo " 2) 🗑️ 一键卸载 IP-Sentinel"
|
||||
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
|
||||
|
||||
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定
|
||||
ACTION_CHOICE=${ACTION_CHOICE:-1}
|
||||
|
||||
if [ "$ACTION_CHOICE" == "2" ]; then
|
||||
@@ -199,7 +178,7 @@ else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
# [态势传承] 平滑升级探测,防用户误删配置档案
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_LOGS="true"
|
||||
|
||||
@@ -213,28 +192,23 @@ else
|
||||
KEEP_LOGS="false"
|
||||
fi
|
||||
|
||||
# 将原配置读入环境变量,为后续跳过配置步骤提供燃料
|
||||
source "$CONFIG_FILE"
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],即将跳过基础配置,直接更新核心装甲...\033[0m"
|
||||
else
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的哨兵数据将被彻底抹除。\033[0m"
|
||||
fi
|
||||
fi
|
||||
# ====================================================================
|
||||
fi
|
||||
|
||||
# ================== [v3.1.1/v3.2.2 优化: 安装前环境纯净度清理] ==================
|
||||
# ==========================================================
|
||||
# [物理清洗] 安装前的环境纯净度构建与幽灵进程抹除
|
||||
# ==========================================================
|
||||
echo -e "\n⏳ 正在清理系统定时任务中的旧版条目..."
|
||||
|
||||
# 1. 清除系统定时任务 (Cron) 中的旧版条目 (安全容错版)
|
||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" > "${SECURE_TMP}/cron_clean" || true
|
||||
# [追加 >/dev/null 2>&1 堵死 Alpine 的脏话输出]
|
||||
[ -f "${SECURE_TMP}/cron_clean" ] && crontab "${SECURE_TMP}/cron_clean" >/dev/null 2>&1
|
||||
rm -f "${SECURE_TMP}/cron_clean"
|
||||
|
||||
# ==========================================
|
||||
# 🛑 [物理抹除] 彻底扫除 Alpine 系统的底层残留与双路径文件
|
||||
# ==========================================
|
||||
for CRON_FILE in "/var/spool/cron/crontabs/root" "/etc/crontabs/root"; do
|
||||
if [ -f "$CRON_FILE" ]; then
|
||||
grep -v "ip_sentinel" "$CRON_FILE" > "${CRON_FILE}.tmp" 2>/dev/null || true
|
||||
@@ -242,12 +216,9 @@ for CRON_FILE in "/var/spool/cron/crontabs/root" "/etc/crontabs/root"; do
|
||||
rm -f "${CRON_FILE}.tmp" 2>/dev/null
|
||||
fi
|
||||
done
|
||||
# 清理 OpenRC 开机启动项
|
||||
rm -f /etc/local.d/ip_sentinel.start 2>/dev/null
|
||||
|
||||
# 3. 抹除旧版核心代码,杜绝代码冲突 (根据模式分流)
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
# [修复] 升级模式:不再提前销毁核心引擎,改为后续下载成功后的原子化替换,彻底防止断网变砖!
|
||||
if [ "$KEEP_LOGS" == "false" ]; then
|
||||
rm -rf "${INSTALL_DIR}/logs" 2>/dev/null
|
||||
echo -e "🗑️ 历史日志已按指令清空。"
|
||||
@@ -255,20 +226,17 @@ if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo -e "📦 历史配置与战地日志已妥善保留。"
|
||||
fi
|
||||
else
|
||||
# 全新安装模式:焦土政策,彻底抹除
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
rm -rf "${INSTALL_DIR}/core" "${INSTALL_DIR}/data" "${INSTALL_DIR}/config.conf" "${INSTALL_DIR}/.last_ip" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
echo -e "\033[32m✅ 环境清理完毕,幽灵进程已肃清!\033[0m"
|
||||
# ========================================================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 如果是全新部署,才执行以下所有交互逻辑;否则直接跳过
|
||||
# [交互装配] 从云端拓扑树中摘取节点信息并构建关联
|
||||
# ==========================================================
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
|
||||
# 📍 动态零级菜单:战区(大洲)选择
|
||||
echo -e "\n\033[36m📍 【第零级】请选择目标战区 (Continent):\033[0m"
|
||||
jq -r '.continents[] | "\(.id)|\(.name)"' "${SECURE_TMP}/map.json" > "${SECURE_TMP}/continents.txt"
|
||||
i=1; CONT_MAP=()
|
||||
@@ -282,7 +250,6 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
CONT_SEL=${CONT_SEL:-1}
|
||||
CONT_ID="${CONT_MAP[$CONT_SEL]}"
|
||||
|
||||
# 📍 动态一级菜单:国家选择 (基于选中战区)
|
||||
echo -e "\n\033[36m📍 【第一级】正在检索 [$CONT_ID] 战区下的国家/地区...\033[0m"
|
||||
jq -r ".continents[] | select(.id==\"$CONT_ID\") | .countries[] | \"\(.id)|\(.name)|\(.keyword_file)\"" "${SECURE_TMP}/map.json" > "${SECURE_TMP}/countries.txt"
|
||||
i=1; COUNTRY_MAP=(); KEYWORD_MAP=()
|
||||
@@ -297,9 +264,8 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
C_SEL=${C_SEL:-1}
|
||||
COUNTRY_ID="${COUNTRY_MAP[$C_SEL]}"
|
||||
KEYWORD_FILE="${KEYWORD_MAP[$C_SEL]}"
|
||||
REGION_CODE="$COUNTRY_ID" # 兼容旧版的 config.conf
|
||||
REGION_CODE="$COUNTRY_ID"
|
||||
|
||||
# 📍 动态二级菜单:省/州选择 (基于选中战区和国家)
|
||||
echo -e "\n\033[36m📍 【第二级】正在检索 [$COUNTRY_ID] 的行政区数据...\033[0m"
|
||||
jq -r ".continents[] | select(.id==\"$CONT_ID\") | .countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | \"\(.id)|\(.name)\"" "${SECURE_TMP}/map.json" > "${SECURE_TMP}/states.txt"
|
||||
STATE_COUNT=$(wc -l < "${SECURE_TMP}/states.txt")
|
||||
@@ -319,7 +285,6 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
STATE_ID="${STATE_MAP[$S_SEL]}"
|
||||
fi
|
||||
|
||||
# 📍 动态三级菜单:城市选择 (基于战区、国家、州三层过滤)
|
||||
echo -e "\n\033[36m📍 【第三级】请锁定具体城市节点:\033[0m"
|
||||
jq -r ".continents[] | select(.id==\"$CONT_ID\") | .countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | select(.id==\"$STATE_ID\") | .cities[] | \"\(.id)|\(.name)\"" "${SECURE_TMP}/map.json" > "${SECURE_TMP}/cities.txt"
|
||||
CITY_COUNT=$(wc -l < "${SECURE_TMP}/cities.txt")
|
||||
@@ -341,21 +306,17 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
CITY_NAME="${CITY_NAME_MAP[$CI_SEL]}"
|
||||
fi
|
||||
|
||||
# 清理临时文件 (增加清理 continents.txt)
|
||||
rm -f "${SECURE_TMP}/map.json" "${SECURE_TMP}/continents.txt" "${SECURE_TMP}/countries.txt" "${SECURE_TMP}/states.txt" "${SECURE_TMP}/cities.txt"
|
||||
|
||||
# 本地工作目录初始化 (支持 v3.0 的深度层级)
|
||||
mkdir -p "${INSTALL_DIR}/core"
|
||||
mkdir -p "${INSTALL_DIR}/data/keywords"
|
||||
mkdir -p "${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}"
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
|
||||
# 3. 功能模块前置开关 (v3.5.3 默认全量加载,后续经由 TG 动态启停)
|
||||
echo -e "\n[3/7] 正在初始化养护模块 (默认全量部署,支持 TG 远程动态启停)..."
|
||||
ENABLE_GOOGLE="true"
|
||||
ENABLE_TRUST="true"
|
||||
|
||||
# 4. 接入 Master 中枢配置
|
||||
echo -e "\n[4/7] 是否接入 Master 司令部进行远程联控? (y/n)"
|
||||
read -p "请输入选择 [y/n] (默认n): " TG_CHOICE
|
||||
TG_TOKEN=""
|
||||
@@ -374,17 +335,14 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
ENABLE_OTA="false"
|
||||
echo -e "\033[32m✅ 已自动连接官方安全网关 (@OmniBeacon_bot)。\033[0m"
|
||||
echo -e "\033[33m👉 请确保您已在 TG 中关注官方机器人并发送过 /start,否则将无法接收消息。\033[0m"
|
||||
# [v3.6.0 安全熔断]
|
||||
echo -e "\n\033[33m⚠️ 【安全熔断提示】\033[0m"
|
||||
echo -e "\033[33m由于您使用了官方公共网关,为防止潜在的滥用或供应链风险,本节点的 [OTA 远程升级] 权限已被系统底层强制禁用。\033[0m"
|
||||
echo -e "\033[33m💡 若未来需要启用 OTA,请自建私有中枢后重新部署本节点。\033[0m"
|
||||
else
|
||||
# [v3.6.0 优化] 使用 OSC 8 终端超链接协议,实现“点击即打开”的极客交互
|
||||
echo -e "\n\033[36m📘 私有 Bot 创建教程: \033[4m\033]8;;https://blog.iot-architect.com/engineering-practice/create-private-telegram-bot-via-botfather/\033\\👉 [点击此处直接在浏览器中打开]\033]8;;\033\\ 👈\033[0m"
|
||||
echo -e "\033[90m (若您的终端较老不支持点击,请手动复制: https://blog.iot-architect.com/engineering-practice/create-private-telegram-bot-via-botfather/ )\033[0m"
|
||||
read -p "请输入您的私有 Telegram Bot Token: " RAW_TOKEN
|
||||
USER_TOKEN=$(echo "$RAW_TOKEN" | tr -cd 'a-zA-Z0-9_:-')
|
||||
# 🛡️ 核心防误触修复:拦截空回车或粘贴换行导致的跳过 Bug
|
||||
while [ -z "$USER_TOKEN" ]; do
|
||||
read -p "⚠️ Token 不能为空或包含非法字符,请重新输入: " RAW_TOKEN
|
||||
USER_TOKEN=$(echo "$RAW_TOKEN" | tr -cd 'a-zA-Z0-9_:-')
|
||||
@@ -394,7 +352,6 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
TG_API_URL="https://api.telegram.org/bot${TG_TOKEN}/sendMessage"
|
||||
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
|
||||
|
||||
# [v3.6.0] 私有模式开放 OTA 授权向导
|
||||
echo -e "\n\033[36m[4.1/7] OTA 远程静默升级授权\033[0m"
|
||||
echo -e "💡 开启后,您可以在 TG 面板一键将本节点热更新至最新版本。"
|
||||
read -p "是否允许本节点接收 OTA 升级指令?(y/n, 默认y): " OTA_CHOICE
|
||||
@@ -411,15 +368,12 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
echo -e "\033[36m📘 查看图文教程: \033[4m\033]8;;https://blog.iot-architect.com/engineering-practice/get-telegram-personal-id-via-userinfobot/\033\\👉 [点击此处直接在浏览器中打开]\033]8;;\033\\ 👈\033[0m"
|
||||
echo -e "\033[90m (若您的终端较老不支持点击,请手动复制: https://blog.iot-architect.com/engineering-practice/get-telegram-personal-id-via-userinfobot/ )\033[0m"
|
||||
read -p "请输入你的 Chat ID (必须准确,否则无法联控): " RAW_CHAT_ID
|
||||
# 强制只保留数字和负号,封死注入
|
||||
CHAT_ID=$(echo "$RAW_CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# ================== [v3.0.3 变更: 智能随机高位端口生成系统] ==================
|
||||
echo -e "\n\033[36m[4.2/7] 正在构建 Webhook 安全通信隧道...\033[0m"
|
||||
echo -n "🎲 正在探测可用随机端口..."
|
||||
while true; do
|
||||
RANDOM_PORT=$((RANDOM % 55536 + 10000))
|
||||
# 同时兼容 ss (新) 和 netstat (旧) 检查端口占用
|
||||
if ! (ss -tuln 2>/dev/null | grep -q ":$RANDOM_PORT " || netstat -tuln 2>/dev/null | grep -q ":$RANDOM_PORT "); then
|
||||
break
|
||||
fi
|
||||
@@ -437,7 +391,6 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
AGENT_PORT="$RANDOM_PORT"
|
||||
break
|
||||
else
|
||||
# 校验手动输入的合法性与可用性
|
||||
if [[ "$INPUT_PORT" =~ ^[0-9]+$ ]] && [ "$INPUT_PORT" -ge 1 ] && [ "$INPUT_PORT" -le 65535 ]; then
|
||||
if (ss -tuln 2>/dev/null | grep -q ":$INPUT_PORT " || netstat -tuln 2>/dev/null | grep -q ":$INPUT_PORT "); then
|
||||
echo -e "\033[31m❌ 端口 $INPUT_PORT 已被占用,请重新输入或使用推荐端口。\033[0m"
|
||||
@@ -451,17 +404,16 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
fi
|
||||
done
|
||||
echo -e "✅ 已锁定 Webhook 通讯端口: \033[32m$AGENT_PORT\033[0m"
|
||||
# ====================================================================
|
||||
fi
|
||||
|
||||
# ================== [v3.0.1新增修改 1: 冗余网络栈探测与锚点锁定] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [网络锚定] 冗余网络栈探测与多出口智能嗅探
|
||||
# ----------------------------------------------------------
|
||||
echo -e "\n\033[36m[4.5/7] 正在探测本机网络栈与可用出口 (多节点雷达扫描中)...\033[0m"
|
||||
|
||||
# 引入容灾机制:依次尝试三个不同的 API,拿到有效的 IP 格式就停止
|
||||
DETECT_V4=$( (curl -4 -s -m 3 api.ip.sb/ip || curl -4 -s -m 3 ifconfig.me || curl -4 -s -m 3 ipv4.icanhazip.com) 2>/dev/null | grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -n 1 | tr -d '[:space:]')
|
||||
DETECT_V6=$( (curl -6 -s -m 3 api.ip.sb/ip || curl -6 -s -m 3 ifconfig.me || curl -6 -s -m 3 ipv6.icanhazip.com) 2>/dev/null | grep -E "^[0-9a-fA-F:]+.*:" | head -n 1 | tr -d '[:space:]')
|
||||
|
||||
# 构建动态选项数组
|
||||
IP_OPTIONS=()
|
||||
IP_PROTO=()
|
||||
|
||||
@@ -497,32 +449,27 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
|
||||
[[ "$PUBLIC_IP" == *":"* ]] && IP_PREF="6" || IP_PREF="4"
|
||||
else
|
||||
# 兜底:乱输就默认选第一个
|
||||
PUBLIC_IP="${IP_OPTIONS[0]}"
|
||||
IP_PREF="${IP_PROTO[0]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.3.1 核心重构: 身份剥离与双栈实弹嗅探] ==================
|
||||
# 1. 固化对外通讯身份 (自动穿透方括号护甲)
|
||||
# [容灾防线] 为含冒号的 IPv6 数据自动装卸方括号护盾,保障下游组件识别不崩溃
|
||||
if [[ "$PUBLIC_IP" == *":"* ]] && [[ "$PUBLIC_IP" != *"["* ]]; then
|
||||
SAFE_PUBLIC_IP="[${PUBLIC_IP}]"
|
||||
else
|
||||
SAFE_PUBLIC_IP="$PUBLIC_IP"
|
||||
fi
|
||||
|
||||
# 2. 实弹打靶测试 (NAT 环境嗅探与双栈自适应)
|
||||
echo -n "🕵️ 正在进行出站链路试射 (NAT环境与双栈嗅探)..."
|
||||
RAW_TEST_IP=$(echo "$SAFE_PUBLIC_IP" | tr -d '[]')
|
||||
|
||||
# 智能切换靶机:V6 机器打 Cloudflare V6 节点,V4 机器打 1.1.1.1
|
||||
if [[ "$RAW_TEST_IP" == *":"* ]]; then
|
||||
TEST_TARGET="https://[2606:4700:4700::1111]"
|
||||
else
|
||||
TEST_TARGET="https://1.1.1.1"
|
||||
fi
|
||||
|
||||
# 执行实弹试射
|
||||
if curl --interface "$RAW_TEST_IP" -sI -m 3 "$TEST_TARGET" >/dev/null 2>&1; then
|
||||
echo -e " \033[32m✅ 原生直连,物理网卡死锁已激活。\033[0m"
|
||||
BIND_IP="$SAFE_PUBLIC_IP"
|
||||
@@ -531,9 +478,8 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
BIND_IP=""
|
||||
fi
|
||||
echo -e "\033[32m✅ 哨兵对外联络点已永久锁定至: $SAFE_PUBLIC_IP\033[0m"
|
||||
# ========================================================================
|
||||
|
||||
# ================== [v3.5.2 新增: 节点不可变主键与展示别名] ==================
|
||||
# [身份分离] 分离底层系统锚定的不可变主键,与暴露给上层展示的可变别名
|
||||
IP_HASH=$(echo "${SAFE_PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||
NODE_NAME="$(hostname | tr -cd 'a-zA-Z0-9' | cut -c 1-10)-${IP_HASH}"
|
||||
NODE_ALIAS="$NODE_NAME"
|
||||
@@ -544,13 +490,11 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
read -p "请输入节点展示别名 (如'纽约机房', 回车使用默认): " CUSTOM_ALIAS
|
||||
|
||||
if [ -n "$CUSTOM_ALIAS" ]; then
|
||||
# 🛡️ 强制字符清洗:防御 Shell 注入,并限制长度防刷屏
|
||||
NODE_ALIAS=$(echo "$CUSTOM_ALIAS" | tr -d '"'\''\`\$\|&;<>\n\r' | cut -c 1-20)
|
||||
[ -z "$NODE_ALIAS" ] && NODE_ALIAS="$NODE_NAME"
|
||||
fi
|
||||
echo -e "✅ 已锁定节点展示别名: \033[32m$NODE_ALIAS\033[0m"
|
||||
fi
|
||||
# ========================================================================
|
||||
|
||||
# 5. 远程拉取冷数据并解析固化
|
||||
echo -e "\n[5/7] 正在从云端数据仓库拉取 [${CITY_NAME}] 节点的底层规则..."
|
||||
@@ -562,14 +506,12 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 使用 jq 提取 JSON 里的核心值
|
||||
REGION_NAME=$(jq -r '.region_name' "$REGION_JSON_FILE")
|
||||
BASE_LAT=$(jq -r '.google_module.base_lat' "$REGION_JSON_FILE")
|
||||
BASE_LON=$(jq -r '.google_module.base_lon' "$REGION_JSON_FILE")
|
||||
LANG_PARAMS=$(jq -r '.google_module.lang_params' "$REGION_JSON_FILE")
|
||||
VALID_URL_SUFFIX=$(jq -r '.google_module.valid_url_suffix' "$REGION_JSON_FILE")
|
||||
|
||||
# 写入本地静态配置文件 (v3.4.0 引入版本锚点)
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
# IP-Sentinel 本地固化配置 (生成时间: $(date '+%Y-%m-%d %H:%M:%S'))
|
||||
AGENT_VERSION="$TARGET_VERSION"
|
||||
@@ -591,32 +533,27 @@ AGENT_PORT="$AGENT_PORT"
|
||||
INSTALL_DIR="$INSTALL_DIR"
|
||||
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
||||
|
||||
# [v3.3.1修改: 双核身份剥离配置]
|
||||
IP_PREF="$IP_PREF"
|
||||
PUBLIC_IP="$SAFE_PUBLIC_IP"
|
||||
BIND_IP="$BIND_IP"
|
||||
|
||||
# [v3.5.2新增: 双轨身份系统]
|
||||
NODE_NAME="$NODE_NAME"
|
||||
NODE_ALIAS="$NODE_ALIAS"
|
||||
|
||||
# [v3.6.0新增: OTA 权限标识]
|
||||
ENABLE_OTA="$ENABLE_OTA"
|
||||
EOF
|
||||
|
||||
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
# ====================================================================
|
||||
|
||||
fi
|
||||
# 🛑 拦截块结束 (全套交互配置跳过完毕)
|
||||
|
||||
# ================== [v3.3.1 核心修复: 老节点配置无损热迁移] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [无感热重载] 老节点数据格式迁移兼容机制
|
||||
# ----------------------------------------------------------
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
if ! grep -q "PUBLIC_IP=" "$CONFIG_FILE"; then
|
||||
echo -e "\n🔄 [平滑迁移] 正在对老节点进行 v3.3.1 双核身份架构升级..."
|
||||
echo -e "\n🔄 [平滑迁移] 正在对老节点进行无损双核身份架构升级..."
|
||||
|
||||
# 重新抓取公网面孔 (应对老节点 BIND_IP 可能已被手动清空的情况)
|
||||
MIGRATE_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
|
||||
[[ "$MIGRATE_IP" == *":"* ]] && [[ "$MIGRATE_IP" != *"["* ]] && MIGRATE_IP="[${MIGRATE_IP}]"
|
||||
|
||||
@@ -636,20 +573,15 @@ if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
NEW_BIND_IP=""
|
||||
fi
|
||||
|
||||
# 动态修改旧配置文件 (更新 BIND_IP,追加 PUBLIC_IP)
|
||||
sed -i "s/^BIND_IP=.*/BIND_IP=\"$NEW_BIND_IP\"/" "$CONFIG_FILE"
|
||||
echo "PUBLIC_IP=\"$MIGRATE_IP\"" >> "$CONFIG_FILE"
|
||||
|
||||
# 刷新当前安装脚本的环境变量,防止底部代码报错
|
||||
SAFE_PUBLIC_IP="$MIGRATE_IP"
|
||||
BIND_IP="$NEW_BIND_IP"
|
||||
else
|
||||
# 如果是未来再升级,配置文件已是最新,直接提取变量供安装脚本尾部使用
|
||||
# [修复] 避免 cut 提取无引号变量失败,直接复用已 source 的原生变量
|
||||
SAFE_PUBLIC_IP="${PUBLIC_IP}"
|
||||
fi
|
||||
|
||||
# [v3.5.2 热修复] 兼容老版本没有 NODE_NAME 和 NODE_ALIAS 的情况,无损补齐
|
||||
if ! grep -q "^NODE_NAME=" "$CONFIG_FILE"; then
|
||||
TMP_HASH=$(echo "${SAFE_PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||
NODE_NAME="$(hostname | tr -cd 'a-zA-Z0-9' | cut -c 1-10)-${TMP_HASH}"
|
||||
@@ -665,7 +597,6 @@ if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# [v3.6.0 热修复] 兼容老版本没有 ENABLE_OTA 的情况,无损补齐默认关闭以防滥用
|
||||
if ! grep -q "^ENABLE_OTA=" "$CONFIG_FILE"; then
|
||||
echo "ENABLE_OTA=\"false\"" >> "$CONFIG_FILE"
|
||||
ENABLE_OTA="false"
|
||||
@@ -673,17 +604,17 @@ if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
ENABLE_OTA=$(grep "^ENABLE_OTA=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||
fi
|
||||
fi
|
||||
# ========================================================================
|
||||
|
||||
# 6. 拉取全套组件 (原子化升级,防断网变砖)
|
||||
# ==========================================================
|
||||
# [原子交接] 防变砖双缓冲下载执行域
|
||||
# 必须保证核心模块物理就绪后,才允许向当前正在运行的旧引擎开火
|
||||
# ==========================================================
|
||||
echo -e "\n[6/7] 正在部署核心引擎与热数据..."
|
||||
mkdir -p "${INSTALL_DIR}/data/keywords"
|
||||
|
||||
# [核心修复] 开辟临时下载区,确保下载 100% 成功后再替换旧核心
|
||||
TMP_CORE="${SECURE_TMP}/core_update"
|
||||
mkdir -p "$TMP_CORE"
|
||||
|
||||
# 拉取核心代码至临时区
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/core/runner.sh" -o "${TMP_CORE}/runner.sh"
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/core/updater.sh" -o "${TMP_CORE}/updater.sh"
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/core/tg_report.sh" -o "${TMP_CORE}/tg_report.sh"
|
||||
@@ -693,7 +624,7 @@ curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/core/mod_google.sh" -
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/core/mod_trust.sh" -o "${TMP_CORE}/mod_trust.sh"
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/core/mod_quality.sh" -o "${TMP_CORE}/mod_quality.sh"
|
||||
|
||||
# 🛡️ 防砖终极校验:检查关键文件是否真实存在且不为空
|
||||
# 🛡️ 终极自检墙:一旦任意文件缺失或长度为零,直接熔断放弃覆写,确保宿主不宕机
|
||||
if [ ! -s "${TMP_CORE}/runner.sh" ] || [ ! -s "${TMP_CORE}/agent_daemon.sh" ]; then
|
||||
echo -e "\033[31m❌ 致命错误:核心代码拉取失败!网络阻断或 GitHub Raw 异常。\033[0m"
|
||||
echo "🛡️ 防砖机制触发:已中止覆盖,旧版哨兵引擎仍安全存活中。"
|
||||
@@ -701,8 +632,6 @@ if [ ! -s "${TMP_CORE}/runner.sh" ] || [ ! -s "${TMP_CORE}/agent_daemon.sh" ]; t
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 🟢 [原子化交接核心]: 校验完美通过,新代码已在本地备妥!
|
||||
# 此时再以雷霆手段镇压旧进程,杜绝遗言陷阱与断网变砖的可能!
|
||||
echo "⏳ 新引擎校验通过,正在抹杀旧版守护进程..."
|
||||
if is_systemd; then
|
||||
systemctl kill --signal=SIGKILL ip-sentinel-agent-daemon.service >/dev/null 2>&1 || true
|
||||
@@ -715,34 +644,30 @@ pkill -9 -f "tg_report.sh" >/dev/null 2>&1 || true
|
||||
pkill -9 -f "updater.sh" >/dev/null 2>&1 || true
|
||||
pkill -9 -f "sentinel_scheduler.sh" >/dev/null 2>&1 || true
|
||||
|
||||
# 执行代码目录的物理替换
|
||||
rm -rf "${INSTALL_DIR}/core" 2>/dev/null
|
||||
mv "$TMP_CORE" "${INSTALL_DIR}/core"
|
||||
chmod +x ${INSTALL_DIR}/core/*.sh
|
||||
|
||||
# 拉取热数据与词库
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/data/user_agents.txt" -o "${INSTALL_DIR}/data/user_agents.txt"
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/data/keywords/${KEYWORD_FILE}" -o "${INSTALL_DIR}/data/keywords/${KEYWORD_FILE}"
|
||||
else
|
||||
# 升级模式:利用已有的 REGION_CODE 更新通用词库
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 7. 配置系统定时任务 (高频调度与看门狗)
|
||||
# ==========================================================
|
||||
# [进程守护] Systemd 原生注入与微内核定时降级兜底
|
||||
# ==========================================================
|
||||
echo -e "\n[7/7] 正在注入系统守护进程与调度器..."
|
||||
|
||||
# [时钟同步核心] 获取部署时的绝对 UTC 时间锚点,用于打散全球节点的云端拉取并发
|
||||
DEPLOY_UTC_HOUR=$(date -u +%H)
|
||||
DEPLOY_UTC_MIN=$(date -u +%M)
|
||||
|
||||
# [v3.3.0 新增] 初始化 UA 指纹库更新时间戳,确立 30 天滚动周期的计算锚点 (强制 UTC)
|
||||
echo $(date -u +%s) > "${INSTALL_DIR}/core/.ua_last_update"
|
||||
|
||||
if is_systemd; then
|
||||
echo "💡 检测到 Systemd 环境,正在部署原生守护服务..."
|
||||
|
||||
# 1. Runner 核心养护模块服务与定时器
|
||||
cat > /etc/systemd/system/ip-sentinel-runner.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Runner Service
|
||||
@@ -761,7 +686,6 @@ EOF
|
||||
[Unit]
|
||||
Description=Timer for IP-Sentinel Runner Service
|
||||
[Timer]
|
||||
# [频率优化] 改用严格的 20 分钟步进,杜绝 OTA 瞬间的并发走火!
|
||||
OnCalendar=*:0/20
|
||||
RandomizedDelaySec=180
|
||||
Persistent=true
|
||||
@@ -770,7 +694,6 @@ Unit=ip-sentinel-runner.service
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
|
||||
# 2. Updater 养料更新模块服务与定时器
|
||||
cat > /etc/systemd/system/ip-sentinel-updater.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Updater Service
|
||||
@@ -789,7 +712,6 @@ EOF
|
||||
[Unit]
|
||||
Description=Timer for IP-Sentinel Updater Service
|
||||
[Timer]
|
||||
# [绝对 UTC 锚点] 每天精确在部署的时刻触发,实现全球请求的天然削峰
|
||||
OnCalendar=*-*-* ${DEPLOY_UTC_HOUR}:${DEPLOY_UTC_MIN}:00 UTC
|
||||
Persistent=true
|
||||
Unit=ip-sentinel-updater.service
|
||||
@@ -801,7 +723,6 @@ EOF
|
||||
systemctl enable --now ip-sentinel-runner.timer ip-sentinel-updater.timer
|
||||
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
# 3. TG 战报服务与定时器
|
||||
cat > /etc/systemd/system/ip-sentinel-report.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Telegram Report Service
|
||||
@@ -820,14 +741,12 @@ EOF
|
||||
[Unit]
|
||||
Description=Timer for IP-Sentinel Telegram Report Service
|
||||
[Timer]
|
||||
# [绝对 UTC 锚点] 全球统一:每天 UTC 16:00 准时向司令部发送战报
|
||||
OnCalendar=*-*-* 16:00:00 UTC
|
||||
Unit=ip-sentinel-report.service
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
|
||||
# 4. [排雷修复] Agent Daemon Webhook 监听守护服务 (Type=simple, 常驻执行)
|
||||
cat > /etc/systemd/system/ip-sentinel-agent-daemon.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Agent Daemon Service
|
||||
@@ -846,8 +765,6 @@ IOSchedulingClass=idle
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# [修复竞态]: 提前写入公网 IP 缓存,阻断重复推送
|
||||
# 强制使用无参数 curl 裸奔探测,对齐 agent_daemon 的认知,防止双栈机型 IPv4/v6 认知错乱导致重启误报
|
||||
DAEMON_IP=$( (curl -s -m 5 api.ip.sb/ip || curl -s -m 5 ifconfig.me) 2>/dev/null | tr -d '[:space:]' )
|
||||
[ -n "$DAEMON_IP" ] && echo "$DAEMON_IP" > "${INSTALL_DIR}/core/.last_ip" || echo "$(echo "$SAFE_PUBLIC_IP" | tr -d '[]')" > "${INSTALL_DIR}/core/.last_ip"
|
||||
|
||||
@@ -858,12 +775,8 @@ EOF
|
||||
else
|
||||
echo "💡 未检测到 Systemd,正在配置备用调度器 (兼容 Alpine/OpenRC)..."
|
||||
|
||||
# ==========================================
|
||||
# 🛑 智能环境嗅探: 判定是否为受限的 Alpine 容器环境
|
||||
# ==========================================
|
||||
IS_RESTRICTED_ALPINE="false"
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
# 探测虚拟化类型:/proc/vz(OpenVZ), environ包含lxc(LXC), /.dockerenv(Docker)
|
||||
if [ -d /proc/vz ] || grep -qa container=lxc /proc/1/environ 2>/dev/null || [ -f /.dockerenv ]; then
|
||||
IS_RESTRICTED_ALPINE="true"
|
||||
fi
|
||||
@@ -873,7 +786,6 @@ EOF
|
||||
echo -e "⚠️ 探测到受限的 LXC/OpenVZ Alpine 环境,系统自带 Cron 极易假死。"
|
||||
echo -e "🔧 自动降维打击:启用 [自定义高可用死循环调度器] 接管全局任务..."
|
||||
|
||||
# 1. 禁用原有的 Cron 大管家 (防止冲突)
|
||||
rc-update del crond default >/dev/null 2>&1 || true
|
||||
rc-service crond stop >/dev/null 2>&1 || true
|
||||
pkill -9 crond >/dev/null 2>&1 || true
|
||||
@@ -881,23 +793,17 @@ EOF
|
||||
[ -f "${SECURE_TMP}/cron_clean" ] && crontab "${SECURE_TMP}/cron_clean" >/dev/null 2>&1
|
||||
rm -f "${SECURE_TMP}/cron_clean"
|
||||
|
||||
# 2. 写入我们的死循环守护进程
|
||||
# [极客修复] 将 << 'EOF' 变为 << EOF,以允许在安装时将部署时刻的 DEPLOY_UTC 变量作为硬编码注入脚本中
|
||||
cat > ${INSTALL_DIR}/core/sentinel_scheduler.sh << EOF
|
||||
#!/bin/bash
|
||||
while true; do
|
||||
# 强制获取绝对 UTC 时分,免疫系统错误时区
|
||||
MIN=\$(date -u +%M)
|
||||
HOUR=\$(date -u +%H)
|
||||
# [频率优化] 匹配 20 分钟步进 (00, 20, 40)
|
||||
if [ "\$MIN" == "00" ] || [ "\$MIN" == "20" ] || [ "\$MIN" == "40" ]; then
|
||||
/bin/bash /opt/ip_sentinel/core/runner.sh >/dev/null 2>&1
|
||||
fi
|
||||
# [绝对 UTC 锚点] 基于部署时刻的锚点触发热数据更新,天然并发削峰
|
||||
if [ "\$HOUR" == "${DEPLOY_UTC_HOUR}" ] && [ "\$MIN" == "${DEPLOY_UTC_MIN}" ]; then
|
||||
/bin/bash /opt/ip_sentinel/core/updater.sh >/dev/null 2>&1
|
||||
fi
|
||||
# [绝对 UTC 锚点] 统一 UTC 16:00 发送战报
|
||||
if [ "\$HOUR" == "16" ] && [ "\$MIN" == "00" ]; then
|
||||
/bin/bash /opt/ip_sentinel/core/tg_report.sh >/dev/null 2>&1
|
||||
fi
|
||||
@@ -909,36 +815,25 @@ done
|
||||
EOF
|
||||
chmod +x ${INSTALL_DIR}/core/sentinel_scheduler.sh
|
||||
|
||||
# 3. 写入 OpenRC 开机自启
|
||||
if command -v rc-update >/dev/null 2>&1 && [ -d "/etc/local.d" ]; then
|
||||
echo "nohup bash ${INSTALL_DIR}/core/sentinel_scheduler.sh >/dev/null 2>&1 &" > /etc/local.d/ip_sentinel_scheduler.start
|
||||
chmod +x /etc/local.d/ip_sentinel_scheduler.start
|
||||
rc-update add local default >/dev/null 2>&1
|
||||
else
|
||||
# 连 OpenRC 都没有的极端环境,写入 profile 兜底
|
||||
grep -q "sentinel_scheduler" /etc/profile || echo "nohup bash ${INSTALL_DIR}/core/sentinel_scheduler.sh >/dev/null 2>&1 &" >> /etc/profile
|
||||
fi
|
||||
|
||||
# 4. 立即后台启动
|
||||
[ -n "$PUBLIC_IP" ] && echo "$PUBLIC_IP" > "${INSTALL_DIR}/core/.last_ip"
|
||||
nohup bash ${INSTALL_DIR}/core/sentinel_scheduler.sh >/dev/null 2>&1 &
|
||||
|
||||
else
|
||||
# ==========================================
|
||||
# 🟢 走常规调度路线 (正常的 Linux 或 KVM 型 Alpine)
|
||||
# ==========================================
|
||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" > "${SECURE_TMP}/cron_backup" || true
|
||||
# [频率优化] 调整为 */20
|
||||
echo "*/20 * * * * ${INSTALL_DIR}/core/runner.sh >/dev/null 2>&1" >> "${SECURE_TMP}/cron_backup"
|
||||
# [绝对 UTC 锚点] 每天精确在部署的 UTC 时刻触发
|
||||
echo "${DEPLOY_UTC_MIN} ${DEPLOY_UTC_HOUR} * * * ${INSTALL_DIR}/core/updater.sh >/dev/null 2>&1" >> "${SECURE_TMP}/cron_backup"
|
||||
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
# [绝对 UTC 锚点] 统一 UTC 16:00
|
||||
echo "0 16 * * * ${INSTALL_DIR}/core/tg_report.sh >/dev/null 2>&1" >> "${SECURE_TMP}/cron_backup"
|
||||
echo "$SAFE_PUBLIC_IP" > "${INSTALL_DIR}/core/.last_ip"
|
||||
# [修复竞态]: 提前写入公网 IP 缓存,阻断重复推送
|
||||
# 强制使用无参数 curl 裸奔探测,对齐 agent_daemon 的认知,防止双栈机型 IPv4/v6 认知错乱导致重启误报
|
||||
DAEMON_IP=$( (curl -s -m 5 api.ip.sb/ip || curl -s -m 5 ifconfig.me) 2>/dev/null | tr -d '[:space:]' )
|
||||
[ -n "$DAEMON_IP" ] && echo "$DAEMON_IP" > "${INSTALL_DIR}/core/.last_ip" || echo "$(echo "$SAFE_PUBLIC_IP" | tr -d '[]')" > "${INSTALL_DIR}/core/.last_ip"
|
||||
|
||||
@@ -973,18 +868,17 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.4.0 核心: 状态机驱动的热更新路由] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [通讯指控] 部署后首播,打入中枢通信网关及指令态势传递
|
||||
# ----------------------------------------------------------
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
|
||||
# [v3.6.0 核心] 发送携带全套身份属性的注册指令 (追加 ENABLE_OTA 作为第 7 个字段)
|
||||
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${SAFE_PUBLIC_IP}|${AGENT_PORT}|${NODE_ALIAS}|${ENABLE_OTA}"
|
||||
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
# 读取本地老版本号,如果没有则视为远古版本 v3.3.1
|
||||
OLD_VERSION=$(grep "^AGENT_VERSION=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||
[ -z "$OLD_VERSION" ] && OLD_VERSION="3.3.1"
|
||||
|
||||
# [路由表 1]: 跨代兼容 (老版本 < v3.3.2)
|
||||
if version_lt "$OLD_VERSION" "3.3.2"; then
|
||||
echo -e "\n📡 [路由枢纽] 正在执行跨代架构重组 (v${OLD_VERSION} -> v${TARGET_VERSION})..."
|
||||
TEXT_MSG="✨ *IP-Sentinel 引擎热更新完成!*
|
||||
@@ -995,13 +889,11 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
⚠️ *战区架构已重组,请务必点击下方指令并发送,以同步新的防撞档案:*
|
||||
\`${REG_MSG}\`"
|
||||
|
||||
# [v4.0.3 体验升级] 注入交互式控制台按钮
|
||||
JSON_PAYLOAD=$(jq -n --arg cid "$CHAT_ID" --arg txt "$TEXT_MSG" --arg cb "manage:${NODE_NAME}" '{chat_id: $cid, text: $txt, parse_mode: "Markdown", reply_markup: {inline_keyboard: [[{text: "⚙️ 调出该节点控制台", callback_data: $cb}]]}}')
|
||||
curl -s -X POST "${TG_API_URL}" -H "Content-Type: application/json" -d "$JSON_PAYLOAD" >/dev/null 2>&1
|
||||
|
||||
echo -e "\033[32m✅ 升级通知已推送!请前往 TG 点击注册指令完成身份同步!\033[0m"
|
||||
|
||||
# [路由表 2]: 现代静默升级 (老版本 >= v3.3.2)
|
||||
else
|
||||
echo -e "\n📡 [路由枢纽] 正在执行静默平滑升级 (v${OLD_VERSION} -> v${TARGET_VERSION})..."
|
||||
TEXT_MSG="✨ *IP-Sentinel 引擎热更新完成!*
|
||||
@@ -1009,15 +901,13 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
🌐 IP:\`${SAFE_PUBLIC_IP}\`
|
||||
🚀 状态:v${TARGET_VERSION} OTA 动态活体引擎已部署"
|
||||
|
||||
# [v4.0.3 体验升级] 注入交互式控制台按钮
|
||||
JSON_PAYLOAD=$(jq -n --arg cid "$CHAT_ID" --arg txt "$TEXT_MSG" --arg cb "manage:${NODE_NAME}" '{chat_id: $cid, text: $txt, parse_mode: "Markdown", reply_markup: {inline_keyboard: [[{text: "⚙️ 调出该节点控制台", callback_data: $cb}]]}}')
|
||||
curl -s -X POST "${TG_API_URL}" -H "Content-Type: application/json" -d "$JSON_PAYLOAD" >/dev/null 2>&1
|
||||
|
||||
echo -e "\033[32m✅ 升级成功通知已推送到您的 Telegram!\033[0m"
|
||||
fi
|
||||
|
||||
# [清理遗留垃圾并刷新版本号]
|
||||
sed -i '/^NAME_HASHED=/d' "$CONFIG_FILE" 2>/dev/null # 抹除上个版本的临时基因锁
|
||||
sed -i '/^NAME_HASHED=/d' "$CONFIG_FILE" 2>/dev/null
|
||||
if grep -q "^AGENT_VERSION=" "$CONFIG_FILE"; then
|
||||
sed -i "s/^AGENT_VERSION=.*/AGENT_VERSION=\"$TARGET_VERSION\"/" "$CONFIG_FILE"
|
||||
else
|
||||
@@ -1025,7 +915,6 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
fi
|
||||
|
||||
else
|
||||
# [全新安装路由]
|
||||
echo -e "\n📡 正在向指挥部发送注册暗号..."
|
||||
TEXT_MSG="✨ *IP-Sentinel 部署成功!*
|
||||
📍 区域:${REGION_NAME}
|
||||
@@ -1035,7 +924,6 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
🔑 *请点击下方指令复制并回复给机器人:*
|
||||
\`${REG_MSG}\`"
|
||||
|
||||
# [v4.0.3 体验升级] 注入交互式控制台按钮
|
||||
JSON_PAYLOAD=$(jq -n --arg cid "$CHAT_ID" --arg txt "$TEXT_MSG" --arg cb "manage:${NODE_NAME}" '{chat_id: $cid, text: $txt, parse_mode: "Markdown", reply_markup: {inline_keyboard: [[{text: "⚙️ 调出该节点控制台", callback_data: $cb}]]}}')
|
||||
PUSH_RESULT=$(curl -s -X POST "${TG_API_URL}" -H "Content-Type: application/json" -d "$JSON_PAYLOAD")
|
||||
|
||||
@@ -1046,7 +934,6 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# =========================================================================
|
||||
|
||||
echo "========================================================"
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
@@ -1059,14 +946,12 @@ echo "⚙️ 哨兵现已开启 [每20分钟] 的高频高拟真养护循环。"
|
||||
if [[ -n "$TG_TOKEN" ]]; then
|
||||
echo "📡 Webhook 监听已启动 (端口: $AGENT_PORT) 并向中枢发送了注册请求。"
|
||||
|
||||
# ================== [v3.0.3 变更: 智能防火墙检测与放行指引] ==================
|
||||
FW_MSG=""
|
||||
if command -v ufw >/dev/null 2>&1 && ufw status | grep -qw active; then
|
||||
FW_MSG="ufw allow $AGENT_PORT/tcp"
|
||||
elif command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active firewalld | grep -qw active; then
|
||||
FW_MSG="firewall-cmd --zone=public --add-port=$AGENT_PORT/tcp --permanent && firewall-cmd --reload"
|
||||
elif command -v iptables >/dev/null 2>&1; then
|
||||
# 智能双栈雷达:根据对外公网 IP 属性,动态下发对应的防火墙放行指令
|
||||
if [[ "$SAFE_PUBLIC_IP" == *":"* ]]; then
|
||||
FW_MSG="ip6tables -I INPUT -p tcp --dport $AGENT_PORT -j ACCEPT"
|
||||
else
|
||||
@@ -1081,13 +966,10 @@ if [[ -n "$TG_TOKEN" ]]; then
|
||||
echo "💡 检测到本地系统防火墙开启,您可以尝试执行以下命令放行本机端口 (注意: 云端安全组仍需您手动放行):"
|
||||
echo -e "\033[36m $FW_MSG\033[0m"
|
||||
fi
|
||||
# ====================================================================
|
||||
fi
|
||||
echo "🗑️ 若未来需卸载,可重新运行本脚本选择[2]或执行: bash ${INSTALL_DIR}/core/uninstall.sh"
|
||||
echo "========================================================"
|
||||
|
||||
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
|
||||
# [修复] 仅在全新部署时触发统计,平滑升级/OTA 时绝对不触发,防止配额耗尽与数据注水
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名,不收集IP)..."
|
||||
AGENT_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/agent" || echo "")
|
||||
@@ -1099,9 +981,8 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [新增: 安装成功高光时刻 Star 引导] ==================
|
||||
echo -e "\n========================================================"
|
||||
echo -e "⭐ \033[33m开源不易,如果 IP-Sentinel 提升了您的节点稳定性,请赐予我们一枚星标!\033[0m"
|
||||
echo -e "💡 \033[32m您的每一颗 Star 都是我们持续对抗风控、维护更新指纹库的核心动力。\033[0m"
|
||||
echo -e "👉 \033[36m\033[4m\033]8;;https://github.com/hotyue/IP-Sentinel\033\\点击此处直达 GitHub 仓库点亮 Star 🌟\033[0m\033]8;;\033\\"
|
||||
echo -e "========================================================\n"
|
||||
echo -e "========================================================\n"
|
||||
@@ -1,14 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: mod_google.sh (Google 业务逻辑模块 - 动态锚点版)
|
||||
# 核心功能: 执行坐标微抖动、模拟真实阅读时长、会话行为拉伸
|
||||
# 脚本名称: mod_google.sh
|
||||
# 核心功能: 区域网络模拟、行为轨迹拉伸、地理定位锚定
|
||||
# ==========================================================
|
||||
|
||||
MODULE_NAME="Google"
|
||||
CONFIG_FILE="/opt/ip_sentinel/config.conf"
|
||||
|
||||
# 1. 加载冷数据配置
|
||||
# --- [环境预载] ---
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
source "$CONFIG_FILE"
|
||||
else
|
||||
@@ -16,25 +16,22 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 容错机制:如果父进程没有传递 log 函数,则本地定义一个作为 fallback (v3.4.0 引入版本探针)
|
||||
# [容灾机制] 若宿主环境未注入日志函数,则启动 Fallback 接管
|
||||
if ! type log >/dev/null 2>&1; then
|
||||
log() {
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
# [版本锚定] 提取运行时动态版本标识
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 保证日志目录存在
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识
|
||||
# [时区对齐] 强制采用绝对 UTC 时间消除跨域日志偏移
|
||||
local core_msg=$(printf "[v%-5s] [%-5s] [%-7s] [%s] %s" "$local_ver" "$2" "$1" "$REGION_CODE" "$3")
|
||||
# [时区对齐] 强制无视本地时区,以绝对 UTC 时间写入日志
|
||||
echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] $core_msg" >> "${INSTALL_DIR}/logs/sentinel.log"
|
||||
|
||||
# 强制推送到 Systemd Journal (如果系统支持)
|
||||
# [系统挂载] 桥接至 Systemd Journal 守护日志
|
||||
if command -v logger >/dev/null 2>&1; then
|
||||
logger -t ip-sentinel "$core_msg"
|
||||
else
|
||||
# 降级输出到 stdout,让 Systemd 捕获
|
||||
echo "$core_msg"
|
||||
fi
|
||||
}
|
||||
@@ -42,7 +39,7 @@ fi
|
||||
|
||||
log "$MODULE_NAME" "START" "========== 唤醒网络模拟器 [区域: $REGION_NAME] =========="
|
||||
|
||||
# 2. 动态加载热数据 (设备指纹池 和 专属搜索词库)
|
||||
# --- [数据装配] ---
|
||||
UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
|
||||
KW_FILE="${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
|
||||
|
||||
@@ -51,11 +48,10 @@ if [ ! -f "$UA_FILE" ] || [ ! -f "$KW_FILE" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 将文本按行读取到数组中 (并自动过滤空行)
|
||||
mapfile -t UA_POOL < <(grep -v '^$' "$UA_FILE")
|
||||
mapfile -t KEYWORDS < <(grep -v '^$' "$KW_FILE")
|
||||
|
||||
# --- [工具函数] ---
|
||||
# --- [辅助运算] ---
|
||||
get_random_coord() {
|
||||
local base=$1
|
||||
local range=$2
|
||||
@@ -63,38 +59,34 @@ get_random_coord() {
|
||||
awk "BEGIN {print ($base + $offset)}"
|
||||
}
|
||||
|
||||
# --- [环境初始化] ---
|
||||
# [v3.3.1修改] 优先读取对外公网面孔作为哈希种子,兼容 NAT 机的空 BIND_IP
|
||||
# --- [身份画像构建] ---
|
||||
# [防线提取] 优先捕获固化的公网面孔作为种子
|
||||
CURRENT_IP="${PUBLIC_IP:-${BIND_IP:-Unknown}}"
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
|
||||
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
|
||||
# [指纹固化] 哈希锚定法 (Hash-Seeded Persona)
|
||||
# 基于 IP 算力固定会话指纹池,彻底破除僵尸网络同质化特征
|
||||
# -----------------------------------------------------------
|
||||
TOTAL_UA=${#UA_POOL[@]}
|
||||
if [ "$TOTAL_UA" -gt 0 ]; then
|
||||
# 1. 以本地锁定的公网 IP 为种子,计算固定不变的 CRC32 哈希值
|
||||
SEED=$(echo -n "$CURRENT_IP" | cksum | awk '{print $1}')
|
||||
|
||||
# 2. 利用确定的种子和质数乘数,在全球 4000 的库中计算出本机的 3 个绝对专属坐标
|
||||
IDX1=$(( SEED % TOTAL_UA ))
|
||||
IDX2=$(( (SEED * 17) % TOTAL_UA ))
|
||||
IDX3=$(( (SEED * 31) % TOTAL_UA ))
|
||||
|
||||
# 3. 将绝对坐标映射为该节点的“专属设备库”
|
||||
MY_UA_POOL=("${UA_POOL[$IDX1]}" "${UA_POOL[$IDX2]}" "${UA_POOL[$IDX3]}")
|
||||
|
||||
# 4. 本次会话从这 3 台专属设备中随机挑选 1 台进行模拟
|
||||
SESSION_UA=${MY_UA_POOL[$RANDOM % 3]}
|
||||
else
|
||||
# 兜底容错机制
|
||||
# [极简兜底] 降维指纹防御崩溃
|
||||
SESSION_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
||||
fi
|
||||
# 位置锁定:在基准点(比如东京新宿)附近 3 公里内随机生成本次上网的“固定咖啡馆”坐标
|
||||
|
||||
# [LBS 锚定] 在基准战区内生成固定范围内的微抖动咖啡馆坐标
|
||||
SESSION_BASE_LAT=$(get_random_coord $BASE_LAT 270)
|
||||
SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
|
||||
|
||||
# 【核心升级】随机决定本次上网深度 (5 - 8 个复合动作,配合高频长效拉伸)
|
||||
# [行为学控制] 随机指派本次会话的动作深度
|
||||
TOTAL_ACTIONS=$((5 + RANDOM % 4))
|
||||
|
||||
log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_IP"
|
||||
@@ -102,21 +94,19 @@ log "$MODULE_NAME" "INFO " "设备指纹锁定: ${SESSION_UA:0:45}..."
|
||||
log "$MODULE_NAME" "INFO " "虚拟驻留坐标: $SESSION_BASE_LAT, $SESSION_BASE_LON"
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# [V3.2.1 热修复] 网络锚定与协议自适应构建
|
||||
# 强制 curl 绑定网卡,并自动匹配 IPv4/v6 协议,杜绝 curl 冲突报错
|
||||
# [网络栈探底] 协议自适应与出站死锁
|
||||
# -----------------------------------------------------------
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
# [防线校验] 探测物理网卡存活状态,防 IP 漂移引发通信雪崩
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log "$MODULE_NAME" "WARN " "检测到配置的出口 IP ($RAW_BIND_IP) 已丢失,自动降级为系统默认路由出网!"
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
@@ -127,34 +117,33 @@ if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- [行为循环模拟] ---
|
||||
# --- [会话漫游模拟] ---
|
||||
for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
# 模拟真实移动设备拿在手里时的 GPS 信号微抖动 (范围约 10 米)
|
||||
# [LBS 微抖动] 模拟人类手持设备时的 GPS 细微漂移
|
||||
ACTION_LAT=$(get_random_coord $SESSION_BASE_LAT 1)
|
||||
ACTION_LON=$(get_random_coord $SESSION_BASE_LON 1)
|
||||
|
||||
# 随机抽取一个符合当地特征的热点搜索词
|
||||
RAND_KEY=${KEYWORDS[$RANDOM % ${#KEYWORDS[@]}]}
|
||||
ENCODED_KEY=$(echo "$RAND_KEY" | jq -sRr @uri)
|
||||
|
||||
# 随机选择一种上网行为
|
||||
# [动作轮盘] 随机指派单次行为类型
|
||||
ACTION_TYPE=$((1 + RANDOM % 4))
|
||||
|
||||
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
|
||||
# [协议挂载] 注入双栈与网卡死锁参数
|
||||
case $ACTION_TYPE in
|
||||
1) # 搜索行为
|
||||
1) # 搜索引擎交互
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://www.google.com/search?q=${ENCODED_KEY}&${LANG_PARAMS}")
|
||||
;;
|
||||
2) # 浏览本土新闻
|
||||
2) # 区域新闻阅读
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://news.google.com/home?${LANG_PARAMS}")
|
||||
;;
|
||||
3) # 地图坐标查询
|
||||
3) # 坐标系 LBS 查询
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://www.google.com/maps/search/$${ENCODED_KEY}/@${ACTION_LAT},${ACTION_LON},17z?${LANG_PARAMS}")
|
||||
;;
|
||||
4) # 触发移动端系统底层位置检测像素
|
||||
4) # 底层系统级网络探测连通性握手
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://connectivitycheck.gstatic.com/generate_204")
|
||||
;;
|
||||
@@ -162,38 +151,31 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
|
||||
log "$MODULE_NAME" "EXEC " "动作[$i/$TOTAL_ACTIONS]完成 | HTTP状态: $CODE | 抖动坐标: $ACTION_LAT, $ACTION_LON"
|
||||
|
||||
# 【核心升级】行为拉伸:每次动作后强制休眠 90 - 120 秒
|
||||
# 结合动作总数,总耗时将稳定在 10 分钟 到 20 分钟之间
|
||||
# [行为拉伸] 动作间隔注入泊松长尾睡眠,模拟人类真实阅读思考时间
|
||||
if [ $i -lt $TOTAL_ACTIONS ]; then
|
||||
# 【时间收敛修复】休眠控制在 45-75 秒,防止跨周期重叠导致进程被强杀
|
||||
SLEEP_TIME=$((45 + RANDOM % 31))
|
||||
log "$MODULE_NAME" "WAIT " "阅读当前页面内容,模拟停留 $SLEEP_TIME 秒..."
|
||||
sleep $SLEEP_TIME
|
||||
fi
|
||||
done
|
||||
|
||||
# --- [结果纠偏自检 (V4.0.9 终极三核雷达: URL跳转 + Premium + Music)] ---
|
||||
# 战术揭秘:汲取开源社区顶级探针的精髓!
|
||||
# 1. 传统 URL 跳转探测:捕捉 www.google.com 底层 302 重定向域名的真实归属。
|
||||
# 2. YT Premium 深度探测:提取核心 contentRegion 变量,并强匹配 www.google.cn 送中特征。
|
||||
# 3. 严格一致性校验:任何一端出现非预期偏移,立即判定为漂移,彻底消除虚假“成功”。
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# [态势感知] 三核探测雷达 (跳转 / Premium / Music)
|
||||
# -----------------------------------------------------------
|
||||
log "$MODULE_NAME" "INFO " "启动三核交叉验证 (URL跳转 + YT Premium + YT Music) 穿透获取 GeoIP..."
|
||||
|
||||
# 核心 1: 传统 URL 跳转探测 (请求 www 才能触发准确跳转)
|
||||
# 探针 1: URL 区域重定向嗅探
|
||||
JUMP_HDR=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -sI "http://www.google.com/")
|
||||
JUMP_LOC=$(echo "$JUMP_HDR" | grep -i "^location:" | tr -d '\r\n')
|
||||
JUMP_GL=""
|
||||
|
||||
if [ -z "$JUMP_LOC" ]; then
|
||||
# 无跳转 (HTTP 200) 通常意味着原生被定位于 US
|
||||
JUMP_GL="US"
|
||||
elif [[ "$JUMP_LOC" == *".google.cn"* ]] || [[ "$JUMP_LOC" == *"gl=CN"* ]]; then
|
||||
JUMP_GL="CN"
|
||||
elif [[ "$JUMP_LOC" == *"gl="* ]]; then
|
||||
JUMP_GL=$(echo "$JUMP_LOC" | grep -o 'gl=[A-Za-z]\{2\}' | head -n 1 | cut -d'=' -f2 | tr 'a-z' 'A-Z')
|
||||
else
|
||||
# 从域名中提取区域后缀 (如 .co.jp -> JP, .com.hk -> HK, .de -> DE)
|
||||
JUMP_DOMAIN=$(echo "$JUMP_LOC" | grep -o 'google\.[a-z\.]*' | head -n 1 | sed 's/google\.//')
|
||||
case "$JUMP_DOMAIN" in
|
||||
"com") JUMP_GL="US" ;;
|
||||
@@ -215,7 +197,6 @@ else
|
||||
"cn") JUMP_GL="CN" ;;
|
||||
"") JUMP_GL="" ;;
|
||||
*)
|
||||
# 提取标准两字母后缀 (.de, .fr, .nl)
|
||||
LAST_EXT=$(echo "$JUMP_DOMAIN" | awk -F'.' '{print $NF}' | tr 'a-z' 'A-Z')
|
||||
if [ ${#LAST_EXT} -eq 2 ]; then
|
||||
JUMP_GL="$LAST_EXT"
|
||||
@@ -226,41 +207,38 @@ else
|
||||
esac
|
||||
fi
|
||||
|
||||
# 核心 2: YouTube Premium 探测
|
||||
# 探针 2: YouTube Premium 区域锁嗅探
|
||||
YT_PR_GL=""
|
||||
# [修复] 必须带上本轮循环的专属 UA (-A "$SESSION_UA"),防止被 Google CDN 丢进无状态爬虫兜底页
|
||||
YT_PR_HTML=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -L -A "$SESSION_UA" "https://www.youtube.com/premium")
|
||||
if [[ "$YT_PR_HTML" == *"www.google.cn"* ]]; then
|
||||
YT_PR_GL="CN"
|
||||
else
|
||||
# 穷举风控变量提取
|
||||
YT_PR_GL=$(echo "$YT_PR_HTML" | grep -o '"contentRegion":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||||
[ -z "$YT_PR_GL" ] && YT_PR_GL=$(echo "$YT_PR_HTML" | grep -o '"countryCode":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||||
[ -z "$YT_PR_GL" ] && YT_PR_GL=$(echo "$YT_PR_HTML" | grep -o '"INNERTUBE_CONTEXT_GL":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||||
fi
|
||||
|
||||
# 核心 3: YouTube Music 探测
|
||||
# 探针 3: YouTube Music 区域锁嗅探
|
||||
YT_MU_GL=""
|
||||
# [修复] 同样加持 UA 装甲,强行唤出完整版前端框架
|
||||
YT_MU_HTML=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -L -A "$SESSION_UA" "https://music.youtube.com/")
|
||||
if [[ "$YT_MU_HTML" == *"www.google.cn"* ]]; then
|
||||
YT_MU_GL="CN"
|
||||
else
|
||||
# [修复] Music 的核心配置变量是 INNERTUBE_CONTEXT_GL
|
||||
YT_MU_GL=$(echo "$YT_MU_HTML" | grep -o '"INNERTUBE_CONTEXT_GL":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||||
[ -z "$YT_MU_GL" ] && YT_MU_GL=$(echo "$YT_MU_HTML" | grep -o '"countryCode":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||||
[ -z "$YT_MU_GL" ] && YT_MU_GL=$(echo "$YT_MU_HTML" | grep -o '"GL":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||||
fi
|
||||
|
||||
# [基准对齐] 提取配置大区 (兼容州级穿透),并修正英国的 ISO 代码
|
||||
# [坐标规整] 兼容横杠分割体系,并修正英区缩写
|
||||
TARGET_CC="${REGION_CODE%%-*}"
|
||||
[ "$TARGET_CC" == "UK" ] && TARGET_CC="GB"
|
||||
|
||||
# --- 终极审判逻辑 (以 YouTube 核心业务为主导,兼顾底层雷达权重) ---
|
||||
# -----------------------------------------------------------
|
||||
# [终极审判] 异常过滤与一致性裁决机制
|
||||
# -----------------------------------------------------------
|
||||
IS_CN=0
|
||||
VALID_PROBES=0
|
||||
|
||||
# 1. 扫描所有探针,统计有效性并执行“送中”一票否决
|
||||
for val in "$JUMP_GL" "$YT_PR_GL" "$YT_MU_GL"; do
|
||||
if [ -n "$val" ]; then
|
||||
((VALID_PROBES++))
|
||||
@@ -273,22 +251,18 @@ if [ $VALID_PROBES -eq 0 ]; then
|
||||
elif [ $IS_CN -eq 1 ]; then
|
||||
STATUS="❌ 严重高危!三核雷达判定 IP 已被中国大陆锁定 (送中)!"
|
||||
else
|
||||
# 2. 评估核心流媒体业务是否达标 (只要 YT_PR 或 YT_MU 其一达标,即视为成功)
|
||||
# [权重仲裁] 以流媒体核心解锁状态为主导,允许基础网段跨国漂移
|
||||
YT_MATCH=0
|
||||
[ "$YT_PR_GL" == "$TARGET_CC" ] && YT_MATCH=1
|
||||
[ "$YT_MU_GL" == "$TARGET_CC" ] && YT_MATCH=1
|
||||
|
||||
if [ $YT_MATCH -eq 1 ]; then
|
||||
# 3. 核心业务达标,进一步评估底层路由权重
|
||||
if [ -n "$JUMP_GL" ] && [ "$JUMP_GL" != "$TARGET_CC" ]; then
|
||||
# YT 解锁了,但基础跳转 IP 库漂移了 (降级为 ✅,但备注底层漂移)
|
||||
STATUS="✅ 目标区域达成 (YT主导成功, Jump副雷达漂移至 ${JUMP_GL}) | Prem: ${YT_PR_GL:-无} | Music: ${YT_MU_GL:-无}"
|
||||
else
|
||||
# 完美达成
|
||||
STATUS="✅ 目标区域达成 (Jump: ${JUMP_GL:-无} | Prem: ${YT_PR_GL:-无} | Music: ${YT_MU_GL:-无})"
|
||||
fi
|
||||
else
|
||||
# YouTube 流媒体核心未能解锁目标区域,宣判漂移
|
||||
STATUS="⚠️ 区域发生漂移!目标 $TARGET_CC,实际 (Jump: ${JUMP_GL:-无} | Prem: ${YT_PR_GL:-无} | Music: ${YT_MU_GL:-无})"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# IP-Sentinel: 深海声呐 (IP 质量全维异步检测模块 v4.0.0)
|
||||
# IP-Sentinel: 深海声呐 (IP 质量全维异步检测模块)
|
||||
# 核心功能: 动态路由寻路、第三方 API 容灾获取、流媒体解锁链路剖析
|
||||
# ==========================================================
|
||||
|
||||
source /opt/ip_sentinel/config.conf
|
||||
|
||||
# ==========================================
|
||||
# 1. 动态网络锚定与协议自适应 (专为多 IP / NAT 架构打造)
|
||||
# ==========================================
|
||||
# ==========================================================
|
||||
# 1. 动态网络锚定与协议自适应
|
||||
# 专为多 IP 站群与 NAT 架构设计,确保探针流量精准路由
|
||||
# ==========================================================
|
||||
DYNAMIC_IP_PREF="${IP_PREF:-4}"
|
||||
PROBE_ARGS=("-y" "-j" "-f") # 默认注入: 自动确认、JSON格式、明文无掩码IP
|
||||
|
||||
# 强壮正则:支持 V4, V6 以及带有 [] 护甲的 V6 (兼容多 IP 站群机)
|
||||
# [强壮正则] 支持 V4, V6 以及带有 [] 护甲的 V6 寻址
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\[\]\.]+$ ]]; then
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
# 严格探测物理网卡/虚拟 IP 存活状态,防止 IP 漂移导致探针彻底报错
|
||||
if ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
# 恢复使用官方原生参数 -i,不再进行徒劳的底层劫持
|
||||
# 挂载原生出网网卡
|
||||
PROBE_ARGS+=("-i" "$RAW_BIND_IP")
|
||||
|
||||
# 智能识别 V4 / V6,强制覆盖系统默认的 IP_PREF
|
||||
@@ -31,32 +33,33 @@ fi
|
||||
# 补齐协议版本参数 (-4 或 -6)
|
||||
PROBE_ARGS+=("-${DYNAMIC_IP_PREF}")
|
||||
|
||||
# 2. 智能拉取引擎 (官方主干优先防 RCE,双栈 CDN 保底,外加文件防伪强校验)
|
||||
# ----------------------------------------------------------
|
||||
# 2. 智能拉取引擎 (防 RCE 与 文件防伪校验)
|
||||
# ----------------------------------------------------------
|
||||
PROBE_SCRIPT="/opt/ip_sentinel/core/ip_probe.sh"
|
||||
|
||||
# [校验 1] 验证本地残留脚本是否损坏 (防止之前被墙或拦截返回了 HTML 报错页)
|
||||
# [完整性校验] 验证本地残留脚本是否损坏 (防止因被墙或拦截导致本地缓存了无效的 HTML 报错页)
|
||||
if [ -f "$PROBE_SCRIPT" ] && ! grep -q "xykt" "$PROBE_SCRIPT" 2>/dev/null; then
|
||||
rm -f "$PROBE_SCRIPT"
|
||||
fi
|
||||
|
||||
if [ ! -s "$PROBE_SCRIPT" ]; then
|
||||
# 🛡️ 首选防线: 严格遵守从 GitHub 官方主干拉取,捍卫纯净底线
|
||||
# [首选防线] 严格遵守从官方主干拉取,捍卫纯净底线
|
||||
curl -sL -m 10 "https://raw.githubusercontent.com/xykt/IPQuality/main/ip.sh" -o "$PROBE_SCRIPT" 2>/dev/null
|
||||
|
||||
# 🚑 文件防伪校验: 如果纯 V6 无法解析 GitHub 返回了 HTML 报错页,剔除它!
|
||||
# [文件防伪校验] 剔除因解析失效返回的污染文本,并降级至双栈 CDN 节点兜底
|
||||
if ! grep -q "xykt" "$PROBE_SCRIPT" 2>/dev/null; then
|
||||
rm -f "$PROBE_SCRIPT" 2>/dev/null
|
||||
# 降级到双栈 CDN 节点兜底 (仅在 GitHub 彻底失效时启用)
|
||||
curl -sL -m 15 "https://IP.Check.Place" -o "$PROBE_SCRIPT" 2>/dev/null
|
||||
fi
|
||||
chmod +x "$PROBE_SCRIPT" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# ==========================================================
|
||||
# 3. 极速预检与容灾打靶系统
|
||||
# ==========================================
|
||||
|
||||
# 封装链路预检函数 (4秒极速探路,拒绝死等)
|
||||
# 封装链路预检函数 (4秒极速探路,拒绝死等阻塞)
|
||||
preflight_check() {
|
||||
local curl_args=("-s" "-m" "4")
|
||||
# 提取网卡和协议约束
|
||||
@@ -75,7 +78,7 @@ preflight_check() {
|
||||
return $?
|
||||
}
|
||||
|
||||
# 📡 寻路雷达:测定哪一组参数可以走通
|
||||
# [寻路雷达] 阶梯式探测,保证在极端环境下的探针连通性
|
||||
FINAL_ARGS=()
|
||||
if preflight_check "${PROBE_ARGS[@]}"; then
|
||||
# 阶梯 0: 原定参数 (带 BIND_IP 和协议) 通畅
|
||||
@@ -91,12 +94,11 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 4. 终极实弹打靶
|
||||
# ==========================================
|
||||
# ==========================================================
|
||||
# 4. 终极实弹打靶与情报萃取
|
||||
# ==========================================================
|
||||
|
||||
# 此时 FINAL_ARGS 已经被证实是连通的,我们只执行 1 次 ip.sh
|
||||
# 将超时放宽至 300 秒,给第三方 API (如 ipregistry) 充足的响应时间
|
||||
# 确保连通性后执行探测,放宽超时阈值以给予第三方 API 充足响应时间
|
||||
RAW_OUTPUT=$(timeout 300 bash "$PROBE_SCRIPT" "${FINAL_ARGS[@]}" 2>/dev/null)
|
||||
JSON_DATA="{${RAW_OUTPUT#*\{}"
|
||||
ESC=$(printf '\033')
|
||||
@@ -121,28 +123,32 @@ COUNTRY=$(echo "$JSON_DATA" | jq -r '.Info.Region.Name // "Unknown"' 2>/dev/null
|
||||
IP_TYPE=$(echo "$JSON_DATA" | jq -r '.Info.Type // "未知属性"' 2>/dev/null)
|
||||
USAGE_TYPE=$(echo "$JSON_DATA" | jq -r '.Type.Usage.IPinfo // "未知场景"' 2>/dev/null)
|
||||
|
||||
# 3. 深度欺诈与信用评估 (各大权威库联查)
|
||||
# ----------------------------------------------------------
|
||||
# 深度欺诈与信用评估 (各大权威库联查)
|
||||
# ----------------------------------------------------------
|
||||
SCAM_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.SCAMALYTICS // "0"' 2>/dev/null)
|
||||
ABUSE_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.AbuseIPDB // "0"' 2>/dev/null)
|
||||
IPQS_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.IPQS // "0"' 2>/dev/null)
|
||||
IP2L_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.IP2LOCATION // "0"' 2>/dev/null)
|
||||
FRAUD_RISK=$(echo "$JSON_DATA" | jq -r '.Score.ipapi // "0%"' 2>/dev/null)
|
||||
|
||||
# [修复] 清洗 API 阻断返回的 null 值,保障面板整洁
|
||||
# [数据清洗] 过滤 API 阻断带来的空值,确保面板展示整洁
|
||||
[ "$SCAM_SCORE" == "null" ] || [ -z "$SCAM_SCORE" ] && SCAM_SCORE="N/A"
|
||||
[ "$ABUSE_SCORE" == "null" ] || [ -z "$ABUSE_SCORE" ] && ABUSE_SCORE="N/A"
|
||||
[ "$IPQS_SCORE" == "null" ] || [ -z "$IPQS_SCORE" ] && IPQS_SCORE="N/A"
|
||||
[ "$IP2L_SCORE" == "null" ] || [ -z "$IP2L_SCORE" ] && IP2L_SCORE="N/A"
|
||||
[ "$FRAUD_RISK" == "null" ] || [ -z "$FRAUD_RISK" ] && FRAUD_RISK="N/A"
|
||||
|
||||
# 代理/VPN 特征探针 (只要有一家认为是代理,就亮黄灯)
|
||||
# [代理特征嗅探] 识别商业 VPN 或代理痕迹
|
||||
IS_PROXY="🟢 干净"
|
||||
if echo "$JSON_DATA" | jq -e '.Factor.Proxy | to_entries | any(.value == true)' >/dev/null 2>&1 || \
|
||||
echo "$JSON_DATA" | jq -e '.Factor.VPN | to_entries | any(.value == true)' >/dev/null 2>&1; then
|
||||
IS_PROXY="🟡 疑似代理/VPN"
|
||||
fi
|
||||
|
||||
# 4. 提取流媒体与 AI 解锁指标 (带解锁类型)
|
||||
# ----------------------------------------------------------
|
||||
# 提取流媒体与 AI 解锁指标
|
||||
# ----------------------------------------------------------
|
||||
parse_media() {
|
||||
local status=$(echo "$JSON_DATA" | jq -r ".Media.$1.Status // \"未知\"" 2>/dev/null)
|
||||
local reg=$(echo "$JSON_DATA" | jq -r ".Media.$1.Region // \"\"" 2>/dev/null)
|
||||
@@ -151,10 +157,10 @@ parse_media() {
|
||||
if [[ "$status" == *"解锁"* ]]; then
|
||||
echo "🟢 ${reg} (${type})"
|
||||
elif [[ "$status" == *"仅"* ]] || [[ "$status" == *"机房"* ]] || [[ "$status" == *"待支持"* ]]; then
|
||||
# 捕捉 Netflix "仅自制"、ChatGPT "仅网页"、TikTok "机房" 等半残状态
|
||||
# 捕捉 Netflix "仅自制"、ChatGPT "仅网页"、TikTok "机房" 等半残缺状态
|
||||
echo "🟡 ${status} ${reg}"
|
||||
elif [[ "$status" == *"屏蔽"* ]] || [[ "$status" == *"失败"* ]] || [[ "$status" == *"中国"* ]] || [[ "$status" == *"禁"* ]]; then
|
||||
# 捕捉 "屏蔽"、"失败"、"禁会员"、"中国"(送中)
|
||||
# 捕捉高危风控状态
|
||||
echo "🔴 ${status}"
|
||||
else
|
||||
echo "⚪ ${status}"
|
||||
@@ -168,31 +174,33 @@ TK_STAT=$(parse_media "TikTok")
|
||||
GPT_STAT=$(parse_media "ChatGPT")
|
||||
APV_STAT=$(parse_media "AmazonPrimeVideo")
|
||||
|
||||
# 提取原生 JSON 里的原始状态用于底层隐写回传
|
||||
# 提取原生 JSON 原始状态用于底层隐写回传
|
||||
RAW_NF_STAT=$(echo "$JSON_DATA" | jq -r '.Media.Netflix.Status // "Unknown"' 2>/dev/null)
|
||||
RAW_YT_REG=$(echo "$JSON_DATA" | jq -r '.Media.Youtube.Region // ""' 2>/dev/null)
|
||||
RAW_YT_STAT=$(echo "$JSON_DATA" | jq -r '.Media.Youtube.Status // "Unknown"' 2>/dev/null)
|
||||
|
||||
# 5. 邮局连通性与黑名单
|
||||
# ----------------------------------------------------------
|
||||
# 邮局连通性与黑名单污染度
|
||||
# ----------------------------------------------------------
|
||||
PORT25=$(echo "$JSON_DATA" | jq -r '.Mail.Port25 // "false"' 2>/dev/null)
|
||||
[ "$PORT25" == "true" ] && P25_TEXT="✅ 畅通" || P25_TEXT="❌ 封堵"
|
||||
DNS_BLACK=$(echo "$JSON_DATA" | jq -r '.Mail.DNSBlacklist.Blacklisted // "0"' 2>/dev/null)
|
||||
DNS_MARK=$(echo "$JSON_DATA" | jq -r '.Mail.DNSBlacklist.Marked // "0"' 2>/dev/null)
|
||||
|
||||
# 6. “送中” 逻辑判定
|
||||
# [送中逻辑判定] 基于核心指标一票否决
|
||||
WARNING_MSG=""
|
||||
# [修复] 官方 JSON 已经去除了方括号,直接匹配 CN 或者状态包含中国
|
||||
if [[ "$RAW_YT_REG" == "CN" ]] || [[ "$RAW_YT_STAT" == *"中国"* ]]; then
|
||||
# [修复] 采用 Bash 扩展转义 ($'...'),彻底解决直接打印 \n 字符的问题
|
||||
# 采用 Bash 扩展转义,避免直接打印字面换行符
|
||||
WARNING_MSG=$'\n🚨 **[高危] 该节点已被 Google 判定为中国大陆 (送中)!**\n'
|
||||
fi
|
||||
|
||||
# 7. 组装情报级 Markdown 战报
|
||||
# 提取本地运行态版本与生成时间戳
|
||||
# ==========================================================
|
||||
# 5. 组装情报级 Markdown 战报与回调构造
|
||||
# ==========================================================
|
||||
LOCAL_VER="${AGENT_VERSION:-未知}"
|
||||
# [时区对齐] 深海声呐战报落款强制采用绝对 UTC 时间
|
||||
CURRENT_TIME=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||
# [体验修复] 探针返回的 IP 带有星号掩码,强制使用中枢下发的真实 IP 拼接,以防直达链接失效!
|
||||
|
||||
# 强制使用中枢下发的真实 IP 拼接,以防探针星号掩码导致直达链接失效
|
||||
LINK_IP=$(echo "$PUBLIC_IP" | tr -d '[]')
|
||||
|
||||
REPORT="🎯 *IP-Sentinel 深海声呐报告*
|
||||
@@ -228,23 +236,22 @@ _👉 [🔍 详细信用图谱直达 (Scamalytics)](https://scamalytics.com/ip/$
|
||||
|
||||
⏱️ \`${CURRENT_TIME}\` | ⚙️ \`v${LOCAL_VER}\`"
|
||||
|
||||
# [修复] 剥离显示层的 N/A,确保传给 Master 趋势数据库的是纯数字 (无效则记为0)
|
||||
# [核心数据萃取] 剥离非数字残留,确保传给 Master 趋势数据库的纯净性
|
||||
SAFE_SCAM_SCORE=$(echo "$SCAM_SCORE" | tr -cd '0-9')
|
||||
[ -z "$SAFE_SCAM_SCORE" ] && SAFE_SCAM_SCORE="0"
|
||||
|
||||
# [v4.0.2 扩容] 提取 Google(基于YouTube) 和 ChatGPT 的原生状态
|
||||
# 提取 Google(基于YouTube) 和 ChatGPT 的原生状态
|
||||
RAW_GOOG_STAT="${RAW_YT_REG:-$RAW_YT_STAT}"
|
||||
[ -z "$RAW_GOOG_STAT" ] && RAW_GOOG_STAT="未知"
|
||||
RAW_GPT_STAT=$(echo "$JSON_DATA" | jq -r '.Media.ChatGPT.Status // "未知"' 2>/dev/null)
|
||||
|
||||
# [修复] 废除会导致中文 UTF-8 字节被劈裂(产生乱码 )的 awk 暴力截断。
|
||||
# 原始状态文本极短(如"解锁"、"屏蔽"、"US"),只需洗掉隐形换行符即可安全传输。
|
||||
# 废除可能导致中文字符截断乱码的强制限制,改用去隐形换行符的安全传递策略
|
||||
S_GOOG=$(echo "$RAW_GOOG_STAT" | tr -d '\n\r ')
|
||||
S_NF=$(echo "$RAW_NF_STAT" | tr -d '\n\r ')
|
||||
S_GPT=$(echo "$RAW_GPT_STAT" | tr -d '\n\r ')
|
||||
CB_DATA="svq|${NODE_NAME}|${SAFE_SCAM_SCORE}|${S_GOOG}|${S_NF}|${S_GPT}"
|
||||
|
||||
# 8. 挂载内联键盘并直送指挥部
|
||||
# 挂载内联键盘并直送指挥部
|
||||
JSON_PAYLOAD=$(jq -n \
|
||||
--arg cid "$CHAT_ID" \
|
||||
--arg txt "$REPORT" \
|
||||
|
||||
@@ -1,113 +1,104 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: mod_trust.sh (IP 信用净化模块 - 动态锚点版)
|
||||
# 脚本名称: mod_trust.sh
|
||||
# 核心功能: 动态扫描本地 LBS 冷数据,提取权威白名单,执行流量净化
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为私库地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# 1. 基础环境校验
|
||||
# --- [基础环境校验] ---
|
||||
[ ! -f "$CONFIG_FILE" ] && exit 1
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
REGION=${REGION_CODE:-"US"}
|
||||
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
||||
|
||||
# 2. 动态获取配置 (V3 拓扑自适应与兜底)
|
||||
# 利用 find 穿透多级子目录,自动抓取安装时落地的那份专属 json 文件
|
||||
# ==========================================================
|
||||
# 1. 动态获取配置 (拓扑自适应与兜底机制)
|
||||
# ==========================================================
|
||||
# 利用 find 穿透多级子目录,自动抓取安装时落地的专属 json 文件
|
||||
REGION_JSON_FILE=$(find "${INSTALL_DIR}/data/regions" -name "*.json" 2>/dev/null | head -n 1)
|
||||
|
||||
# 兼容旧节点兜底:如果本地真没找到 json,回退到拉取云端通用大区配置
|
||||
# [容灾兜底] 如果本地 json 异常,回退拉取云端通用大区配置
|
||||
if [ -z "$REGION_JSON_FILE" ] || [ ! -f "$REGION_JSON_FILE" ]; then
|
||||
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION}.json"
|
||||
mkdir -p "${INSTALL_DIR}/data/regions"
|
||||
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/regions/${REGION}.json" -o "$REGION_JSON_FILE"
|
||||
fi
|
||||
|
||||
# 使用 jq 将 json 中的网址数组安全地读入 Bash 数组
|
||||
# 解析安全网址数组
|
||||
if [ -f "$REGION_JSON_FILE" ]; then
|
||||
mapfile -t TRUST_URLS < <(jq -r '.trust_module.white_urls[]' "$REGION_JSON_FILE" 2>/dev/null)
|
||||
fi
|
||||
|
||||
# 兜底:如果仓库挂了或者解析失败,提供国际通用白名单
|
||||
# [极限容灾] 提供国际通用无害白名单防全盘崩溃
|
||||
if [ ${#TRUST_URLS[@]} -eq 0 ]; then
|
||||
TRUST_URLS=("https://en.wikipedia.org/wiki/Special:Random" "https://www.apple.com/" "https://www.microsoft.com/")
|
||||
fi
|
||||
|
||||
# 3. 日志规范化 (v3.4.0 引入版本探针)
|
||||
# --- [日志规范化组件] ---
|
||||
log_msg() {
|
||||
local TYPE=$1
|
||||
local MSG=$2
|
||||
# [时区对齐] 强制无视本地时区,以绝对 UTC 时间生成日志时间戳
|
||||
# 强制无视本地时区,统一采用 UTC 时间生成日志时间戳
|
||||
local TIME=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识,保持对齐
|
||||
echo "[$TIME] [v%-5s] [%-5s] [Trust ] [$REGION] $MSG" | sed "s/%-5s/$local_ver/;s/%-5s/$TYPE/" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 4. 锁定单次会话指纹
|
||||
# -----------------------------------------------------------
|
||||
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
|
||||
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
|
||||
# -----------------------------------------------------------
|
||||
# ==========================================================
|
||||
# 2. 锁定单次会话指纹 (Hash-Seeded Persona)
|
||||
# ==========================================================
|
||||
if [ -f "$UA_FILE" ]; then
|
||||
mapfile -t UA_POOL < <(grep -v '^$' "$UA_FILE")
|
||||
TOTAL_UA=${#UA_POOL[@]}
|
||||
|
||||
if [ "$TOTAL_UA" -gt 0 ]; then
|
||||
# [v3.3.1修改] 优先使用固化的公网 IP 作为哈希种子,防止 NAT 节点指纹同质化
|
||||
# 优先使用固化的公网 IP 作为哈希种子,防范 NAT 节点指纹同质化特征
|
||||
SEED=$(echo -n "${PUBLIC_IP:-${BIND_IP:-127.0.0.1}}" | cksum | awk '{print $1}')
|
||||
|
||||
# 利用确定的种子,在全球 4000 的库中,计算出本机的 3 个绝对专属坐标
|
||||
# 构建当前节点的固定设备组映射
|
||||
IDX1=$(( SEED % TOTAL_UA ))
|
||||
IDX2=$(( (SEED * 17) % TOTAL_UA ))
|
||||
IDX3=$(( (SEED * 31) % TOTAL_UA ))
|
||||
|
||||
# 将专属坐标映射为专属设备库
|
||||
MY_UA_POOL=("${UA_POOL[$IDX1]}" "${UA_POOL[$IDX2]}" "${UA_POOL[$IDX3]}")
|
||||
|
||||
# 本次会话从这 3 台专属设备中随机挑选 1 台 (模拟真实的家庭多设备环境)
|
||||
# 模拟真实的家庭多设备环境进行会话隔离
|
||||
CURRENT_UA=${MY_UA_POOL[$RANDOM % 3]}
|
||||
else
|
||||
# 兜底容错
|
||||
CURRENT_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
||||
fi
|
||||
else
|
||||
CURRENT_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 🚀 净化行动开始
|
||||
# ==========================================
|
||||
# ==========================================================
|
||||
# 3. 执行流量净化行动
|
||||
# ==========================================================
|
||||
log_msg "START" "========== 启动区域 IP 信用净化会话 =========="
|
||||
log_msg "INFO " "已载入 [${REGION}] 区域白名单,配置库条目: ${#TRUST_URLS[@]} 个"
|
||||
log_msg "INFO " "已锁定本地伪装指纹: $(echo $CURRENT_UA | cut -d' ' -f1-2)..."
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# [V3.2.1 热修复] 网络锚定与协议自适应构建
|
||||
# 强制 curl 绑定网卡,并自动匹配 IPv4/v6 协议,杜绝 curl 冲突报错
|
||||
# -----------------------------------------------------------
|
||||
# ----------------------------------------------------------
|
||||
# 网络锚定与协议自适应构建
|
||||
# 强制 curl 绑定网卡并自动匹配底层网络协议栈
|
||||
# ----------------------------------------------------------
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log_msg "WARN " "检测到配置的出口 IP ($RAW_BIND_IP) 已丢失,自动降级为系统默认路由出网!"
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
@@ -122,11 +113,9 @@ STEP_COUNT=$((RANDOM % 4 + 3))
|
||||
SUCCESS_INJECT=0
|
||||
|
||||
for ((i=1; i<=STEP_COUNT; i++)); do
|
||||
# 随机抽取本地区域权威网址
|
||||
TARGET_URL=${TRUST_URLS[$RANDOM % ${#TRUST_URLS[@]}]}
|
||||
|
||||
# [v3.0.1修复] 注入高权重流量时,强制从绑定的 IPv4 或 IPv6 隧道出网
|
||||
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
|
||||
# 注入高权重流量,严格绑定出网协议,构造隐蔽的安全伪装协议头
|
||||
HTTP_CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -A "$CURRENT_UA" \
|
||||
-H "Accept: text/html,application/xhtml+xml;q=0.9,image/avif,image/webp,*/*;q=0.8" \
|
||||
-H "Accept-Language: en-US,en;q=0.9" \
|
||||
@@ -136,7 +125,7 @@ for ((i=1; i<=STEP_COUNT; i++)); do
|
||||
--compressed \
|
||||
-s -o /dev/null -w "%{http_code}" -m 15 "$TARGET_URL")
|
||||
|
||||
# 扩大 HTTP 状态码容错区间:包含所有 20x (如亚马逊的 202) 和 30x 重定向
|
||||
# 扩大 HTTP 状态码容错区间:包含各大骨干 CDN 常见的 20x 及 30x 状态转移
|
||||
if [[ "$HTTP_CODE" =~ ^(20[0-9]|30[1-8])$ ]]; then
|
||||
log_msg "EXEC " "动作[$i/$STEP_COUNT]完成 | 状态: $HTTP_CODE | 注入: $TARGET_URL"
|
||||
((SUCCESS_INJECT++))
|
||||
@@ -151,9 +140,9 @@ for ((i=1; i<=STEP_COUNT; i++)); do
|
||||
fi
|
||||
done
|
||||
|
||||
# ==========================================
|
||||
# 📊 结论判定与输出
|
||||
# ==========================================
|
||||
# ==========================================================
|
||||
# 4. 结论判定与输出
|
||||
# ==========================================================
|
||||
if [ "$SUCCESS_INJECT" -ge $((STEP_COUNT / 2)) ]; then
|
||||
log_msg "SCORE" "自检结论: ✅ 信用净化完成 (已成功注入 $SUCCESS_INJECT 条无害流量)"
|
||||
else
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: runner.sh (IP-Sentinel 主控调度引擎 - 动态锚点版)
|
||||
# 核心功能: 防并发延迟启动、功能开关(Feature Flag)自适应、多模块概率轮盘调度
|
||||
# 脚本名称: runner.sh
|
||||
# 核心功能: 主控调度枢纽,管理防并发锁与 Feature Flag 概率轮盘
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
|
||||
# 1. 检查并加载本地冷数据配置
|
||||
# --- [基础环境构建] ---
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "配置文件丢失,请重新运行 install.sh"
|
||||
exit 1
|
||||
fi
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# ================== [新增: 文件排他锁,防止并发重入引发内存雪崩] ==================
|
||||
# ==========================================================
|
||||
# [防线 1] 进程排他锁管控
|
||||
# 严格防止高频并发重入引发的底层内存雪崩与死锁
|
||||
# ==========================================================
|
||||
exec 200>"/tmp/ip_sentinel_runner.lock"
|
||||
if ! flock -n 200; then
|
||||
echo "[$(date)] ⚠️ 上一轮巡逻任务尚未结束,本次触发自动取消。" >> "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
# ==================================================================================
|
||||
|
||||
# 2. 全局日志写入函数 (导出给子进程共享使用,v3.4.0 引入版本探针)
|
||||
# --- [系统级日志通道] ---
|
||||
log() {
|
||||
local module=$1
|
||||
local level=$2
|
||||
local msg=$3
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 保证日志目录存在
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识
|
||||
local core_msg=$(printf "[v%-5s] [%-5s] [%-7s] [%s] %s" "$local_ver" "$level" "$module" "$REGION_CODE" "$msg")
|
||||
echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] $core_msg" >> "$LOG_FILE"
|
||||
|
||||
# 强制推送到 Systemd Journal (如果系统支持)
|
||||
if command -v logger >/dev/null 2>&1; then
|
||||
logger -t ip-sentinel "$core_msg"
|
||||
else
|
||||
# 降级输出到 stdout,让 Systemd 捕获
|
||||
echo "$core_msg"
|
||||
fi
|
||||
}
|
||||
export -f log
|
||||
export CONFIG_FILE INSTALL_DIR
|
||||
|
||||
# 3. 防僵尸网络特征 (Cron Jitter) - 核心隐蔽逻辑
|
||||
# 配合每 20 分钟的调度周期,将随机休眠控制在 0 到 180 秒内,彻底打散全球并发请求
|
||||
# ==========================================================
|
||||
# [防线 2] 行为学隐蔽 (Cron Jitter)
|
||||
# 彻底消除僵尸网络同频定时唤醒特征,自然打散全球并发请求
|
||||
# ==========================================================
|
||||
if [ -t 1 ]; then
|
||||
log "SYSTEM" "INFO " "💻 检测到人工终端干预,跳过静默休眠,立即执行任务!"
|
||||
else
|
||||
@@ -59,15 +58,17 @@ else
|
||||
sleep $JITTER_TIME
|
||||
fi
|
||||
|
||||
# 4. 唤醒并读取功能开关,执行智能调度 (Feature Flag)
|
||||
# ==========================================================
|
||||
# 智能轮盘赌调度系统 (基于 Feature Flag)
|
||||
# ==========================================================
|
||||
log "SYSTEM" "INFO" "休眠结束,开始计算本轮任务轮盘..."
|
||||
|
||||
TARGET_MOD=""
|
||||
MOD_NAME=""
|
||||
|
||||
# 智能轮盘赌算法
|
||||
# 概率任务分配模型
|
||||
if [ "$ENABLE_GOOGLE" == "true" ] && [ "$ENABLE_TRUST" == "true" ]; then
|
||||
# 双管齐下: 70% 概率跑 Google 稳固定位,30% 概率跑 Trust 洗刷风控分
|
||||
# 优先锚定地理画像,辅助洗刷风控分
|
||||
ROLL=$((RANDOM % 100 + 1))
|
||||
if [ $ROLL -le 70 ]; then
|
||||
TARGET_MOD="mod_google.sh"
|
||||
@@ -87,11 +88,12 @@ else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 5. 拉起选定的业务模块
|
||||
# ----------------------------------------------------------
|
||||
# 安全执行与资源剥离
|
||||
# ----------------------------------------------------------
|
||||
if [ -n "$TARGET_MOD" ] && [ -x "${INSTALL_DIR}/core/${TARGET_MOD}" ]; then
|
||||
log "SYSTEM" "INFO" "命中触发条件,加载并执行子模块: ${MOD_NAME}"
|
||||
# 核心降耗逻辑:使用 nice -n 19 赋予进程最低 CPU 优先级,绝不抢占 VPS 正常业务的资源
|
||||
# [安全修复] 注入 200>&-,强行关闭子进程对排他锁的继承权!防止子进程假死导致全局死锁
|
||||
# [进程隔离与降耗] 赋予最低 CPU 优先级,并强制剥离排他锁的继承权,防止子进程假死拖垮全局
|
||||
nice -n 19 bash "${INSTALL_DIR}/core/${TARGET_MOD}" 200>&-
|
||||
else
|
||||
log "SYSTEM" "ERROR" "配置了模块 ${MOD_NAME},但未找到对应的可执行脚本: ${TARGET_MOD}"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: tg_report.sh (Telegram 每日战报模块 - 动态锚点版)
|
||||
# 核心功能: 适配 Feature Flag 架构,按需展示独立统计数据,OTA 更新预警
|
||||
# 脚本名称: tg_report.sh
|
||||
# 核心功能: 收集并聚合终端特征、提取执行快照、侦测云端版本并生成简报
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
||||
|
||||
# 1. 加载配置并自检
|
||||
# --- [基础自检] ---
|
||||
if [ ! -f "$CONFIG_FILE" ]; then exit 1; fi
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
@@ -18,12 +18,14 @@ if [ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v4.0.8 核心: 防并发风暴与 60 秒冷却机制] ==================
|
||||
# ==========================================================
|
||||
# [防线 1] 并发风暴熔断机制 (60s 冷却池)
|
||||
# ==========================================================
|
||||
LOCK_FILE="${INSTALL_DIR}/core/.report_lock"
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
LAST_RUN=$(cat "$LOCK_FILE" 2>/dev/null)
|
||||
NOW=$(date +%s)
|
||||
# 校验 LAST_RUN 是否为有效数字,并比对 60 秒冷却期
|
||||
# 严格校验最后执行时间的合法性,防御密集回调
|
||||
if [[ "$LAST_RUN" =~ ^[0-9]+$ ]]; then
|
||||
if [ $((NOW - LAST_RUN)) -lt 60 ]; then
|
||||
echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] [v${AGENT_VERSION:-未知}] [WARN ] [Report ] [SYSTEM] ⚠️ 战报请求过于频繁,触发 60 秒防并发风暴拦截。" >> "${INSTALL_DIR}/logs/sentinel.log"
|
||||
@@ -32,22 +34,23 @@ if [ -f "$LOCK_FILE" ]; then
|
||||
fi
|
||||
fi
|
||||
echo $(date +%s) > "$LOCK_FILE"
|
||||
# ==============================================================================
|
||||
|
||||
# 2. 节点元数据抓取 (v3.2.2 协议自适应与多级容灾版)
|
||||
# [v3.5.2 核心: 引入双轨身份架构]
|
||||
# ==========================================================
|
||||
# 1. 节点元数据与双轨身份解析
|
||||
# ==========================================================
|
||||
if [ -z "$NODE_NAME" ]; then
|
||||
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||
NODE_NAME="$(hostname | cut -c 1-10)-${IP_HASH}"
|
||||
fi
|
||||
NODE_ALIAS="${NODE_ALIAS:-$NODE_NAME}"
|
||||
|
||||
# --- [防线 1: 底层路由锁定与协议自适应] ---
|
||||
# ----------------------------------------------------------
|
||||
# [容灾探针 1] 底层路由锁定与多节点出口 IP 嗅探
|
||||
# ----------------------------------------------------------
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
CURL_BIND_OPT=""
|
||||
@@ -61,37 +64,36 @@ if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# 多节点容灾探测出口 IP (注入协议自适应)
|
||||
# 结合协议自适应进行外部 IP 回显探测
|
||||
CURRENT_IP=$( (curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 api.ip.sb/ip || curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ifconfig.me) 2>/dev/null | tr -d '[:space:]' )
|
||||
# [v3.3.1 修改] 强制兜底:如果外部 API 挂了,优先使用固化的对外公网面孔 (兼容 NAT 机的空 BIND_IP)
|
||||
# 强制兜底逻辑:网络完全阻断时回退使用配置文件锚点
|
||||
[ -z "$CURRENT_IP" ] && CURRENT_IP="${PUBLIC_IP:-$BIND_IP}"
|
||||
|
||||
# 为可能获取到的 IPv6 自动添加方括号护甲
|
||||
# 为 IPv6 环境追加方括号安全护甲
|
||||
[[ "$CURRENT_IP" == *":"* ]] && [[ "$CURRENT_IP" != *"["* ]] && CURRENT_IP="[${CURRENT_IP}]"
|
||||
|
||||
# --- [防线 2: 多级 ISP 容灾探针链路] ---
|
||||
# ----------------------------------------------------------
|
||||
# [容灾探针 2] 多级 ISP 情报探测链路
|
||||
# ----------------------------------------------------------
|
||||
ISP_INFO=""
|
||||
|
||||
# 探针 A: 纯文本 API (免 jq,极速稳定)
|
||||
# 优先级 A: 高吞吐极速纯文本接口
|
||||
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ipinfo.io/org 2>/dev/null)
|
||||
|
||||
# 探针 B: 备用纯文本 API
|
||||
# 优先级 B: 备用纯文本接口
|
||||
if [ -z "$ISP_INFO" ] || [[ "$ISP_INFO" == *"error"* ]]; then
|
||||
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ip-api.com/line/?fields=isp 2>/dev/null)
|
||||
fi
|
||||
|
||||
# 探针 C: 原版的 JSON API (需要 jq 兜底)
|
||||
# 优先级 C: 需构建环境依赖的 JSON 接口
|
||||
if [ -z "$ISP_INFO" ] || [[ "$ISP_INFO" == *"error"* ]]; then
|
||||
if command -v jq &> /dev/null; then
|
||||
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- [防线 3: 数据清洗 (遵循底层共识原则)] ---
|
||||
# 剔除 ipinfo 返回的开头 AS 号 (例如 "AS137535 JT TELECOM" -> "JT TELECOM")
|
||||
# 数据清洗过滤与类型渲染
|
||||
ISP_INFO=$(echo "$ISP_INFO" | sed -E 's/^AS[0-9]+ //')
|
||||
|
||||
# 最终兜底判断
|
||||
[ -z "$ISP_INFO" ] || [ "$ISP_INFO" == "null" ] && ISP_INFO="未知 ISP"
|
||||
|
||||
if [[ "$ISP_INFO" == *"Cloudflare"* ]]; then
|
||||
@@ -100,8 +102,8 @@ else
|
||||
IP_TYPE="$ISP_INFO 🏠"
|
||||
fi
|
||||
|
||||
# 动态国旗 (与中枢全视界雷达对齐)
|
||||
BASE_CC="${REGION_CODE%%-*}" # 兼容提取带有横杠的区域 (如 US-CA 提取为 US)
|
||||
# [全视界旗帜引擎] 动态国旗渲染装配
|
||||
BASE_CC="${REGION_CODE%%-*}"
|
||||
case "$BASE_CC" in
|
||||
US) FLAG="🇺🇸" ;; JP) FLAG="🇯🇵" ;; HK) FLAG="🇭🇰" ;; TW) FLAG="🇹🇼" ;; SG) FLAG="🇸🇬" ;;
|
||||
UK|GB) FLAG="🇬🇧" ;; DE) FLAG="🇩🇪" ;; FR) FLAG="🇫🇷" ;; NL) FLAG="🇳🇱" ;; CA) FLAG="🇨🇦" ;;
|
||||
@@ -113,13 +115,14 @@ case "$BASE_CC" in
|
||||
NZ) FLAG="🇳🇿" ;; AR) FLAG="🇦🇷" ;; CL) FLAG="🇨🇱" ;; MX) FLAG="🇲🇽" ;; IL) FLAG="🇮🇱" ;;
|
||||
SA) FLAG="🇸🇦" ;; EG) FLAG="🇪🇬" ;; NG) FLAG="🇳🇬" ;; KE) FLAG="🇰🇪" ;; RO) FLAG="🇷🇴" ;;
|
||||
BG) FLAG="🇧🇬" ;; CZ) FLAG="🇨🇿" ;; HU) FLAG="🇭🇺" ;; GR) FLAG="🇬🇷" ;; UA) FLAG="🇺🇦" ;;
|
||||
# === 补齐近期扩军的新增战区旗帜 ===
|
||||
MO) FLAG="🇲🇴" ;; KH) FLAG="🇰🇭" ;; MM) FLAG="🇲🇲" ;; LA) FLAG="🇱🇦" ;;
|
||||
MN) FLAG="🇲🇳" ;; NP) FLAG="🇳🇵" ;; BD) FLAG="🇧🇩" ;;
|
||||
*) FLAG="🌐" ;;
|
||||
esac
|
||||
|
||||
# 3. 截取过去 24 小时的日志 (每天72次轮询,保留最新 1000 行足以覆盖单日战报)
|
||||
# ==========================================================
|
||||
# 2. 行为日志萃取与快照分析
|
||||
# ==========================================================
|
||||
LOG_CONTENT=$(tail -n 1000 "$LOG_FILE" 2>/dev/null)
|
||||
|
||||
if [ -z "$LOG_CONTENT" ]; then
|
||||
@@ -131,24 +134,19 @@ if [ -z "$LOG_CONTENT" ]; then
|
||||
🛠️ **建议**: 节点可能刚部署完毕,请在面板手动执行一次养护动作。
|
||||
EOT
|
||||
else
|
||||
# ==========================================
|
||||
# 4. 动态模块数据分析 (核心升级)
|
||||
# ==========================================
|
||||
|
||||
# 提取最近一次运行的快照 (智能识别所属模块)
|
||||
# 抓取末次执行模块的运行态势图
|
||||
LAST_LOG_LINE=$(echo "$LOG_CONTENT" | grep "\[SCORE\]" | tail -n 1)
|
||||
LAST_TIME=$(echo "$LAST_LOG_LINE" | awk '{print $1,$2}' | tr -d '[]')
|
||||
LAST_MOD=$(echo "$LAST_LOG_LINE" | awk '{print $4}' | tr -d '[]')
|
||||
LAST_SCORE=$(echo "$LAST_LOG_LINE" | awk -F'自检结论: ' '{print $2}')
|
||||
|
||||
# 开始组装战报头部
|
||||
MSG="📊 **IP-Sentinel 每日简报 (${FLAG} ${REGION_NAME})**
|
||||
----------------------------
|
||||
📍 **节点名称**: \`${NODE_ALIAS}\`
|
||||
📡 **出口 IP**: \`${CURRENT_IP}\`
|
||||
🛡️ **IP 属性**: ${IP_TYPE}"
|
||||
|
||||
# --- [分析块 1: Google 纠偏模块] ---
|
||||
# 统计 Google 纠偏阵列数据
|
||||
if [ "$ENABLE_GOOGLE" == "true" ]; then
|
||||
GOOGLE_LOGS=$(echo "$LOG_CONTENT" | grep "\[Google")
|
||||
G_TOTAL=$(echo "$GOOGLE_LOGS" | grep "\[START\]" -c)
|
||||
@@ -166,7 +164,7 @@ else
|
||||
✅ 成功: ${G_SUCCESS} | ❌ 送中: ${G_FAILED} | ⚠️ 警告: ${G_WARN}"
|
||||
fi
|
||||
|
||||
# --- [分析块 2: IP 信用净化模块] ---
|
||||
# 统计 Trust 净化阵列数据
|
||||
if [ "$ENABLE_TRUST" == "true" ]; then
|
||||
TRUST_LOGS=$(echo "$LOG_CONTENT" | grep "\[Trust")
|
||||
T_TOTAL=$(echo "$TRUST_LOGS" | grep "\[START\]" -c)
|
||||
@@ -183,7 +181,7 @@ else
|
||||
✅ 成功注入: ${T_SUCCESS} | ❌ 访问受阻: ${T_FAILED}"
|
||||
fi
|
||||
|
||||
# 组装战报尾部 (最近快照)
|
||||
# 追加末次快照
|
||||
MSG="$MSG
|
||||
|
||||
🕒 **最近执行快照: \`${LAST_MOD:-"System"} \`**
|
||||
@@ -192,48 +190,42 @@ else
|
||||
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 5. [核心: OTA 云端版本探针与告警模块]
|
||||
# ==========================================
|
||||
# 从配置文件提取当前本地版本,若无则默认为未知
|
||||
# ==========================================================
|
||||
# 3. 云端版本探针与 OTA 调度模块
|
||||
# ==========================================================
|
||||
LOCAL_VER="${AGENT_VERSION:-未知}"
|
||||
# [时区对齐] 强制获取当前绝对 UTC 时间,作为全局统一的战报落款
|
||||
# [时间线对齐] 强制采用绝对 UTC 时间消除多节点的系统偏差
|
||||
REPORT_UTC_TIME=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
# 极轻量级探针: 抓取 GitHub 云端的 version.txt (超时 3 秒,KV解析法)
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
REMOTE_VER=$(curl -s -m 3 "${REPO_RAW_URL}/version.txt" | grep "^AGENT_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
|
||||
# 构建底部引擎状态块的基础信息
|
||||
MSG="$MSG
|
||||
----------------------------
|
||||
🛡️ **系统引擎状态**
|
||||
⏱️ 战报生成: \`${REPORT_UTC_TIME}\`"
|
||||
|
||||
# 比准逻辑:根据是否为最新版本,动态决定占用的行数
|
||||
# 根据云端版本一致性自动渲染更新提示面板
|
||||
if [ -n "$REMOTE_VER" ]; then
|
||||
if [ "$REMOTE_VER" != "$LOCAL_VER" ]; then
|
||||
# [需要升级] 换行显示旧版本与新版本提示
|
||||
MSG="$MSG
|
||||
当前运行版本: \`v${LOCAL_VER}\`
|
||||
✨ **发现新版本**: \`v${REMOTE_VER}\` (建议更新)
|
||||
💡 *系统提示:检测到新版引擎,建议通过中枢控制台执行 OTA 热更新!*"
|
||||
else
|
||||
# [已是最新] 不换行,直接在尾部追加绿勾
|
||||
MSG="$MSG
|
||||
当前运行版本: \`v${LOCAL_VER}\` (✅已是最新)
|
||||
💡 *IP-Sentinel 持续为您守护节点。*
|
||||
*若本项目对您有帮助,欢迎前往 GitHub 赐予 🌟*"
|
||||
fi
|
||||
else
|
||||
# [抓取失败兜底]
|
||||
MSG="$MSG
|
||||
当前运行版本: \`v${LOCAL_VER}\`
|
||||
💡 *IP-Sentinel 持续为您守护节点。*
|
||||
*若本项目对您有帮助,欢迎前往 GitHub 赐予 🌟*"
|
||||
fi
|
||||
|
||||
# 5. 调用 API 推送 (接入安全网关,挂载交互式控制台按钮)
|
||||
# --- [下发 API 载荷] ---
|
||||
JSON_PAYLOAD=$(jq -n \
|
||||
--arg cid "$CHAT_ID" \
|
||||
--arg txt "$MSG" \
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 - 动态锚点版)
|
||||
# 核心功能: 无痕清理守护进程、定时任务、运行目录及临时缓存
|
||||
# ==========================================================
|
||||
# 脚本名称: uninstall.sh
|
||||
# 核心功能: 无痕追踪溯源、全面抹杀幽灵进程、清空宿主脏数据残留
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
# ----------------------------------------------------------
|
||||
# [权限鉴权] 防止非管理员误触导致组件残留挂起
|
||||
# ----------------------------------------------------------
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 卸载 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
@@ -18,7 +19,6 @@ INSTALL_DIR="/opt/ip_sentinel"
|
||||
echo "========================================================"
|
||||
echo " 🗑️ 准备卸载 IP-Sentinel (边缘节点 Edge Agent)"
|
||||
|
||||
# [核心: 动态读取并播报即将销毁的本地版本号]
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
CURRENT_VER=$(grep "^AGENT_VERSION=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||
@@ -26,11 +26,13 @@ if [ -f "$CONFIG_FILE" ]; then
|
||||
fi
|
||||
echo "========================================================"
|
||||
|
||||
# 1. 停止并删除 Systemd 服务 (适配新架构)
|
||||
# ----------------------------------------------------------
|
||||
# [进程抹杀] 阻塞并卸除底层 Systemd 强绑定服务单元
|
||||
# ----------------------------------------------------------
|
||||
echo "[1/4] 正在停止并删除 Systemd 服务..."
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在抹除 Systemd 服务单元..."
|
||||
# [防死锁与走火修复] 先发送 SIGKILL 瞬间抹杀常驻守护进程,防止卡死或触发遗言
|
||||
# 强制压制守护状态,发送 SIGKILL 剥夺其产生遗言及重启的机会
|
||||
systemctl kill --signal=SIGKILL ip-sentinel-agent-daemon.service >/dev/null 2>&1 || true
|
||||
systemctl disable --now ip-sentinel-runner.service ip-sentinel-runner.timer \
|
||||
ip-sentinel-updater.service ip-sentinel-updater.timer \
|
||||
@@ -49,7 +51,9 @@ else
|
||||
echo "💡 未检测到 Systemd,跳过此步骤..."
|
||||
fi
|
||||
|
||||
# 2. 停止运行中的守护进程与主控模块 (兜底清理老版进程)
|
||||
# ----------------------------------------------------------
|
||||
# [内存清洗] 全面追踪并镇压游离状态的挂起业务逻辑
|
||||
# ----------------------------------------------------------
|
||||
echo "[2/4] 正在终止后台守护进程与所有养护任务..."
|
||||
pkill -9 -f "tg_daemon.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1
|
||||
@@ -62,14 +66,14 @@ pkill -9 -f "mod_google.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "mod_trust.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "sentinel_scheduler.sh" >/dev/null 2>&1
|
||||
|
||||
# 3. 清除系统定时任务 (Cron)
|
||||
# ----------------------------------------------------------
|
||||
# [任务清洗] 基于内存管道流彻底擦除系统底层调度劫持
|
||||
# ----------------------------------------------------------
|
||||
echo "[3/4] 正在清理系统定时任务 (Cron)..."
|
||||
# [终极安全防御] 直接使用管道流过滤并覆盖,不产生任何 /tmp 落地文件,杜绝劫持提权
|
||||
# 通过管道原位清洗避免落地到 /tmp,免疫提权或外部劫持探测
|
||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" | crontab - >/dev/null 2>&1 || true
|
||||
|
||||
# ==========================================
|
||||
# 🛑 [物理抹除] 彻底扫除 Alpine 系统的底层残留与双路径文件
|
||||
# ==========================================
|
||||
# 扫除高受限环境 (如 Alpine) 中的额外触发隐患
|
||||
for CRON_FILE in "/var/spool/cron/crontabs/root" "/etc/crontabs/root"; do
|
||||
if [ -f "$CRON_FILE" ]; then
|
||||
grep -v "ip_sentinel" "$CRON_FILE" > "${CRON_FILE}.tmp" 2>/dev/null || true
|
||||
@@ -77,16 +81,16 @@ for CRON_FILE in "/var/spool/cron/crontabs/root" "/etc/crontabs/root"; do
|
||||
rm -f "${CRON_FILE}.tmp" 2>/dev/null
|
||||
fi
|
||||
done
|
||||
# 清理 OpenRC 开机启动项
|
||||
rm -f /etc/local.d/ip_sentinel.start 2>/dev/null
|
||||
rm -f /etc/local.d/ip_sentinel_scheduler.start 2>/dev/null
|
||||
|
||||
# 清理极端环境写在 /etc/profile 里的兜底启动项
|
||||
if grep -q "sentinel_scheduler.sh" /etc/profile 2>/dev/null; then
|
||||
sed -i '/sentinel_scheduler\.sh/d' /etc/profile 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 4. 删除所有文件、日志与临时缓存
|
||||
# ----------------------------------------------------------
|
||||
# [物理销毁] 抹杀持久化特征,销毁系统沙盒痕迹
|
||||
# ----------------------------------------------------------
|
||||
echo "[4/4] 正在抹除核心程序、配置文件与系统痕迹..."
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
rm -rf "$INSTALL_DIR"
|
||||
|
||||
@@ -1,43 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: updater.sh (IP-Sentinel 养料注入与分频调度中枢 - 动态锚点版)
|
||||
# 核心功能: 静默更新热数据/LBS、指纹库错峰调度、强制出站死锁、版本无缝继承
|
||||
# 脚本名称: updater.sh
|
||||
# 核心功能: 指纹防惊群错峰轮换、LBS 底层静默分发、深度探针签名防伪
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
UA_TIME_FILE="${INSTALL_DIR}/core/.ua_last_update"
|
||||
|
||||
# GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为开发地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# 1. 加载本地冷数据配置
|
||||
# --- [底层数据链装载] ---
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
exit 1
|
||||
fi
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# 2. 全局日志写入函数 (v3.4.0 引入版本探针)
|
||||
# --- [全局态势日志系统] ---
|
||||
log() {
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 保证日志目录存在
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识
|
||||
local core_msg=$(printf "[v%-5s] [%-5s] [%-7s] [%s] %s" "$local_ver" "$2" "$1" "$REGION_CODE" "$3")
|
||||
# [时区对齐] 强制无视本地时区,以绝对 UTC 时间写入日志
|
||||
# 强制剔除节点宿主机本地时差,严格对齐指挥部 UTC 基准
|
||||
echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] $core_msg" >> "$LOG_FILE"
|
||||
|
||||
# 强制推送到 Systemd Journal (如果系统支持)
|
||||
if command -v logger >/dev/null 2>&1; then
|
||||
logger -t ip-sentinel "$core_msg"
|
||||
else
|
||||
# 降级输出到 stdout,让 Systemd 捕获
|
||||
echo "$core_msg"
|
||||
fi
|
||||
}
|
||||
@@ -45,16 +37,12 @@ log() {
|
||||
log "Updater" "INFO " "========== 触发后台静默 OTA 热数据更新 =========="
|
||||
|
||||
# ==========================================================
|
||||
# 🛡️ 终极护城河:构建强锚定出站的 curl 请求引擎
|
||||
# [网络路由锁定] 构建强锚定出站屏障,彻底阻断跨协议溢出逃逸
|
||||
# ==========================================================
|
||||
# 基础参数:跟随 install.sh 锁定的协议偏好 (4 或 6)
|
||||
CURL_CMD="curl -${IP_PREF:-4} -sL"
|
||||
|
||||
# 【防坑核心】如果用户配置了死锁锚点,必须强制绑定网卡,杜绝流量溢出!
|
||||
if [ -n "$BIND_IP" ]; then
|
||||
# curl 的 --interface 参数不支持带方括号的 IPv6 地址,必须强行脱壳
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
# [v3.6.3 容错层补丁] 探测网卡存活状态,防止 IP 漂移导致永久断网
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log "Updater" "WARN " "检测到绑定的出口 IP ($RAW_BIND_IP) 已丢失,自动退回默认路由!"
|
||||
else
|
||||
@@ -63,28 +51,24 @@ if [ -n "$BIND_IP" ]; then
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 3. 容灾机制拉取 UA 指纹池 (V3.3.0 引入 30 天错峰防惊群逻辑)
|
||||
# [指纹池滚动更新] 错峰调度防惊群风暴算法
|
||||
# 强制设定 30 天超长冷静期以规避 Github 限流与特征同构
|
||||
# ==========================================================
|
||||
NOW=$(date +%s)
|
||||
LAST_UPDATE=0
|
||||
|
||||
# 读取上一次更新的时间戳
|
||||
if [ -f "$UA_TIME_FILE" ]; then
|
||||
# tr -d 清除可能存在的换行或回车符,防止算术崩溃
|
||||
LAST_UPDATE=$(cat "$UA_TIME_FILE" | tr -d '\r\n')
|
||||
fi
|
||||
|
||||
# 校验数据合法性,防崩溃
|
||||
if ! [[ "$LAST_UPDATE" =~ ^[0-9]+$ ]]; then
|
||||
LAST_UPDATE=0
|
||||
fi
|
||||
|
||||
DIFF=$((NOW - LAST_UPDATE))
|
||||
|
||||
# 距离上次拉取超过 30 天 (2592000 秒),才执行下载
|
||||
if [ "$DIFF" -ge 2592000 ] || [ "$LAST_UPDATE" -eq 0 ]; then
|
||||
TMP_UA="/tmp/ip_sentinel_ua.txt"
|
||||
# 使用重装升级后的 CURL_CMD
|
||||
$CURL_CMD "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
|
||||
|
||||
if [ -s "$TMP_UA" ]; then
|
||||
@@ -100,9 +84,9 @@ else
|
||||
log "Updater" "INFO " "⏳ 设备指纹池处于 30 天静默期 (剩余约 ${DAYS_LEFT} 天),跳过拉取"
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 4. 容灾机制拉取当地最新搜索词库 (每日高频拉取,保证活体新鲜度)
|
||||
# ==========================================================
|
||||
# ----------------------------------------------------------
|
||||
# [态势感知热更] 动态注入本土高权热搜及战区 LBS 规则
|
||||
# ----------------------------------------------------------
|
||||
TMP_KW="/tmp/ip_sentinel_kw.txt"
|
||||
$CURL_CMD "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "$TMP_KW"
|
||||
|
||||
@@ -114,9 +98,6 @@ else
|
||||
rm -f "$TMP_KW"
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 5. 自适应拉取本地 LBS 专属 JSON 规则库 (每日同步)
|
||||
# ==========================================================
|
||||
REGION_JSON_FILE=$(find "${INSTALL_DIR}/data/regions" -name "*.json" 2>/dev/null | head -n 1)
|
||||
|
||||
if [ -n "$REGION_JSON_FILE" ] && [ -f "$REGION_JSON_FILE" ]; then
|
||||
@@ -135,12 +116,12 @@ if [ -n "$REGION_JSON_FILE" ] && [ -f "$REGION_JSON_FILE" ]; then
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 5.5. 容灾更新深海声呐底层探针 (彻底消除第三方 RCE 依赖)
|
||||
# [容灾校验] 外置供应链投毒防线与底层签名嗅探
|
||||
# ==========================================================
|
||||
TMP_PROBE="/tmp/ip_sentinel_probe.sh"
|
||||
$CURL_CMD "https://raw.githubusercontent.com/xykt/IPQuality/main/ip.sh" -o "$TMP_PROBE"
|
||||
|
||||
# 🛡️ 供应链防毒:验证脚本内是否包含原作者特有签名,防止被墙重定向为 HTML
|
||||
# 严格过滤无标识或 HTML 劫持阻断页面,免疫上游源的降级攻击
|
||||
if [ -s "$TMP_PROBE" ] && grep -q "xykt" "$TMP_PROBE" 2>/dev/null; then
|
||||
mv "$TMP_PROBE" "${INSTALL_DIR}/core/ip_probe.sh"
|
||||
chmod +x "${INSTALL_DIR}/core/ip_probe.sh"
|
||||
@@ -151,7 +132,7 @@ else
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 6. 日志防满瘦身机制 (保留最近 2000 行)
|
||||
# [空间瘦身] 长效健康清理与爆栈预防机制
|
||||
# ==========================================================
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
tail -n 2000 "$LOG_FILE" > "${LOG_FILE}.tmp"
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本 - 动态锚点版)
|
||||
# 核心功能: 部署/卸载调度中枢、SQLite 资产管理、平滑热更新引擎
|
||||
# 脚本名称: install_master.sh
|
||||
# 核心功能: Master 环境探底预装、无感 OTA 置换、持久化 SQLite 建库
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
# ----------------------------------------------------------
|
||||
# [权限鉴权] 阻断非预期非最高权限的部署操作
|
||||
# ----------------------------------------------------------
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 部署 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 🟢 [防劫持沙盒] 引入司令部专属随机安全工作区
|
||||
SECURE_TMP=$(mktemp -d /tmp/ips_master_install.XXXXXX)
|
||||
trap 'rm -rf "$SECURE_TMP"' EXIT HUP INT QUIT TERM
|
||||
|
||||
# ==========================================================
|
||||
# 🔍 核心探针: 系统环境与虚拟化检测 (v4.0.13 架构升级)
|
||||
# ==========================================================
|
||||
# ----------------------------------------------------------
|
||||
# [环境预检] 中枢架构探测与系统级诊断
|
||||
# ----------------------------------------------------------
|
||||
is_systemd() {
|
||||
command -v systemctl >/dev/null 2>&1 || return 1
|
||||
[ -d /run/systemd/system ] || return 1
|
||||
@@ -61,27 +60,21 @@ fi
|
||||
echo -e "======================================\n"
|
||||
sleep 1
|
||||
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
|
||||
# [核心: 动态提取 Master 专属版本锚点 (KV 解析法)]
|
||||
# 通过 grep 定位 MASTER_VERSION 行,再通过 cut 提取等号右侧的值
|
||||
# [红警修复] 增加 -f 与 --retry 护甲,防御 404 吞雷 Bug
|
||||
# [链路容灾] 双栈冗余防抖抓取,确立本地态势版本号
|
||||
TARGET_VERSION=$( (curl -fsSL --connect-timeout 5 --retry 2 "${REPO_RAW_URL}/version.txt" || curl -4 -fsSL --connect-timeout 5 --retry 2 "${REPO_RAW_URL}/version.txt") 2>/dev/null | grep "^MASTER_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
|
||||
# 🛡️ 兜底防线:如果网络波动拉取失败,启用内置的最新兜底版本
|
||||
TARGET_VERSION=${TARGET_VERSION:-"4.0.7"}
|
||||
|
||||
MASTER_DIR="/opt/ip_sentinel_master"
|
||||
DB_FILE="${MASTER_DIR}/sentinel.db"
|
||||
|
||||
echo "========================================================"
|
||||
# [修改] 将欢迎语改为更通用的文案,因为现在不仅能部署,还能卸载
|
||||
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢) v${TARGET_VERSION}"
|
||||
echo "========================================================"
|
||||
|
||||
# ==========================================================
|
||||
# [v3.6.1 核心] 拦截司令部静默 OTA 升级模式 (强行接管执行流)
|
||||
# [指令接管] 云端 OTA 重构流引擎拦截
|
||||
# ==========================================================
|
||||
if [ "$SILENT_MASTER_OTA" == "true" ]; then
|
||||
echo -e "\n⏳ [OTA] 中枢重构指令已确认,正在剥离控制台交互..."
|
||||
@@ -89,11 +82,9 @@ if [ "$SILENT_MASTER_OTA" == "true" ]; then
|
||||
UPGRADE_MODE="true"
|
||||
KEEP_DB="true"
|
||||
|
||||
# 汲取原配置进入内存
|
||||
if [ -f "${MASTER_DIR}/master.conf" ]; then
|
||||
source "${MASTER_DIR}/master.conf"
|
||||
|
||||
# 同步新版本号至配置文件
|
||||
if grep -q "^MASTER_VERSION=" "${MASTER_DIR}/master.conf"; then
|
||||
sed -i "s/^MASTER_VERSION=.*/MASTER_VERSION=\"$TARGET_VERSION\"/" "${MASTER_DIR}/master.conf"
|
||||
else
|
||||
@@ -102,13 +93,11 @@ if [ "$SILENT_MASTER_OTA" == "true" ]; then
|
||||
fi
|
||||
echo -e "\033[32m✅ 已激活 [中枢静默重构模式],即将无损覆写内核...\033[0m"
|
||||
else
|
||||
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
|
||||
echo -e "\n请选择操作:"
|
||||
echo " 1) 🚀 部署 Master 控制中枢"
|
||||
echo " 2) 🗑️ 一键卸载 Master 中枢"
|
||||
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
|
||||
|
||||
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定被误删档
|
||||
ACTION_CHOICE=${ACTION_CHOICE:-1}
|
||||
|
||||
if [ "$ACTION_CHOICE" == "2" ]; then
|
||||
@@ -120,7 +109,7 @@ else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
# [态势传承] 平滑接管探查并保护库文件
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_DB="true"
|
||||
|
||||
@@ -149,7 +138,9 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 优化: 数据纯净度清理与保护] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [环境清洗] 执行装配前系统清理动作
|
||||
# ----------------------------------------------------------
|
||||
echo -e "\n⏳ 正在验证本地环境与数据..."
|
||||
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
@@ -159,42 +150,36 @@ if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
else
|
||||
echo -e "📦 历史节点数据库 (SQLite) 已绝密保留。"
|
||||
fi
|
||||
# [防砖修复] 移除过早的旧进程抹杀与脚本物理删除,防止拉取失败导致司令部变砖失联
|
||||
else
|
||||
# 焦土政策:如果不是升级模式,直接扬了整个司令部目录
|
||||
rm -rf "$MASTER_DIR" 2>/dev/null
|
||||
fi
|
||||
# =======================================================================
|
||||
|
||||
# 1. 依赖检查与智能安装 (v3.6.0 兼容性与优雅性升级)
|
||||
# ==========================================================
|
||||
# [依赖装甲] 多分支环境极简包管理器适配策略
|
||||
# ==========================================================
|
||||
echo -e "\n[1/4] 正在探测核心依赖 (curl, jq, sqlite3, crontab, pgrep, openssl)..."
|
||||
|
||||
REQUIRED_CMDS=("curl" "jq" "sqlite3" "crontab" "pgrep" "openssl")
|
||||
MISSING_CMDS=()
|
||||
|
||||
# 基础探测:预检查缺失的命令
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
MISSING_CMDS+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
# 如果有缺失,才执行包管理器拉取逻辑
|
||||
if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
echo "⏳ 发现缺失依赖: ${MISSING_CMDS[*]},正在尝试自动补齐..."
|
||||
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
# [v3.6.3 抽脂级优化] 注入 --no-install-recommends 拒绝捆绑销售
|
||||
apt-get install -y --no-install-recommends curl jq sqlite3 cron procps openssl >/dev/null 2>&1
|
||||
systemctl enable cron >/dev/null 2>&1 && systemctl start cron >/dev/null 2>&1
|
||||
elif command -v yum >/dev/null 2>&1 || command -v dnf >/dev/null 2>&1 || command -v microdnf >/dev/null 2>&1; then
|
||||
# RHEL / CentOS / AlmaLinux / Rocky 系列 (含极简 microdnf 镜像)
|
||||
PKG_MGR="yum"
|
||||
OPT_ARGS=""
|
||||
if command -v dnf >/dev/null 2>&1; then
|
||||
PKG_MGR="dnf"
|
||||
# [v3.6.3 抽脂级优化] 强行关闭 DNF 的弱依赖拉取
|
||||
OPT_ARGS="--setopt=install_weak_deps=False"
|
||||
elif command -v microdnf >/dev/null 2>&1; then
|
||||
PKG_MGR="microdnf"
|
||||
@@ -204,12 +189,10 @@ if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
$PKG_MGR install -y epel-release >/dev/null 2>&1 || true
|
||||
|
||||
echo -e "\033[90m (正在拉取核心组件...)\033[0m"
|
||||
# [核心修复] 移除屏蔽符,暴露真实执行过程
|
||||
$PKG_MGR install -y $OPT_ARGS curl jq sqlite cronie procps-ng openssl
|
||||
systemctl enable crond >/dev/null 2>&1 && systemctl start crond >/dev/null 2>&1
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
echo "Alpine 探测到系统类型为 Alpine Linux,正在执行轻量级安装..."
|
||||
# [修复] 优先尝试 cronie,若失败则回退至系统内置 cron,彻底避免单点依赖拖垮全局
|
||||
apk add --no-cache curl jq sqlite cronie procps bash openssl || apk add --no-cache curl jq sqlite procps bash openssl
|
||||
mkdir -p /var/spool/cron/crontabs
|
||||
rc-update add crond default >/dev/null 2>&1
|
||||
@@ -228,7 +211,6 @@ if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 安装后二次复检
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo -e "\033[31m❌ 致命错误:核心命令 '$cmd' 仍未找到!\033[0m"
|
||||
@@ -242,14 +224,12 @@ echo -e "\033[32m✅ 基础环境检测通过。\033[0m"
|
||||
mkdir -p "$MASTER_DIR"
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 如果是全新部署,才询问 Token 并写入配置
|
||||
# [配置总线] 构建交互与策略文件固化
|
||||
# ==========================================================
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
# 2. 交互配置机器人
|
||||
echo -e "\n[2/4] 配置控制中枢机器人:"
|
||||
read -p "请输入 Telegram Bot Token: " TG_TOKEN
|
||||
|
||||
# [v3.6.0 新增] 官方网关模式选项 (用于屏蔽全局 OTA 按钮)
|
||||
echo -e "\n请选择您的部署环境身份:"
|
||||
echo " 1) 🛡️ 私有独立中枢 (默认推荐,保留完整 OTA 遥控权限)"
|
||||
echo " 2) ☁️ 官方公共网关 (面向大众服务,将强制物理隐藏全局 OTA 按钮防滥用)"
|
||||
@@ -262,7 +242,6 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
IS_OFFICIAL_GATEWAY="true"
|
||||
echo -e "\033[33m⚠️ 已开启官方公共网关模式,全舰队与司令部的 OTA 将被强制屏蔽。\033[0m"
|
||||
else
|
||||
# [v3.6.1] 私有模式开放中枢 OTA 授权向导
|
||||
echo -e "\n[2.1/4] 司令部自我进化授权"
|
||||
echo -e "💡 开启后,您可以在 TG 菜单一键将中枢核心系统热更新至最新版本。"
|
||||
read -p "是否允许司令部接收 OTA 重构指令?(y/n, 默认y): " M_OTA_CHOICE
|
||||
@@ -281,14 +260,11 @@ MASTER_VERSION="$TARGET_VERSION"
|
||||
TG_TOKEN="$TG_TOKEN"
|
||||
DB_FILE="$DB_FILE"
|
||||
MASTER_DIR="$MASTER_DIR"
|
||||
# [v3.6.0 核心] 官方网关 UI 熔断标识
|
||||
IS_OFFICIAL_GATEWAY="$IS_OFFICIAL_GATEWAY"
|
||||
# [v3.6.1 新增] 司令部自身 OTA 授权标识
|
||||
ENABLE_MASTER_OTA="$ENABLE_MASTER_OTA"
|
||||
EOF
|
||||
fi
|
||||
|
||||
# [v3.6.1 热修复] 老司令部平滑升级时,自动补齐缺失字段
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
if ! grep -q "^IS_OFFICIAL_GATEWAY=" "${MASTER_DIR}/master.conf"; then
|
||||
echo "IS_OFFICIAL_GATEWAY=\"false\"" >> "${MASTER_DIR}/master.conf"
|
||||
@@ -297,9 +273,10 @@ if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo "ENABLE_MASTER_OTA=\"false\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
fi
|
||||
# 🛑 拦截块结束
|
||||
|
||||
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下由 tg_master.sh 负责热修补)
|
||||
# ----------------------------------------------------------
|
||||
# [数据存储] 初始化 SQLite 表结构基线
|
||||
# ----------------------------------------------------------
|
||||
echo -e "\n[3/4] 正在初始化 SQLite 数据库表结构..."
|
||||
sqlite3 "$DB_FILE" <<EOF
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
@@ -316,7 +293,6 @@ CREATE TABLE IF NOT EXISTS nodes (
|
||||
PRIMARY KEY(chat_id, node_name)
|
||||
);
|
||||
|
||||
-- [v4.0.0 新增, v4.0.2 扩容] 核心情报表:记录历史 IP 质量数据,用于绘制趋势图
|
||||
CREATE TABLE IF NOT EXISTS ip_trend_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_name TEXT,
|
||||
@@ -329,18 +305,17 @@ CREATE TABLE IF NOT EXISTS ip_trend_log (
|
||||
EOF
|
||||
echo "✅ 数据库创建成功: $DB_FILE"
|
||||
|
||||
# ================== [v3.0.3 变更: 敏感文件权限收敛] ==================
|
||||
chmod 600 "${MASTER_DIR}/master.conf"
|
||||
chmod 600 "$DB_FILE"
|
||||
# ====================================================================
|
||||
|
||||
# 4. 拉取核心调度代码并执行原子化交接
|
||||
# ==========================================================
|
||||
# [原子交接] 防变砖双缓冲下载,确保执行层无断层覆写
|
||||
# ==========================================================
|
||||
echo -e "\n[4/4] 正在拉取新版司令部核心引擎..."
|
||||
|
||||
TMP_MASTER="${SECURE_TMP}/tg_master.sh"
|
||||
curl -fsSL --connect-timeout 10 --retry 3 "${REPO_RAW_URL}/master/tg_master.sh" -o "$TMP_MASTER"
|
||||
|
||||
# 🛡️ 防砖终极校验
|
||||
if [ ! -s "$TMP_MASTER" ]; then
|
||||
echo -e "\033[31m❌ 致命错误:中枢核心代码拉取失败!网络阻断或 GitHub Raw 异常。\033[0m"
|
||||
echo "🛡️ 防砖机制触发:已中止覆盖,旧版司令部仍在安全运行中。"
|
||||
@@ -348,8 +323,6 @@ if [ ! -s "$TMP_MASTER" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 🟢 [原子化交接核心]: 校验完美通过,新代码已备妥!
|
||||
# 以雷霆手段抹杀旧版调度进程,杜绝文件覆写时的并发错乱
|
||||
echo "⏳ 新引擎校验通过,正在抹杀旧版守护进程..."
|
||||
if is_systemd; then
|
||||
systemctl kill --signal=SIGKILL ip-sentinel-master.service >/dev/null 2>&1 || true
|
||||
@@ -357,7 +330,6 @@ if is_systemd; then
|
||||
fi
|
||||
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||
|
||||
# 执行物理替换
|
||||
mv "$TMP_MASTER" "${MASTER_DIR}/tg_master.sh"
|
||||
chmod +x "${MASTER_DIR}/tg_master.sh"
|
||||
|
||||
@@ -389,7 +361,6 @@ EOF
|
||||
systemctl enable --now ip-sentinel-master.service
|
||||
systemctl restart ip-sentinel-master.service
|
||||
|
||||
# 清理可能残留的历史 Cron (无落地内存流防劫持)
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" | crontab - >/dev/null 2>&1 || true
|
||||
else
|
||||
echo "💡 未检测到 Systemd,回退到 Cron 看门狗调度模式..."
|
||||
@@ -400,13 +371,15 @@ else
|
||||
pgrep -f tg_master.sh >/dev/null || { nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 & disown 2>/dev/null; }
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 优化 & v3.6.1 OTA捷报: 战报文案分流] ==================
|
||||
# ==========================================================
|
||||
# [状态汇报] 根据操作场景分发回执
|
||||
# ==========================================================
|
||||
echo "========================================================"
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo "🎉 Master 控制中枢平滑热更新完成!"
|
||||
echo "🤖 新版中枢引擎已接管数据库,继续等待边缘节点汇报。"
|
||||
|
||||
# [v3.6.1 核心] 静默 OTA 完成后,由幽灵进程主动向指挥官发送捷报
|
||||
# 幽灵态静默 OTA 完毕后执行回叫汇报
|
||||
if [ "$SILENT_MASTER_OTA" == "true" ] && [ -n "$OTA_CHAT_ID" ] && [ -n "$TG_TOKEN" ]; then
|
||||
echo -e "\n📡 正在向指挥官发送司令部重构捷报..."
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
@@ -421,10 +394,7 @@ else
|
||||
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
||||
fi
|
||||
echo "========================================================"
|
||||
# =================================================================
|
||||
|
||||
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
|
||||
# [修复] 仅在全新部署时触发统计,司令部热重载时绝对不触发
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名,不收集IP)..."
|
||||
MASTER_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/master" || echo "")
|
||||
@@ -436,9 +406,8 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [新增: 安装成功高光时刻 Star 引导] ==================
|
||||
echo -e "\n========================================================"
|
||||
echo -e "⭐ \033[33m开源不易,如果 IP-Sentinel 极大简化了您的多节点管理,请赐予我们一枚星标!\033[0m"
|
||||
echo -e "💡 \033[32m您的每一颗 Star 都是我们持续迭代架构、开发 Web 视窗化控制台的动力源泉。\033[0m"
|
||||
echo -e "👉 \033[36m\033[4m\033]8;;https://github.com/hotyue/IP-Sentinel\033\\点击此处直达 GitHub 仓库点亮 Star 🌟\033[0m\033]8;;\033\\"
|
||||
echo -e "========================================================\n"
|
||||
echo -e "========================================================\n"
|
||||
@@ -1,29 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: tg_master.sh (Master 端调度枢纽 - 动态锚点版)
|
||||
# 核心功能: 监听 TG、操作 SQLite、Webhook 精准调度、403权限拦截、僵尸节点清理
|
||||
# 脚本名称: tg_master.sh
|
||||
# 核心功能: 监听并处理全局指令回调,安全下发 OTA、Webhook、节点改名及僵尸节点清洗
|
||||
# ==========================================================
|
||||
|
||||
CONF="/opt/ip_sentinel_master/master.conf"
|
||||
[ ! -f "$CONF" ] && exit 1
|
||||
source "$CONF"
|
||||
|
||||
# [核心: 运行态版本继承与云通信地址]
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
|
||||
# MASTER_VERSION 已经在上方的 source "$CONF" 中被载入
|
||||
# 如果本地极度陈旧没有该变量,才给定一个基础兜底值,避免变量为空导致崩溃
|
||||
MASTER_VERSION=${MASTER_VERSION:-"3.5.0"}
|
||||
|
||||
OFFSET_FILE="${MASTER_DIR}/.tg_offset"
|
||||
[[ -f $OFFSET_FILE ]] || echo "0" > $OFFSET_FILE
|
||||
|
||||
# --- 工具函数 ---
|
||||
# ================== [v4.0.3 核心: 全球全能旗帜渲染引擎] ==================
|
||||
# ==========================================================
|
||||
# 1. 核心工具组件
|
||||
# ==========================================================
|
||||
|
||||
# [全局旗帜渲染引擎] 基于 ISO 代码动态匹配地区国旗
|
||||
get_flag() {
|
||||
local region=$(echo "$1" | tr 'a-z' 'A-Z')
|
||||
local base_cc="${region%%-*}" # 提取横杠前的主国家代码 (例如 US-TX 提取为 US)
|
||||
local base_cc="${region%%-*}"
|
||||
local flag="🌐"
|
||||
case "$base_cc" in
|
||||
US) flag="🇺🇸" ;; JP) flag="🇯🇵" ;; HK) flag="🇭🇰" ;; TW) flag="🇹🇼" ;; SG) flag="🇸🇬" ;;
|
||||
@@ -36,7 +35,6 @@ get_flag() {
|
||||
NZ) flag="🇳🇿" ;; AR) flag="🇦🇷" ;; CL) flag="🇨🇱" ;; MX) flag="🇲🇽" ;; IL) flag="🇮🇱" ;;
|
||||
SA) flag="🇸🇦" ;; EG) flag="🇪🇬" ;; NG) flag="🇳🇬" ;; KE) flag="🇰🇪" ;; RO) flag="🇷🇴" ;;
|
||||
BG) flag="🇧🇬" ;; CZ) flag="🇨🇿" ;; HU) flag="🇭🇺" ;; GR) flag="🇬🇷" ;; UA) flag="🇺🇦" ;;
|
||||
# === 补齐近期扩军的新增战区旗帜 ===
|
||||
MO) flag="🇲🇴" ;; KH) flag="🇰🇭" ;; MM) flag="🇲🇲" ;; LA) flag="🇱🇦" ;;
|
||||
MN) flag="🇲🇳" ;; NP) flag="🇳🇵" ;; BD) flag="🇧🇩" ;;
|
||||
esac
|
||||
@@ -54,58 +52,49 @@ send_msg() {
|
||||
-d "chat_id=$1" -d "text=$2" -d "parse_mode=Markdown" > /dev/null
|
||||
}
|
||||
|
||||
# ================== [v3.0.1 新增: 消息原位刷新函数] ==================
|
||||
edit_msg() {
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
-d "chat_id=$1" -d "message_id=$2" -d "text=$3" -d "parse_mode=Markdown" > /dev/null
|
||||
}
|
||||
|
||||
# [v3.5.3 新增: 支持内联键盘的原位 UI 重绘函数]
|
||||
edit_ui() {
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"$1\",\"message_id\":\"$2\",\"text\":\"$3\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"inline_keyboard\":$4}}" > /dev/null
|
||||
}
|
||||
|
||||
# 数据库执行函数 (v3.6.3 终极静默版: 动用 .timeout 点命令防泄露)
|
||||
# [SQLite 终极并发架构] 激活高并发 WAL 引擎防锁库,并设置安全锁时延
|
||||
db_exec() {
|
||||
printf ".timeout 5000\n%s\n" "$1" | sqlite3 "$DB_FILE"
|
||||
}
|
||||
|
||||
# ================== [v3.0.4 核心: 动态 HMAC 签名生成器] ==================
|
||||
# 用法: generate_signed_url <IP> <PORT> <PATH>
|
||||
# [HMAC 动态签名引擎] 下发指令挂载带有时效性的哈希签名,防止重放与中间人篡改
|
||||
generate_signed_url() {
|
||||
local target_ip=$1
|
||||
local target_port=$2
|
||||
local action_path=$3
|
||||
local current_t=$(date +%s)
|
||||
|
||||
# 构建加密载荷: "路径:时间戳"
|
||||
local payload="${action_path}:${current_t}"
|
||||
|
||||
# 使用 CHAT_ID 作为密钥,生成 SHA256 HMAC 签名
|
||||
local signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$CHAT_ID" | awk '{print $NF}')
|
||||
|
||||
# 返回最终带签名的 URL
|
||||
echo "https://${target_ip}:${target_port}${action_path}?t=${current_t}&sign=${signature}"
|
||||
}
|
||||
# ========================================================================
|
||||
|
||||
# ================== [v3.6.3 核心: 激活 SQLite 高并发 WAL 引擎] ==================
|
||||
# ==========================================================
|
||||
# 2. 数据库热升级自愈系统
|
||||
# ==========================================================
|
||||
db_exec "PRAGMA journal_mode=WAL;" > /dev/null 2>&1
|
||||
db_exec "PRAGMA synchronous=NORMAL;" > /dev/null 2>&1
|
||||
# ==============================================================================
|
||||
|
||||
# ================== [v3.1.3-v3.6.0 核心: 数据库结构无损热升级] ==================
|
||||
# 自动探测并增加缺失字段,屏蔽已存在的报错,保护老节点数据
|
||||
# 自动探测并动态扩展节点基础表结构,屏蔽已存在的报错
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN region TEXT DEFAULT 'UNKNOWN';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN node_alias TEXT;" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_google TEXT DEFAULT 'true';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_trust TEXT DEFAULT 'true';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_ota TEXT DEFAULT 'false';" 2>/dev/null
|
||||
# ========================================================================
|
||||
|
||||
# ================== [v4.0.0/v4.0.2 核心: 增加 IP 质量趋势追踪表] ==================
|
||||
# 构建与动态扩展 IP 质量历史趋势库
|
||||
db_exec "CREATE TABLE IF NOT EXISTS ip_trend_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_name TEXT,
|
||||
@@ -113,12 +102,12 @@ db_exec "CREATE TABLE IF NOT EXISTS ip_trend_log (
|
||||
scam_score INTEGER,
|
||||
nf_status TEXT
|
||||
);" 2>/dev/null
|
||||
# [v4.0.2 热更新] 动态扩容 谷歌 与 ChatGPT 状态追踪字段
|
||||
db_exec "ALTER TABLE ip_trend_log ADD COLUMN goog_status TEXT DEFAULT 'Unknown';" 2>/dev/null
|
||||
db_exec "ALTER TABLE ip_trend_log ADD COLUMN gpt_status TEXT DEFAULT 'Unknown';" 2>/dev/null
|
||||
# ========================================================================
|
||||
|
||||
# --- 核心轮询循环 ---
|
||||
# ==========================================================
|
||||
# 3. 核心长轮询调度器
|
||||
# ==========================================================
|
||||
while true; do
|
||||
OFFSET=$(cat $OFFSET_FILE)
|
||||
UPDATES=$(curl -s --connect-timeout 5 -m 35 "https://api.telegram.org/bot${TG_TOKEN}/getUpdates?offset=${OFFSET}&timeout=30")
|
||||
@@ -133,18 +122,18 @@ while true; do
|
||||
CHAT_ID=$(echo "$UPDATE" | jq -r '.message.chat.id // .callback_query.message.chat.id')
|
||||
TEXT=$(echo "$UPDATE" | jq -r '.message.text // .callback_query.data')
|
||||
|
||||
# ================== [基础消息解析提取提前] ==================
|
||||
# [致命 Bug 修复] 必须在 svq 入库判断前提取这俩变量,否则入库后无法重绘 UI
|
||||
# [UI 状态机] 提前提取交互回调 ID,确保后续 UI 重绘正常流转
|
||||
CB_ID=$(echo "$UPDATE" | jq -r '.callback_query.id // empty')
|
||||
MSG_ID=$(echo "$UPDATE" | jq -r '.callback_query.message.message_id // empty')
|
||||
|
||||
# ================== [v4.0.2 核心: 态势感知按钮一键入库] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [业务流 A] 深海声呐态势感知一键入库模块
|
||||
# ----------------------------------------------------------
|
||||
if [[ "$TEXT" == "svq|"* ]]; then
|
||||
# 格式: svq|NODE_NAME|SCORE|GOOG|NF|GPT
|
||||
IFS='|' read -r MAGIC RAW_NODE_ID RAW_SCORE RAW_GOOG_ST RAW_NF_ST RAW_GPT_ST <<< "$TEXT"
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# 🛡️ 终极防御:彻底清洗,封死一切 SQL 注入通道
|
||||
# [安全防御] 严格正则清洗,封死所有 SQL 注入通道
|
||||
NODE_ID=$(echo "$RAW_NODE_ID" | tr -cd 'a-zA-Z0-9_.-')
|
||||
SCORE=$(echo "$RAW_SCORE" | tr -cd '0-9')
|
||||
GOOG_ST=$(echo "$RAW_GOOG_ST" | tr -d '"'\''\`\$\|&;<>\n\r')
|
||||
@@ -152,10 +141,8 @@ while true; do
|
||||
GPT_ST=$(echo "$RAW_GPT_ST" | tr -d '"'\''\`\$\|&;<>\n\r')
|
||||
|
||||
if [ -n "$NODE_ID" ] && [ -n "$SCORE" ]; then
|
||||
# 1. 写入 SQLite
|
||||
db_exec "INSERT INTO ip_trend_log (node_name, scam_score, goog_status, nf_status, gpt_status) VALUES ('$NODE_ID', '$SCORE', '$GOOG_ST', '$NF_ST', '$GPT_ST');"
|
||||
|
||||
# [体验优化] 弹出顶部 Toast 气泡,提示入库成功
|
||||
if [ -n "$CB_ID" ]; then
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/answerCallbackQuery" \
|
||||
-d "callback_query_id=${CB_ID}" \
|
||||
@@ -163,14 +150,13 @@ while true; do
|
||||
-d "show_alert=false" > /dev/null
|
||||
fi
|
||||
|
||||
# 2. 无损修改原消息:移除入库按钮展示绿勾状态,并保留返回控制台按钮 (体验优化)
|
||||
# 无损修改原消息,擦除入库按钮保留逃生舱
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageReplyMarkup" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"${CHAT_ID}\",\"message_id\":\"${MSG_ID}\",\"reply_markup\":{\"inline_keyboard\":[[{\"text\":\"✅ 此报告已存档\",\"callback_data\":\"ignore\"}],[{\"text\":\"⚙️ 调出该节点控制台\",\"callback_data\":\"manage:${NODE_ID}\"}]]}}" > /dev/null
|
||||
fi
|
||||
else
|
||||
# [异常兜底] 弹出红色警告弹窗
|
||||
if [ -n "$CB_ID" ]; then
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/answerCallbackQuery" \
|
||||
-d "callback_query_id=${CB_ID}" \
|
||||
@@ -180,39 +166,35 @@ while true; do
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
# ======================================================================
|
||||
|
||||
REPLY_TO_TEXT=$(echo "$UPDATE" | jq -r '.message.reply_to_message.text // empty')
|
||||
|
||||
# ================== [v3.5.2 新增: 拦截别名修改的对话回复] ==================
|
||||
# ----------------------------------------------------------
|
||||
# [业务流 B] 拦截并解析别名重命名回执
|
||||
# ----------------------------------------------------------
|
||||
if [[ "$REPLY_TO_TEXT" == *"✏️ 请回复本消息以重命名节点:"* ]]; then
|
||||
# 精准提取被回复消息中的节点主键名
|
||||
TARGET_NODE=$(echo "$REPLY_TO_TEXT" | grep -v "✏️" | grep -v "仅限" | tr -d '\` ' | tr -cd 'a-zA-Z0-9_.-' | head -n 1)
|
||||
|
||||
# [v3.5.2 热修复] 废除 Bash 原生 tr 命令的中文白名单 (不支持 Unicode 会误删中文)。
|
||||
# 改用黑名单策略:仅自动转化下划线,剔除引号、特殊符号和冒号(防止破坏内部路由),
|
||||
# 将完整的中文原样送入 Base64 编码,最终严格正则清洗交由 Agent 的 Python 引擎处理!
|
||||
# 黑名单清洗策略,保护内部路由特征并容纳 Unicode
|
||||
NEW_ALIAS=$(echo "$TEXT" | sed 's/_/-/g' | tr -d '"'\''\`\$\|&;<>\n\r:' | cut -c 1-30)
|
||||
|
||||
if [ -n "$TARGET_NODE" ] && [ -n "$NEW_ALIAS" ]; then
|
||||
# 强行重写内部路由
|
||||
TEXT="do_rename:${TARGET_NODE}:${NEW_ALIAS}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.0.1 新增: 消除转圈圈与获取消息ID] ==================
|
||||
# 告诉 TG 官方“指令已收到”,立刻消除按钮上的加载圈圈 (对其他常规按钮生效)
|
||||
# 消除终端 UI 加载状态圈
|
||||
if [ -n "$CB_ID" ]; then
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/answerCallbackQuery" -d "callback_query_id=${CB_ID}" > /dev/null
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 1. 节点注册通道 (V3.1.3 大区拓扑升级版)
|
||||
# ==========================================
|
||||
# ----------------------------------------------------------
|
||||
# [业务流 C] 节点注册与通讯架构解包通道
|
||||
# ----------------------------------------------------------
|
||||
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
|
||||
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
|
||||
|
||||
# V3.6.0 兼容性拆包: 支持 7字段(OTA)、6字段(双轨)、5字段(单轨)、4字段(远古)
|
||||
# 兼容性拆包: 自动判定不同世代版本的挂载载荷
|
||||
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
|
||||
if [ "$FIELD_COUNT" -ge 7 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT RAW_ALIAS RAW_OTA <<< "$REG_LINE"
|
||||
@@ -230,7 +212,6 @@ while true; do
|
||||
RAW_OTA="false"
|
||||
fi
|
||||
|
||||
# 🛡️ 强制字符白名单过滤:保留历史特征不变
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
AGENT_REGION=$(echo "$RAW_REGION" | tr -cd 'a-zA-Z0-9' | cut -c 1-10)
|
||||
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30)
|
||||
@@ -241,6 +222,7 @@ while true; do
|
||||
AGENT_OTA=$(echo "$RAW_OTA" | tr -cd 'a-z')
|
||||
[ -z "$AGENT_OTA" ] && AGENT_OTA="false"
|
||||
|
||||
# SSRF 拦截墙
|
||||
if [[ "$AGENT_IP" =~ ^127\.|^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^::1$|^localhost$ ]]; then
|
||||
send_msg "$CHAT_ID" "⛔ **安全拦截**:禁止注册内网或回环 IP,防止 SSRF 攻击渗透。"
|
||||
continue
|
||||
@@ -251,11 +233,9 @@ while true; do
|
||||
continue
|
||||
fi
|
||||
|
||||
# [核心] 入库时追加 node_alias 与 enable_ota 字段
|
||||
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region, node_alias, enable_ota) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION', '$NODE_ALIAS', '$AGENT_OTA') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION', node_alias='$NODE_ALIAS', enable_ota='$AGENT_OTA';"
|
||||
send_msg "$CHAT_ID" "✅ **司令部确认 (v${MASTER_VERSION})**%0A节点 \`${NODE_ALIAS}\` 档案已录入!"
|
||||
|
||||
# ================== [v3.1.3 丝滑连招: 直接呼出全球大区雷达] ==================
|
||||
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||
if [ -n "$REGION_DATA" ]; then
|
||||
BTNS="["
|
||||
@@ -267,39 +247,31 @@ while true; do
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区:" "$BTNS"
|
||||
fi
|
||||
# ========================================================================
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 2. 交互菜单与下发通道
|
||||
# ==========================================
|
||||
# ----------------------------------------------------------
|
||||
# [业务流 D] 控制中枢指令集与面板呈现引擎
|
||||
# ----------------------------------------------------------
|
||||
case "$TEXT" in
|
||||
"/start"|"/menu")
|
||||
# [核心: 抓取云端最新 Master 版本 (KV 解析法)]
|
||||
REMOTE_VER=$(curl -s -m 2 "${REPO_RAW_URL}/version.txt" | grep "^MASTER_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
VER_INFO="当前版本: \`v${MASTER_VERSION}\`"
|
||||
|
||||
BTN_MASTER_OTA=""
|
||||
if [ -n "$REMOTE_VER" ]; then
|
||||
if [ "$REMOTE_VER" != "$MASTER_VERSION" ]; then
|
||||
# [需要升级] 换行显示最新版本提示
|
||||
VER_INFO="${VER_INFO}\n✨ **发现新版本**: \`v${REMOTE_VER}\` (可执行中枢热重载)"
|
||||
|
||||
# 仅当非官方网关 且 开启了中枢 OTA 权限时,才渲染升级按钮
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "${ENABLE_MASTER_OTA:-false}" == "true" ]; then
|
||||
BTN_MASTER_OTA="[{\"text\":\"🆙 升级控制中枢至 v${REMOTE_VER}\",\"callback_data\":\"master_ota_confirm\"}],"
|
||||
fi
|
||||
else
|
||||
# [已是最新] 不换行,直接在当前版本后追加绿勾状态
|
||||
VER_INFO="当前版本: \`v${MASTER_VERSION}\` (✅已是最新)"
|
||||
fi
|
||||
fi
|
||||
|
||||
NODE_COUNT=$(db_exec "SELECT COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
|
||||
# L0 扁平化重构:升级按钮置顶,底部追加带有 url 属性的 GitHub 引流按钮
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ]; then
|
||||
BTNS="[${BTN_MASTER_OTA}[{\"text\":\"🌍 进入全球雷达 (管理节点)\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 唤醒全局巡逻\",\"callback_data\":\"all_run\"}, {\"text\":\"📊 获取全局简报\",\"callback_data\":\"all_reports\"}], [{\"text\":\"🔄 全网节点 OTA 热重载\",\"callback_data\":\"all_ota_confirm\"}], [{\"text\":\"🌟 前往 GitHub 点亮星标\",\"url\":\"https://github.com/hotyue/IP-Sentinel\"}]]"
|
||||
else
|
||||
@@ -324,7 +296,7 @@ while true; do
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_ota")
|
||||
curl -k -s --connect-timeout 5 -m 15 "$TARGET_URL" > /dev/null &
|
||||
sleep 0.3 # 严格流量削峰
|
||||
sleep 0.3
|
||||
done
|
||||
fi
|
||||
;;
|
||||
@@ -346,10 +318,9 @@ while true; do
|
||||
send_msg "$CHAT_ID" "⏳ 正在下载重构图纸,司令部即将进入静默重启..."
|
||||
fi
|
||||
|
||||
# 下载最新的 master install 脚本作为幽灵进程
|
||||
curl -fsSL "${REPO_RAW_URL}/master/install_master.sh" -o "/tmp/install_master.sh"
|
||||
|
||||
# [v3.6.3 修复] 🚀 OTA 防砖机制:严格校验脚本完整性
|
||||
# [OTA 防砖机制] 严格校验脚本语法完整性,防止传输中断导致司令部失联
|
||||
if ! bash -n "/tmp/install_master.sh" >/dev/null 2>&1; then
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "❌ OTA 传输受损:脚本下载不完整,已触发防砖熔断,升级取消!"
|
||||
@@ -361,8 +332,6 @@ while true; do
|
||||
|
||||
chmod +x "/tmp/install_master.sh"
|
||||
|
||||
# 抛出幽灵进程进行脱壳升级,传递静默变量与回执 ID
|
||||
# [修复] 必须显式将环境变量注入到 bash -c 的指令串中,防止被 systemd-run 沙盒隔离丢弃
|
||||
if command -v systemd-run >/dev/null 2>&1; then
|
||||
systemd-run --quiet --no-block /bin/bash -c "export SILENT_MASTER_OTA='true'; export OTA_CHAT_ID='$CHAT_ID'; bash /tmp/install_master.sh"
|
||||
else
|
||||
@@ -370,8 +339,6 @@ while true; do
|
||||
export OTA_CHAT_ID="$CHAT_ID"
|
||||
nohup bash /tmp/install_master.sh >/dev/null 2>&1 & disown
|
||||
fi
|
||||
|
||||
# 当前旧进程休眠并等待被幽灵进程处决
|
||||
sleep 10
|
||||
;;
|
||||
|
||||
@@ -380,18 +347,15 @@ while true; do
|
||||
if [ -z "$NODE_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点。"
|
||||
else
|
||||
# [文案优化] 提前告知指挥官需要排队等待
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在召唤所有哨兵回传简报...**%0A*(为防止触发 TG 官方限流,简报将排队依次送达,请耐心等待)*"
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_report")
|
||||
curl -k -s --connect-timeout 5 -m 15 "$TARGET_URL" > /dev/null &
|
||||
# [致命修复] 强行休眠 2 秒!错开 TG 官方 1条/秒 的发信红线
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
;;
|
||||
|
||||
# ================== [补充缺失的全节点一键维护功能] ==================
|
||||
"all_run")
|
||||
NODE_DATA=$(db_exec "SELECT node_name, agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
if [ -z "$NODE_DATA" ]; then
|
||||
@@ -401,15 +365,11 @@ while true; do
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_run")
|
||||
curl -k -s --connect-timeout 5 -m 15 "$TARGET_URL" > /dev/null &
|
||||
sleep 0.2 # [新增] 流量削峰:防止瞬间 fork 导致句柄耗尽
|
||||
sleep 0.2
|
||||
done
|
||||
fi
|
||||
;;
|
||||
# ====================================================================
|
||||
|
||||
# ------------------- 🚨 请将下面这段代码插入在这里 -------------------
|
||||
|
||||
# ================== [v4.0.0 新增: 文本指令直接控制通道] ==================
|
||||
"/quality"|"/quality@"*)
|
||||
TARGET_NODE=$(echo "$TEXT" | awk '{print $2}')
|
||||
if [ -z "$TARGET_NODE" ]; then
|
||||
@@ -418,7 +378,6 @@ while true; do
|
||||
TARGET_NODE=$(echo "$TARGET_NODE" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# [加密通讯逻辑]
|
||||
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
@@ -426,11 +385,9 @@ while true; do
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [quality] 指令,请稍候..."
|
||||
|
||||
# 动态 HMAC 签名防篡改
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_quality")
|
||||
RESPONSE=$(curl -k -s --connect-timeout 5 -m 15 "$TARGET_URL" || echo "FAILED")
|
||||
|
||||
# 结果判定
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
send_msg "$CHAT_ID" "❌ 指令下发超时或失败!请检查节点公网 IP 或防火墙端口 ($AGENT_PORT) 是否放行。"
|
||||
elif [[ "$RESPONSE" == *"403"* ]]; then
|
||||
@@ -481,16 +438,13 @@ while true; do
|
||||
done <<< "$TREND_DATA"
|
||||
TEXT_RES+="\n_💡 提示:🔴风险分 >60 极易触发网页验证码拦截;谷歌显示 CN 即为高危送中。_"
|
||||
|
||||
# [v4.0.3 体验升级] 注入交互式控制台按钮
|
||||
BTNS="[[{\"text\":\"⚙️ 调出该节点控制台\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
send_ui "$CHAT_ID" "$TEXT_RES" "$BTNS"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
# ------------------- 🚨 插入代码到此结束 -------------------
|
||||
|
||||
"list_nodes")
|
||||
# 【V3.1.3】一级菜单:大区聚合并列出数量
|
||||
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||
if [ -z "$REGION_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
|
||||
@@ -501,18 +455,15 @@ while true; do
|
||||
FLAG=$(get_flag "$REGION_NAME")
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
# L1 追加返回中枢逃生舱
|
||||
BTNS="$BTNS[{\"text\":\"🏠 回到司令部\",\"callback_data\":\"/start\"}]]"
|
||||
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n已为您聚合当前舰队的部署大区,请选择要检阅的战区:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
region:*)
|
||||
# 【V3.1.3】二级菜单:目标大区下的节点双列排版
|
||||
TARGET_REGION=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# [v3.5.2] 提取物理主键和展示别名
|
||||
NODE_LIST=$(db_exec "SELECT node_name, IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND region='$TARGET_REGION';")
|
||||
if [ -z "$NODE_LIST" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 该战区下暂无可用节点。"
|
||||
@@ -531,12 +482,10 @@ while true; do
|
||||
ROW_STR="["
|
||||
fi
|
||||
done <<< "$NODE_LIST"
|
||||
# 如果是奇数,补齐最后的尾巴
|
||||
if [ $COL -eq 1 ]; then
|
||||
ROW_STR="${ROW_STR%,}]"
|
||||
BTNS="$BTNS$ROW_STR,"
|
||||
fi
|
||||
# L2 追加双重逃生舱
|
||||
BTNS="$BTNS[{\"text\":\"⬅️ 返回战区地图\",\"callback_data\":\"list_nodes\"}, {\"text\":\"🏠 回到司令部\",\"callback_data\":\"/start\"}]]"
|
||||
send_ui "$CHAT_ID" "📍 **[$TARGET_REGION] 战区哨兵矩阵**\n请锁定要执行战术动作的具体目标:" "$BTNS"
|
||||
fi
|
||||
@@ -547,7 +496,6 @@ while true; do
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
|
||||
|
||||
# 抓取节点全景元数据
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust, enable_ota, agent_ip, IFNULL(last_seen, '未知') FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
|
||||
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
|
||||
@@ -555,29 +503,21 @@ while true; do
|
||||
A_IP=$(echo "$TOGGLE_INFO" | cut -d'|' -f4)
|
||||
LAST_SEEN=$(echo "$TOGGLE_INFO" | cut -d'|' -f5)
|
||||
|
||||
# 动态渲染状态文字
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🟢 Google巡逻: 已开" && ACT_G="false" || { BTN_G="🔴 Google巡逻: 已停"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🟢 信用净化: 已开" && ACT_T="false" || { BTN_T="🔴 信用净化: 已停"; ACT_T="true"; }
|
||||
|
||||
# 模块一:即时战术动作 (V4.0.0 引入深海声呐与趋势面板)
|
||||
BTN_ACTION="[{\"text\":\"📍 触发 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 触发信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"🔍 投放深海声呐 (查IP质量)\",\"callback_data\":\"quality:$TARGET_NODE\"}, {\"text\":\"📈 查看 IP 污染趋势图\",\"callback_data\":\"trend:$TARGET_NODE\"}], [{\"text\":\"📜 提取终端实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 生成单机战报\",\"callback_data\":\"report:$TARGET_NODE\"}]"
|
||||
|
||||
# 模块二:养护状态启停
|
||||
BTN_TOGGLE="[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}, {\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}]"
|
||||
|
||||
# 模块三:深度配置管理 (结合 UI 熔断)
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "$ST_OTA" == "true" ]; then
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}, {\"text\":\"🆙 OTA 静默升级\",\"callback_data\":\"ota_confirm:$TARGET_NODE\"}]"
|
||||
else
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}]"
|
||||
fi
|
||||
|
||||
# 模块四:危险区与逃生舱
|
||||
BTN_DANGER="[{\"text\":\"🗑️ 从中枢销毁该档案\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回战区列表\",\"callback_data\":\"list_nodes\"}]"
|
||||
|
||||
# 组合终极矩阵
|
||||
BTNS="[$BTN_ACTION, $BTN_TOGGLE, $BTN_CONFIG, $BTN_DANGER]"
|
||||
|
||||
TEXT_MSG="⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n(底层标识: \`$TARGET_NODE\`)\n🌐 IP 坐标: \`$A_IP\`\n🕒 最后通讯: \`$LAST_SEEN\`\n\n请下达精确控制指令:"
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
@@ -588,7 +528,6 @@ while true; do
|
||||
;;
|
||||
|
||||
toggle:*)
|
||||
# [动态启停通信闭环]
|
||||
IFS=':' read -r CMD MOD_NAME TARGET_NODE TARGET_STATE <<< "$TEXT"
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
@@ -603,7 +542,6 @@ while true; do
|
||||
RESPONSE=$(curl -k -s --connect-timeout 5 -m 15 "$TARGET_URL" || echo "FAILED")
|
||||
|
||||
if [[ "$RESPONSE" == *"Action Accepted"* ]]; then
|
||||
# 下发成功,更新 DB,原位重绘
|
||||
db_exec "UPDATE nodes SET enable_${MOD_NAME}='$TARGET_STATE' WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
@@ -612,7 +550,6 @@ while true; do
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🔴 停用 Google 纠偏" && ACT_G="false" || { BTN_G="🟢 启用 Google 纠偏"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🔴 停用信用净化" && ACT_T="false" || { BTN_T="🟢 启用信用净化"; ACT_T="true"; }
|
||||
|
||||
# 切换后直接复用扁平化 L3 面板的重绘逻辑
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust, enable_ota, agent_ip, IFNULL(last_seen, '未知') FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
|
||||
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
|
||||
@@ -623,8 +560,7 @@ while true; do
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🟢 Google巡逻: 已开" && ACT_G="false" || { BTN_G="🔴 Google巡逻: 已停"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🟢 信用净化: 已开" && ACT_T="false" || { BTN_T="🔴 信用净化: 已停"; ACT_T="true"; }
|
||||
|
||||
# 模块一:即时战术动作 (V4.0.0 引入深海声呐与趋势面板)
|
||||
BTN_ACTION="[{\"text\":\"📍 触发 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 触发信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"🔍 投放深海声呐 (查IP质量)\",\"callback_data\":\"quality:$TARGET_NODE\"}, {\"text\":\"📈 查看 IP 污染趋势图\",\"callback_data\":\"trend:$TARGET_NODE\"}], [{\"text\":\"📜 提取终端实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 生成单机战报\",\"callback_data\":\"report:$TARGET_NODE\"}]"
|
||||
BTN_ACTION="[{\"text\":\"📍 触发 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 触发信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"🔍 投放深海声呐 (查IP质量)\",\"callback_data\":\"quality:$TARGET_NODE\"}, {\"text\":\"📈 查看 IP 污染趋势图\",\"callback_data\":\"trend:$TARGET_NODE\"}], [{\"text\":\"📜 提取终端实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 生成单机战报\",\"callback_data\":\"report:$TARGET_NODE\"}]"
|
||||
BTN_TOGGLE="[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}, {\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}]"
|
||||
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "$ST_OTA" == "true" ]; then
|
||||
@@ -646,16 +582,13 @@ while true; do
|
||||
;;
|
||||
|
||||
del:*)
|
||||
# 🛡️ 提取并强制过滤节点名与 CHAT_ID 防注入
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# 🛡️ [终极防线: 防越权横向打击] 先校验该节点是否真实属于当前操作者!
|
||||
# 因为趋势库中没有 Chat_ID 标识,不校验直接删会给黑客伪造回调清空他人数据的机会!
|
||||
# [验权防御] 防止通过伪造回调接口越权摧毁他人节点档案
|
||||
VALID_OWNER=$(db_exec "SELECT 1 FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
|
||||
if [ "$VALID_OWNER" == "1" ]; then
|
||||
# 验权通过,执行原子化级联销毁:同时抹除主配置与历史污染趋势
|
||||
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
db_exec "DELETE FROM ip_trend_log WHERE node_name='$TARGET_NODE';"
|
||||
send_msg "$CHAT_ID" "🗑️ 节点 \`$TARGET_NODE\` 的档案及历史污染趋势已从司令部彻底销毁!"
|
||||
@@ -664,7 +597,6 @@ while true; do
|
||||
continue
|
||||
fi
|
||||
|
||||
# 剔除后直接返回上级一级雷达菜单
|
||||
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||
if [ -z "$REGION_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 当前司令部已无任何节点挂载。"
|
||||
@@ -683,14 +615,12 @@ while true; do
|
||||
rename:*)
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
# [v3.5.2] 发送 ForceReply 引导用户回复
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"$CHAT_ID\",\"text\":\"✏️ 请回复本消息以重命名节点:\n\`$TARGET_NODE\`\n(仅限中英文、数字,最长20字符)\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"force_reply\":true}}" > /dev/null
|
||||
;;
|
||||
|
||||
do_rename:*)
|
||||
# [v3.5.2] 内部重命名路由 (已被第2处的代码拦截并格式化)
|
||||
IFS=':' read -r CMD TARGET_NODE NEW_ALIAS <<< "$TEXT"
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
@@ -703,7 +633,7 @@ while true; do
|
||||
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_rename")
|
||||
|
||||
# [绝密防线: Base64 编码绕过一切传输限制与 WAF 拦截]
|
||||
# [防线穿越] 借由 Base64 编码对下发特征进行混淆与防篡改护甲加持
|
||||
ALIAS_B64=$(echo -n "$NEW_ALIAS" | base64 | tr -d '\n' | tr '+/' '-_')
|
||||
TARGET_URL="${TARGET_URL}&b64=${ALIAS_B64}"
|
||||
|
||||
@@ -712,11 +642,9 @@ while true; do
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
send_msg "$CHAT_ID" "❌ 指令下发超时!为防范劫持风险,已终止请求。"
|
||||
elif [[ "$RESPONSE" == *"Action Accepted"* ]]; then
|
||||
# [v3.5.2 极致丝滑] 确认 Agent 修改成功后,Master 立即自动同步本地 SQLite 数据库!
|
||||
db_exec "UPDATE nodes SET node_alias='$NEW_ALIAS' WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
send_msg "$CHAT_ID" "✅ 通讯成功!节点别名已下发: \`$NEW_ALIAS\`%0A*(司令部档案已自动刷新,雷达面板已同步)*"
|
||||
else
|
||||
# 增加输出 RESPONSE 调试信息,排查任何拦截死因
|
||||
send_msg "$CHAT_ID" "⚠️ 节点拒绝了请求,请确保 Agent 已更新至 v3.5.2%0A(回传信息: \`${RESPONSE}\`)"
|
||||
fi
|
||||
else
|
||||
@@ -726,7 +654,6 @@ while true; do
|
||||
|
||||
ota_confirm:*)
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
# 将取消动作引导回 manage,因为 adv 已经被删除了
|
||||
CONFIRM_BTNS="[[{\"text\":\"🚨 确认执行远程升级\",\"callback_data\":\"ota_execute:$TARGET_NODE\"}], [{\"text\":\"取消\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
send_ui "$CHAT_ID" "☢️ **操作确认**:即将向 \`$TARGET_NODE\` 下发 OTA 热更新指令。\n节点更新完成后会自动发送包含新版本号的注册回执,确定执行?" "$CONFIRM_BTNS"
|
||||
;;
|
||||
@@ -767,9 +694,7 @@ while true; do
|
||||
fi
|
||||
;;
|
||||
|
||||
# 【核心升级 v4.0.0】增加拦截规则,支持 quality 前缀
|
||||
google:*|trust:*|run:*|report:*|log:*|quality:*)
|
||||
# 🛡️ 提取并强制过滤动作参数、节点名与 CHAT_ID
|
||||
ACTION_TYPE=$(echo "$TEXT" | cut -d':' -f1)
|
||||
TARGET_NODE=$(echo "$TEXT" | cut -d':' -f2 | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
@@ -779,18 +704,15 @@ while true; do
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
# [v3.0.2 防刷屏] 原位刷新菜单为等待状态
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
|
||||
else
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
|
||||
fi
|
||||
|
||||
# 🛡️ [v3.0.4] 动态签名生成与触发 (防重放与防篡改)
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_${ACTION_TYPE}")
|
||||
RESPONSE=$(curl -k -s --connect-timeout 5 -m 15 "$TARGET_URL" || echo "FAILED")
|
||||
|
||||
# 结果判定
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
TEXT_RES="❌ 指令下发超时或失败!为保护链路安全,已终止通信 (严禁降级为 HTTP)。"
|
||||
elif [[ "$RESPONSE" == *"403"* ]]; then
|
||||
@@ -809,7 +731,6 @@ while true; do
|
||||
fi
|
||||
fi
|
||||
|
||||
# [v3.0.1 防刷屏] 将等待状态刷新为最终结果
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "$TEXT_RES"
|
||||
else
|
||||
@@ -822,7 +743,7 @@ while true; do
|
||||
|
||||
|
||||
trend:*)
|
||||
# [v4.0.2 优化: 扩容 15 次追踪并引入 GOOG/GPT 状态]
|
||||
# [态势感知面板] 提取近 15 次的历史追踪记录
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
@@ -844,7 +765,6 @@ while true; do
|
||||
[ -z "$nf" ] && nf="未知"
|
||||
[ -z "$gpt" ] && gpt="未知"
|
||||
|
||||
# 时间做极简切割 (截取 04-24 20:52) 节省横向空间
|
||||
short_time=$(echo "$c_time" | cut -c 6-16)
|
||||
|
||||
if [ "$score" -le 20 ]; then SCORE_EMJ="🟢"
|
||||
@@ -852,13 +772,11 @@ while true; do
|
||||
else SCORE_EMJ="🔴"
|
||||
fi
|
||||
|
||||
# 拼接紧凑排版
|
||||
TEXT_RES+="\`${short_time}\` | ${SCORE_EMJ}\`${score}\` | \`${goog}\` | \`${nf}\` | \`${gpt}\`\n"
|
||||
done <<< "$TREND_DATA"
|
||||
TEXT_RES+="\n_💡 提示:🔴风险分 >60 极易触发网页验证码拦截;谷歌显示 CN 即为高危送中。_"
|
||||
fi
|
||||
|
||||
# [v4.0.3 体验升级] 注入交互式控制台按钮,并调用原生 UI 重绘函数
|
||||
BTNS="[[{\"text\":\"⚙️ 调出该节点控制台\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: uninstall_master.sh (IP-Sentinel Master 一键卸载脚本 - 动态锚点版)
|
||||
# 核心功能: 终止调度进程、清理看门狗定时任务、抹除数据库与配置
|
||||
# 脚本名称: uninstall_master.sh
|
||||
# 核心功能: 司令部无痕销毁、调度进程抹杀、SQLite 资产库安全覆写
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
# ----------------------------------------------------------
|
||||
# [权限鉴权] 防止非管理员误触导致中枢组件挂起或卸载不全
|
||||
# ----------------------------------------------------------
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 卸载 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
@@ -20,7 +20,7 @@ CONF_FILE="${MASTER_DIR}/master.conf"
|
||||
echo "========================================================"
|
||||
echo " 🗑️ 准备卸载 IP-Sentinel Master (控制中枢)"
|
||||
|
||||
# [v3.4.0 优化] 卸载前读取并播报中枢版本号
|
||||
# [态势感知] 卸载前动态提取并播报当前中枢内核版本
|
||||
if [ -f "$CONF_FILE" ]; then
|
||||
MASTER_VER=$(grep "^MASTER_VERSION=" "$CONF_FILE" | cut -d'"' -f2)
|
||||
[ -n "$MASTER_VER" ] && echo " 📍 目标版本: v${MASTER_VER}"
|
||||
@@ -34,11 +34,13 @@ if [[ ! "$CONFIRM_DEL" =~ ^[Yy]$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 1. 停止并删除 Systemd 服务 (适配新架构)
|
||||
# ----------------------------------------------------------
|
||||
# [进程抹杀] 阻塞并卸除底层 Systemd 强绑定服务单元
|
||||
# ----------------------------------------------------------
|
||||
echo "[1/4] 正在停止并删除 Systemd 服务..."
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在抹除 Systemd 服务单元..."
|
||||
# [防死锁修复] 先发送 SIGKILL 瞬间抹杀,防止卡死
|
||||
# 强制压制守护状态,发送 SIGKILL 剥夺其产生遗言及重启的机会
|
||||
systemctl kill --signal=SIGKILL ip-sentinel-master.service >/dev/null 2>&1 || true
|
||||
systemctl disable --now ip-sentinel-master.service >/dev/null 2>&1
|
||||
rm -f /etc/systemd/system/ip-sentinel-master.service
|
||||
@@ -48,16 +50,22 @@ else
|
||||
echo "💡 未检测到 Systemd,跳过此步骤..."
|
||||
fi
|
||||
|
||||
# 2. 停止运行中的 Master 守护进程 (兜底清理老版进程)
|
||||
# ----------------------------------------------------------
|
||||
# [内存清洗] 全面追踪并镇压游离状态的中枢调度进程
|
||||
# ----------------------------------------------------------
|
||||
echo "[2/4] 正在终止后台中枢调度进程..."
|
||||
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||
|
||||
# 3. 清除看门狗定时任务 (Cron)
|
||||
# ----------------------------------------------------------
|
||||
# [任务清洗] 基于内存管道流彻底擦除系统底层看门狗劫持
|
||||
# ----------------------------------------------------------
|
||||
echo "[3/4] 正在清理系统定时任务 (Cron)..."
|
||||
# [终极防御] 内存管道流过滤,绝不写硬盘
|
||||
# 内存管道流原位清洗,不留中间文件,免疫提权探测
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" | crontab - >/dev/null 2>&1 || true
|
||||
|
||||
# 4. 删除所有文件、配置与数据库
|
||||
# ----------------------------------------------------------
|
||||
# [物理销毁] 抹杀持久化特征,彻底销毁档案库与配置容器
|
||||
# ----------------------------------------------------------
|
||||
echo "[4/4] 正在抹除核心程序、配置文件与 SQLite 数据库..."
|
||||
if [ -d "$MASTER_DIR" ]; then
|
||||
rm -rf "$MASTER_DIR"
|
||||
|
||||
Reference in New Issue
Block a user