Compare commits

...

37 Commits

Author SHA1 Message Date
hotyue
01305f98b5 feat(master): Master 脚本接入透明统计,并新增部署前旧进程自洁功能 2026-04-11 07:08:21 +00:00
hotyue
a3ae3b29f9 feat(core): Agent 安装脚本接入透明统计,安装尾声展示全球哨兵编号 2026-04-11 07:08:13 +00:00
hotyue
053c9805d4 feat(telemetry): 开源玻璃房计划 (Glasshouse) Cloudflare Worker 计数器源码 2026-04-11 07:08:05 +00:00
hotyue
be51aab0e0 feat(core): 升级全球节点拓扑图接入香港阵地,正式发布 v3.1.1 2026-04-11 05:32:03 +00:00
hotyue
426fa74ff1 feat(data): 新增亚太核心枢纽 中国香港(HK) 节点配置文件与本土化搜索词库 2026-04-11 05:31:56 +00:00
hotyue
9904246e45 feat(core): 优化 Agent 安装逻辑,新增部署前环境自洁功能 (自动清理僵尸进程与冗余定时任务) 2026-04-11 05:31:52 +00:00
hotyue
06f52cbe1d feat(core): 升级全球节点拓扑图,正式跃升至 v3.1.0 2026-04-11 02:38:27 +00:00
hotyue
33d75925a6 feat(data): 新增亚太枢纽 新加坡 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
52fe92efe5 feat(data): 新增欧洲枢纽 法国(巴黎) 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
7fec21280f feat(data): 新增欧洲枢纽 德国(法兰克福) 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
84b5fef640 feat(data): 新增欧洲枢纽 英国(伦敦) 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
9d2aa9fcd5 fix(agent): 提拔 urllib 依赖至全局作用域,修复局部变量遮蔽导致的 Webhook 空回复崩溃 bug (v3.0.4-hotfix) 2026-04-11 02:23:52 +00:00
hotyue
ca2608756d security(core): 引入时间戳与动态 HMAC-SHA256 签名,彻底斩断明文 PSK 泄露与重放攻击隐患 (v3.0.4) 2026-04-11 01:51:03 +00:00
hotyue
27ebcfd418 security(agent): Webhook 引入 ThreadingMixIn 升级为多线程并发模型,彻底免疫 Slowloris 慢速网络耗尽攻击 (v3.0.3-part4) 2026-04-11 00:34:53 +00:00
hotyue
62deadda1e security(master): 部署脚本新增配置文件与数据库权限收敛 (chmod 600),防止 Bot Token 与节点网络拓扑泄露 (v3.0.3-part3) 2026-04-11 00:30:58 +00:00
hotyue
990d60f63a security(agent): 部署脚本新增配置文件权限收敛 (chmod 600),防止敏感 Token 发生本地提权泄露 (v3.0.3-part2) 2026-04-11 00:28:22 +00:00
hotyue
0571fcfd32 feat(install): 引入随机高位端口探测与占用校验,配合本地防火墙智能放行指引,提升防扫描隐蔽性 (v3.0.3-part1) 2026-04-11 00:12:12 +00:00
hotyue
4a77cb66d4 fix(agent): 引入 HTML 转义与 UA 伪装,修复日志回传中的 Markdown 解析冲突 (v3.0.2-final) 2026-04-10 16:25:04 +00:00
hotyue
26fdb8f899 security(agent): 纯 Python 重构彻底消除 Bash RCE 隐患,引入全局鉴权拦截未授权扫描与 DDoS (v3.0.2) 2026-04-10 16:03:31 +00:00
hotyue
5635c5d851 security(master): 构筑 SSRF 内网物理隔离墙,并为全网广播追加 PSK 鉴权令牌 (v3.0.2) 2026-04-10 16:03:24 +00:00
hotyue
00ec07b88a security(master): 修复高危 SQL 注入漏洞,重构回调交互消除面板延迟与刷屏 (v3.0.1) 2026-04-10 14:49:06 +00:00
hotyue
ef84979f91 feat(master): 司令部交互重构,新增 answerCallbackQuery 消除延迟并实现菜单原位无痕刷新 (v3.0.1) 2026-04-10 14:28:25 +00:00
hotyue
d63df0c48e fix(trust): 净化流量全面适配 IPv6 隧道,杜绝系统默认路由导致的锚点偏移 (v3.0.1) 2026-04-10 14:13:02 +00:00
hotyue
002e5f304d fix(google): 剥离冗余 IP 探测,强制全局 curl 请求遵循 IP_PREF 锚点协议出网 (v3.0.1) 2026-04-10 14:12:57 +00:00
hotyue
4bfe9ea9ca fix(report): 增加 ISP 探针多节点容灾逻辑,API 阻断时强制回退使用锚点 IP (v3.0.1) 2026-04-10 14:12:53 +00:00
hotyue
b7988dc666 fix(install): 引入多节点雷达容灾机制,新增双栈 IP 动态数组交互菜单 (v3.0.1) 2026-04-10 14:12:49 +00:00
hotyue
5463372b66 fix(report): 战报与 ISP 探针强制使用锚点协议出口,完善 IPv6 自动化方括号包裹展示 2026-04-10 13:30:47 +00:00
hotyue
85525580c6 fix(daemon): 同步锁定网络协议防重复发报,重构 Python Webhook 支持双栈及纯 IPv6 监听 2026-04-10 13:30:42 +00:00
hotyue
c1cfdb17f3 fix(install): 增加双栈网络探测与交互式 IP 锚点锁定机制,彻底修复 IPv6 格式化拼接报错问题 2026-04-10 13:30:36 +00:00
hotyue
f8b4777ed8 feat(master): 新增 Master 中枢一键卸载功能及安装脚本交互菜单 2026-04-10 12:50:53 +00:00
hotyue
f7bbfe9010 feat(master): 新增 Master 中枢一键卸载功能及安装脚本交互菜单 2026-04-10 12:42:28 +00:00
hotyue
690aa2c189 chore: 切回官方源,准备发布 v3.0.0 2026-04-10 08:59:35 +00:00
hotyue
989db0b49b fix(core): 修正 install.sh 注册暗号分隔符与主机名变量,确保 Master 准确解析 2026-04-10 08:53:16 +00:00
hotyue
90fc8adb74 feat(security): 启用 Cloudflare 安全网关,全面重构节点发报链路,彻底消除明文 Token 隐患 2026-04-10 08:40:45 +00:00
hotyue
1b7d7e7b64 docs: 完善 v3.0.0 (Global Nexus) 架构说明与全球节点文档 2026-04-10 07:32:41 +00:00
hotyue
472ead88b0 feat(core): install.sh 核心重构,引入基于 jq 的动态地图解析引擎 2026-04-10 07:29:17 +00:00
hotyue
cb788ab730 refactor(data): 升级至 v3.0 多级拓扑目录并引入 map.json 全球索引 2026-04-10 07:28:43 +00:00
23 changed files with 953 additions and 112 deletions

View File

@@ -8,7 +8,8 @@
## ✨ 核心极客特性
* **云端中枢 (Public Master)****v2.1.0 新特性**。引入官方公共机器人 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot),新手无需部署 Master 司令部,一键回车即可接入全球养护矩阵,极大降低入伍门槛
* 🗺 **全球拓扑矩阵 (Global Nexus)****v3.0.0 新特性**。引入动态 `map.json` 索引中心与多级地理架构 (国家-省州-城市)。安装脚本全自动解析云端地图,支持无限扩展全球节点,真正实现“按图索骥”
* ☁️ **云端中枢 (Public Master)**:引入官方公共机器人 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot),新手无需部署 Master 司令部,一键回车即可接入全球养护矩阵,极大降低入伍门槛。
* 🧠 **分布式中枢 (Master-Agent)**:对于硬核极客,支持私有化部署。一台 Master 主控集成 SQLite 数据库,统管无数台 Agent 边缘节点,确保数据绝对私有。
* 🎮 **TG 战术面板 (Command Center)**:无需记忆繁琐命令,原生 Inline Keyboard 按钮驱动。支持一键调出节点列表、一键下发伪装指令、一键索要精准战报、**毫秒级抓取实时运行日志**。
* 🛡️ **NAT 穿透与安全网关 (NAT-Friendly)**:边缘节点采用 Python3 极轻量 Webhook 监听,**完全自定义通信端口**,完美支持受限 NAT 小鸡。独创 TG 转发授权机制,杜绝野生节点恶意接入。
@@ -23,15 +24,16 @@
📦 IP-Sentinel
┣ 📂 master/ # 🧠 司令部SQLite 存储、TG 监听与 Webhook 调度中心
┣ 📂 core/ # 🛡️ 边缘哨兵Webhook 被动监听、高拟真养护引擎
┗ 📂 data/ # 🗂️ 全球数据规则库
📂 regions/ # 🧊 冷数据:各地区 GPS 基准配置 (固化)
┣ 📂 keywords/ # 🔥 热数据:动态搜索词库 (OTA 自动更新)
┗ 📂 data/ # 🗂️ 全球数据规则库 (v3.0 全新拓扑)
📜 map.json # 🌐 全球区域索引大脑 (Master Index)
┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
┗ 📜 user_agents.txt # 🔥 热数据:全局真实设备指纹池
```
## 🚀 极速部署 (Quick Start)
v2.1.0 提供了两种接入模式,请根据您的战术需求选择:
v3.0.0 提供了两种接入模式,请根据您的战术需求选择:
### 🔹 模式 A官方公共模式 (最简、推荐)
**适合不想折腾、只想快速养护 IP 的新兵。**
@@ -39,7 +41,8 @@ v2.1.0 提供了两种接入模式,请根据您的战术需求选择:
1. **关注机器人**:在 TG 中关注 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot) 并发送 `/start`
2. **部署 Agent**:在目标 VPS 上执行以下指令,安装过程中**直接回车**使用官方机器人,并输入您的 Chat ID
```Bash
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh)
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh)
```
3. **激活节点**:安装完成后,您的手机会收到一条 #REGISTER# 暗号,将其转发给机器人即可完成入库。
@@ -50,10 +53,12 @@ v2.1.0 提供了两种接入模式,请根据您的战术需求选择:
```Bash
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master/install_master.sh)
```
2. **部署 Agent**:在需要养护的机器上执行 Agent 脚本,输入您自建机器人的 Token 以及与 Master 一致的配置。
```Bash
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh)
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh)
```
3. **激活节点**:同上,将暗号转发给您自己的机器人即可。
@@ -63,6 +68,7 @@ bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master
```Bash
bash /opt/ip_sentinel/core/uninstall.sh
```
📡 战术联络 (Community)
@@ -71,8 +77,14 @@ bash /opt/ip_sentinel/core/uninstall.sh
🤝 参与贡献
如果你想为项目增加新的节点区域(例如德国、英国、新加坡等),或者提供更丰富的本土化搜索词库,非常欢迎提交 Pull Request
只需在 data/regions/ 新增对应国家的 JSON 规则,并在 data/keywords/ 新增词库 txt 即可。
**v3.0 全球节点贡献规范:**
1.`data/regions/国家代码/省州代码/` 目录下新增对应城市的配置 `.json`
2.`data/keywords/` 目录下新增或完善配套国家的词库 `kw_XX.txt`
3. **最重要的一步:**`data/map.json` 中登记你的国家、省州与城市信息。安装脚本将自动读取地图,在全球雷达中点亮你的节点!
⚠️ 免责声明
本项目仅供网络原理研究、个人 VPS 维护学习使用。请遵守当地法律法规及目标服务商的 TOS服务条款切勿用于恶意高频请求或任何非法用途。使用者需自行承担因不当使用造成的 IP 封禁或其他相关风险。
## Stargazers over time
[![Stargazers over time](https://starchart.cc/hotyue/IP-Sentinel.svg?variant=adaptive)](https://starchart.cc/hotyue/IP-Sentinel)

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# ==========================================================
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 V2.0)
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 V3.0.3)
# 核心功能: 智能防打扰注册、进程自检、模块级路由分发(403拦截)
# ==========================================================
@@ -24,8 +24,15 @@ if pgrep -f "webhook.py $AGENT_PORT" > /dev/null; then
exit 0
fi
# 1. 获取本机原生公网 IPv4 (强制去除所有不可见换行符和空格)
AGENT_IP=$(curl -4 -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
# 1. [v3.0.1修复] 严格按照 install.sh 锁定的网络协议 (v4/v6) 获取 IP
RAW_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
# 为新获取到的 v6 自动加方括号,以确保与之前锁定的格式对齐比对
if [[ "$RAW_IP" == *":"* ]] && [[ "$RAW_IP" != *"["* ]]; then
AGENT_IP="[${RAW_IP}]"
else
AGENT_IP="$RAW_IP"
fi
if [ -n "$AGENT_IP" ]; then
# --- [重点升级 2: 智能防打扰注册机制] ---
@@ -36,7 +43,7 @@ if [ -n "$AGENT_IP" ]; then
if [ "$AGENT_IP" != "$LAST_IP" ]; then
REG_MSG="👋 **[边缘节点接入申请]**%0A节点: \`${NODE_NAME}\`%0A地址: \`${AGENT_IP}:${AGENT_PORT}\`%0A%0A⚠ **安全验证**: 为防止非法节点接入,请长按复制下方代码,并**发送给我**以完成最终授权录入:%0A%0A\`#REGISTER#|${NODE_NAME}|${AGENT_IP}|${AGENT_PORT}\`"
curl -s -m 5 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
curl -s -m 5 -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
-d "text=${REG_MSG}" \
-d "parse_mode=Markdown" > /dev/null
@@ -48,20 +55,79 @@ if [ -n "$AGENT_IP" ]; then
fi
fi
# 3. 启动轻量级 Python3 Webhook 监听服务 (带 403 权限校验路由)
# 3. 启动轻量级 Python3 Webhook 监听服务 (v3.0.4 动态 HMAC 签名防重放)
cat > "${INSTALL_DIR}/core/webhook.py" << 'EOF'
import http.server
import socketserver
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])
# 🛡️ 提取全局鉴权 Token (利用 CHAT_ID 作为 PSK 预共享密钥)
AUTH_TOKEN = ""
if os.path.exists('/opt/ip_sentinel/config.conf'):
with open('/opt/ip_sentinel/config.conf', 'r') as f:
for line in f:
line = line.strip()
if line.startswith('CHAT_ID='):
AUTH_TOKEN = line.split('=', 1)[1].strip('"\'')
break
class AgentHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
# 路由 1: Google 区域纠偏 (含老版 run 指令兼容)
if self.path == '/trigger_google' or self.path == '/trigger_run':
# 🛡️ [v3.0.4 核心] URL 解析与动态 HMAC-SHA256 签名校验
parsed = urllib.parse.urlparse(self.path)
req_path = parsed.path
if AUTH_TOKEN:
query = urllib.parse.parse_qs(parsed.query)
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()
self.wfile.write(b"401 Unauthorized: Missing Signature\n")
return
try:
# 校验 2时间戳防重放 (误差 ±60秒 内有效,拒绝隔夜抓包重放)
if abs(int(time.time()) - int(req_t)) > 60:
self.send_response(401)
self.end_headers()
self.wfile.write(b"401 Unauthorized: Request Expired\n")
return
except ValueError:
self.send_response(401)
self.end_headers()
return
# 校验 3HMAC 数据完整性与身份合法性校验
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
# ================== 路由分发 (恢复为安全的精确匹配) ==================
# 路由 1: Google 区域纠偏
if req_path == '/trigger_google' or req_path == '/trigger_run':
if os.path.exists('/opt/ip_sentinel/core/mod_google.sh'):
self.send_response(200)
self.send_header("Content-type", "text/plain")
@@ -75,7 +141,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
self.wfile.write(b"403 Forbidden: Google Module Disabled\n")
# 路由 2: IP 信用净化
elif self.path == '/trigger_trust':
elif req_path == '/trigger_trust':
if os.path.exists('/opt/ip_sentinel/core/mod_trust.sh'):
self.send_response(200)
self.send_header("Content-type", "text/plain")
@@ -89,7 +155,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
self.wfile.write(b"403 Forbidden: Trust Module Disabled\n")
# 路由 3: 触发战报推送
elif self.path == '/trigger_report':
elif req_path == '/trigger_report':
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
@@ -97,21 +163,48 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
subprocess.Popen(['bash', '/opt/ip_sentinel/core/tg_report.sh'])
# 路由 4: 抓取并回传实时日志
elif self.path == '/trigger_log':
elif req_path == '/trigger_log':
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"Action Accepted: fetch_log\n")
bash_cmd = """
source /opt/ip_sentinel/config.conf
LOG_DATA=$(tail -n 15 /opt/ip_sentinel/logs/sentinel.log)
NODE=$(hostname | cut -c 1-15)
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
-d "chat_id=${CHAT_ID}" \
-d "text=📄 **[${NODE}] 实时运行日志:**%0A\`\`\`log%0A${LOG_DATA}%0A\`\`\`" \
-d "parse_mode=Markdown"
"""
subprocess.Popen(['bash', '-c', bash_cmd])
try:
config = {}
if os.path.exists('/opt/ip_sentinel/config.conf'):
with open('/opt/ip_sentinel/config.conf', 'r') as f:
for line in f:
line = line.strip()
if '=' in line and not line.startswith('#'):
key, val = line.split('=', 1)
config[key] = val.strip('"\'')
log_data = "日志文件不存在或为空"
log_path = '/opt/ip_sentinel/logs/sentinel.log'
if os.path.exists(log_path):
with open(log_path, 'r', errors='ignore') as f:
lines = f.readlines()
if lines:
log_data = html.escape("".join(lines[-15:]))
node_name = subprocess.check_output(['hostname']).decode('utf-8').strip()[:15]
text_msg = f"📄 <b>[{node_name}] 实时运行日志:</b>\n<pre><code>{log_data}</code></pre>"
data = urllib.parse.urlencode({
'chat_id': config.get('CHAT_ID', ''),
'text': text_msg,
'parse_mode': 'HTML'
}).encode('utf-8')
req = urllib.request.Request(
config.get('TG_API_URL', ''),
data=data,
headers={'User-Agent': 'IP-Sentinel-Agent/3.0.4'}
)
urllib.request.urlopen(req, timeout=10)
except Exception as e:
print(f"Log transmission failed: {e}")
else:
self.send_response(404)
@@ -120,11 +213,19 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
def log_message(self, format, *args):
pass
import socket
# ================== [v3.0.3 变更: 引入多线程模型抵抗 Slowloris 攻击] ==================
class ThreadedDualStackServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
allow_reuse_address = True # 开启端口复用,防止热重启时端口冲突
address_family = socket.AF_INET6 if socket.has_ipv6 else socket.AF_INET
try:
with socketserver.TCPServer(("", PORT), AgentHandler) as httpd:
bind_addr = "::" if socket.has_ipv6 else ""
with ThreadedDualStackServer((bind_addr, PORT), AgentHandler) as httpd:
httpd.serve_forever()
except Exception as e:
sys.exit(1)
# ====================================================================================
EOF
# --- [重点升级 3: 真正的静默后台启动] ---

View File

@@ -1,12 +1,14 @@
#!/bin/bash
# ==========================================================
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v2.1.0)
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v3.0.3 - Global Nexus)
# 核心功能: 区域选择、模块按需开启、官方机器人一键配置
# ==========================================================
# 你的 GitHub 仓库 Raw 数据直链前缀
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
# 临时改为私库地址用于测试
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
INSTALL_DIR="/opt/ip_sentinel"
CONFIG_FILE="${INSTALL_DIR}/config.conf"
@@ -26,15 +28,21 @@ else
echo "⚠️ 未知系统,请确保已手动安装 curl, jq, pgrep 和 python3"
fi
# 2. 交互式引导 (包含卸载选项)
echo -e "\n[2/7] 请选择你要伪装的目标区域或执行卸载:"
echo " 1) 🇯🇵 日本 (东京 - JP)"
echo " 2) 🇺🇸 美国 (美西 - US)"
echo " 3) 🗑️ 一键卸载 IP-Sentinel"
read -p "请输入选择 [1-3] (默认1): " REGION_CHOICE
# 2. 交互式引导与动态地图解析 (v3.0 全球网络)
echo -e "\n[2/7] 正在连线云端,拉取全球节点地图..."
curl -sL "${REPO_RAW_URL}/data/map.json" -o "/tmp/map.json"
# 如果选择卸载,拉取卸载脚本执行并退出
if [ "$REGION_CHOICE" == "3" ]; then
if [ ! -s "/tmp/map.json" ]; then
echo -e "\033[31m❌ 拉取全球地图失败!请检查网络或 GitHub 仓库地址。\033[0m"
exit 1
fi
echo -e "\n请选择操作:"
echo " 1) 🚀 部署边缘节点 (进入全球节点配置)"
echo " 2) 🗑️ 一键卸载 IP-Sentinel"
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
if [ "$ACTION_CHOICE" == "2" ]; then
echo -e "\n⏳ 正在拉取卸载程序..."
curl -sL "${REPO_RAW_URL}/core/uninstall.sh" -o "/tmp/ip_uninstall.sh"
chmod +x "/tmp/ip_uninstall.sh"
@@ -43,16 +51,91 @@ if [ "$REGION_CHOICE" == "3" ]; then
exit 0
fi
# 正常安装流程匹配区域
case ${REGION_CHOICE:-1} in
2) REGION_CODE="US" ;;
*) REGION_CODE="JP" ;;
esac
# ================== [v3.1.1 新增: 安装前环境纯净度清理 (严格保留日志)] ==================
echo -e "\n⏳ 正在清理旧版守护进程与冗余任务 (保留历史日志)..."
# 1. 强制超度可能存活的 Webhook 及各类看门狗进程,释放端口
pkill -9 -f "webhook.py" >/dev/null 2>&1 || true
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1 || true
pkill -9 -f "runner.sh" >/dev/null 2>&1 || true
# 本地工作目录初始化
# 2. 清除系统定时任务 (Cron) 中的旧版条目
if crontab -l >/dev/null 2>&1; then
crontab -l | grep -v "ip_sentinel" > /tmp/cron_clean
crontab /tmp/cron_clean
rm -f /tmp/cron_clean
fi
# 3. 抹除旧版核心代码与配置文件,杜绝代码冲突 (精准避开 logs 目录)
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
echo -e "\033[32m✅ 环境清理完毕,幽灵进程已肃清!\033[0m"
# ========================================================================================
# 📍 动态一级菜单:国家选择
echo -e "\n\033[36m📍 【第一级】请选择目标国家/地区:\033[0m"
jq -r '.countries[] | "\(.id)|\(.name)|\(.keyword_file)"' /tmp/map.json > /tmp/countries.txt
i=1; COUNTRY_MAP=(); KEYWORD_MAP=()
while IFS="|" read -r c_id c_name k_file; do
echo " $i) $c_name"
COUNTRY_MAP[$i]="$c_id"
KEYWORD_MAP[$i]="$k_file"
((i++))
done < /tmp/countries.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " C_SEL
C_SEL=${C_SEL:-1}
COUNTRY_ID="${COUNTRY_MAP[$C_SEL]}"
KEYWORD_FILE="${KEYWORD_MAP[$C_SEL]}"
REGION_CODE="$COUNTRY_ID" # 兼容旧版的 config.conf
# 📍 动态二级菜单:省/州选择
echo -e "\n\033[36m📍 【第二级】正在检索 [$COUNTRY_ID] 的行政区数据...\033[0m"
jq -r ".countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/states.txt
STATE_COUNT=$(wc -l < /tmp/states.txt)
if [ "$STATE_COUNT" -eq 1 ]; then
IFS="|" read -r STATE_ID STATE_NAME < /tmp/states.txt
echo -e "\033[32m💡 该国家下仅有单一配置 [$STATE_NAME],已自动跃迁。\033[0m"
else
i=1; STATE_MAP=()
while IFS="|" read -r s_id s_name; do
echo " $i) $s_name"
STATE_MAP[$i]="$s_id"
((i++))
done < /tmp/states.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " S_SEL
S_SEL=${S_SEL:-1}
STATE_ID="${STATE_MAP[$S_SEL]}"
fi
# 📍 动态三级菜单:城市选择
echo -e "\n\033[36m📍 【第三级】请锁定具体城市节点:\033[0m"
jq -r ".countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | select(.id==\"$STATE_ID\") | .cities[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/cities.txt
CITY_COUNT=$(wc -l < /tmp/cities.txt)
if [ "$CITY_COUNT" -eq 1 ]; then
IFS="|" read -r CITY_ID CITY_NAME < /tmp/cities.txt
echo -e "\033[32m💡 该区域下仅有单一城市 [$CITY_NAME],已自动锁定。\033[0m"
else
i=1; CITY_MAP=()
while IFS="|" read -r c_id c_name; do
echo " $i) $c_name"
CITY_MAP[$i]="$c_id"
((i++))
done < /tmp/cities.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " CI_SEL
CI_SEL=${CI_SEL:-1}
CITY_ID="${CITY_MAP[$CI_SEL]}"
fi
# 清理临时文件
rm -f /tmp/map.json /tmp/countries.txt /tmp/states.txt /tmp/cities.txt
# 本地工作目录初始化 (支持 v3.0 的深度层级)
mkdir -p "${INSTALL_DIR}/core"
mkdir -p "${INSTALL_DIR}/data/keywords"
mkdir -p "${INSTALL_DIR}/data/regions"
mkdir -p "${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}"
mkdir -p "${INSTALL_DIR}/logs"
# 3. 功能模块前置开关 (按需加载)
@@ -83,24 +166,120 @@ if [[ "$TG_CHOICE" =~ ^[Yy]$ ]]; then
read -p "请输入您的 Telegram Bot Token (回车使用官方默认): " USER_TOKEN
if [ -z "$USER_TOKEN" ]; then
TG_TOKEN="8733029779:AAErXnFw45NCWZl4ylKQX-0OIC9SA_4XifM"
echo -e "\033[32m✅ 已自动配置官方机器人 (@OmniBeacon_bot)。\033[0m"
TG_TOKEN="OFFICIAL_GATEWAY_MODE"
TG_API_URL="https://omni-gateway.yuezhongjun.workers.dev"
echo -e "\033[32m✅ 已自动连接官方安全网关 (@OmniBeacon_bot)。\033[0m"
echo -e "\033[33m👉 请确保您已关注官方机器人并发送过 /start否则将无法接收消息。\033[0m"
else
TG_TOKEN="$USER_TOKEN"
TG_API_URL="https://api.telegram.org/bot${TG_TOKEN}/sendMessage"
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
fi
echo -e "\033[33m💡 提示:如果您不知道自己的 Chat ID可以关注 @userinfobot 获取。\033[0m"
read -p "请输入你的 Chat ID (与主控一致): " CHAT_ID
read -p "请输入本机用于接收指令的 Webhook 端口 (默认 9527): " INPUT_PORT
[ -n "$INPUT_PORT" ] && AGENT_PORT="$INPUT_PORT"
# ================== [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
echo -n "."
done
echo -e " 完成!"
echo -e "💡 系统为您生成的推荐随机高位端口为: \033[32m$RANDOM_PORT\033[0m"
echo -e "\033[33m(该端口已通过本地占用校验,可直接使用)\033[0m"
while true; do
read -p "请输入 Webhook 监听端口 (回车采用推荐, 或手动输入): " INPUT_PORT
if [ -z "$INPUT_PORT" ]; 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"
else
AGENT_PORT="$INPUT_PORT"
break
fi
else
echo -e "\033[31m❌ 输入非法!端口范围应为 1-65535。\033[0m"
fi
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=()
[[ -n "$DETECT_V4" ]] && { IP_OPTIONS+=("$DETECT_V4"); IP_PROTO+=("4"); }
[[ -n "$DETECT_V6" ]] && { IP_OPTIONS+=("$DETECT_V6"); IP_PROTO+=("6"); }
if [ ${#IP_OPTIONS[@]} -eq 0 ]; then
echo -e "\033[33m⚠ 雷达受阻:未能自动探测到公网 IP请手动指定。\033[0m"
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
[[ "$PUBLIC_IP" == *":"* ]] && IP_PREF="6" || IP_PREF="4"
else
echo "📍 发现可用出口 IP请选择要注册与养护的锚点:"
for i in "${!IP_OPTIONS[@]}"; do
num=$((i+1))
if [ "${IP_PROTO[$i]}" == "4" ]; then
echo " $num) 🌐 IPv4: ${IP_OPTIONS[$i]} (默认选项)"
else
echo " $num) 🌌 IPv6: ${IP_OPTIONS[$i]}"
fi
done
CUSTOM_OPT=$(( ${#IP_OPTIONS[@]} + 1 ))
echo " $CUSTOM_OPT) ✍️ 手动指定其他 IP (适合多 IP 站群机)"
read -p "请输入选择 (默认1): " IP_CHOICE
IP_CHOICE=${IP_CHOICE:-1}
if [ "$IP_CHOICE" -le "${#IP_OPTIONS[@]}" ] && [ "$IP_CHOICE" -gt 0 ]; then
idx=$((IP_CHOICE-1))
PUBLIC_IP="${IP_OPTIONS[$idx]}"
IP_PREF="${IP_PROTO[$idx]}"
elif [ "$IP_CHOICE" -eq "$CUSTOM_OPT" ]; 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
# 终极修复:为 IPv6 自动穿上防护装甲(方括号),解决 Master 拼接 URL 报错问题
if [[ "$PUBLIC_IP" == *":"* ]] && [[ "$PUBLIC_IP" != *"["* ]]; then
BIND_IP="[${PUBLIC_IP}]"
else
BIND_IP="$PUBLIC_IP"
fi
echo -e "\033[32m✅ 哨兵锚点已永久锁定至: $BIND_IP\033[0m"
# ========================================================================
# 5. 远程拉取冷数据并解析固化
echo -e "\n[5/7] 正在从你的数据仓库拉取 [${REGION_CODE}] 节点的底层规则..."
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION_CODE}.json"
curl -sL "${REPO_RAW_URL}/data/regions/${REGION_CODE}.json" -o "$REGION_JSON_FILE"
echo -e "\n[5/7] 正在从云端数据仓库拉取 [${CITY_NAME}] 节点的底层规则..."
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json"
curl -sL "${REPO_RAW_URL}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json" -o "$REGION_JSON_FILE"
if [ ! -s "$REGION_JSON_FILE" ]; then
echo "❌ 拉取或解析规则失败!请检查 Forgejo 仓库是否公开或网络是否畅通。"
@@ -129,12 +308,21 @@ ENABLE_GOOGLE="$ENABLE_GOOGLE"
ENABLE_TRUST="$ENABLE_TRUST"
TG_TOKEN="$TG_TOKEN"
TG_API_URL="$TG_API_URL"
CHAT_ID="$CHAT_ID"
AGENT_PORT="$AGENT_PORT"
INSTALL_DIR="$INSTALL_DIR"
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
# [v3.0.1新增修改 2: 网络栈锚点锁定配置,供其他脚本读取]
IP_PREF="$IP_PREF"
BIND_IP="$BIND_IP"
EOF
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
chmod 600 "$CONFIG_FILE"
# ====================================================================
# 6. 拉取全套组件 (按需下载,绝不浪费空间)
echo -e "\n[6/7] 正在根据模块开关部署核心引擎与热数据..."
# 基础公共组件
@@ -148,7 +336,8 @@ curl -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "${INSTALL_DIR}/data/user_age
# 动态按需组件
if [ "$ENABLE_GOOGLE" == "true" ]; then
curl -sL "${REPO_RAW_URL}/core/mod_google.sh" -o "${INSTALL_DIR}/core/mod_google.sh"
curl -sL "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
# 根据 map.json 动态匹配的词库文件进行下载
curl -sL "${REPO_RAW_URL}/data/keywords/${KEYWORD_FILE}" -o "${INSTALL_DIR}/data/keywords/${KEYWORD_FILE}"
fi
if [ "$ENABLE_TRUST" == "true" ]; then
@@ -185,19 +374,21 @@ rm -f /tmp/cron_backup
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
echo -e "\n📡 正在向指挥部发送注册暗号..."
# 获取公网 IP
PUBLIC_IP=$(curl -s https://api64.ipify.org || curl -s https://ifconfig.me || echo "未知IP")
# [v3.0.1新增修改 3: 删除原来的 curl 取 IP直接使用我们上方锁定的 BIND_IP]
# 并提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的重复推送
echo "$BIND_IP" > "${INSTALL_DIR}/core/.last_ip"
# 构造注册暗号
REG_MSG="#REGISTER#:${REGION_NAME}:${PUBLIC_IP}:${AGENT_PORT}"
# 构造注册暗号 (使用带 [] 装甲的 BIND_IP防止 Master 端解析错误)
NODE_NAME=$(hostname | cut -c 1-15)
REG_MSG="#REGISTER#|${NODE_NAME}|${BIND_IP}|${AGENT_PORT}"
# 执行主动推送
PUSH_RESULT=$(curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
# 执行主动推送
PUSH_RESULT=$(curl -s -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
-d "parse_mode=Markdown" \
-d "text=✨ *IP-Sentinel 部署成功!*
📍 区域:${REGION_NAME}
🌐 IP${PUBLIC_IP}
🌐 IP${BIND_IP}
🔌 端口:${AGENT_PORT}
🔑 *请点击下方指令复制并回复给机器人:*
@@ -216,7 +407,34 @@ echo "📍 你的本地守护区域已锁定为: $REGION_NAME"
echo "⚙️ 哨兵现已开启 [每30分钟] 的高频高拟真养护循环。"
if [[ -n "$TG_TOKEN" ]]; then
echo "📡 Webhook 监听已启动 (端口: $AGENT_PORT) 并向中枢发送了注册请求。"
echo "⚠️ 请务必确保本机的防火墙放行了 TCP $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
FW_MSG="iptables -I INPUT -p tcp --dport $AGENT_PORT -j ACCEPT"
fi
echo -e "\033[33m⚠ 警告:请务必确保本机及云服务商安全组放行了 TCP $AGENT_PORT 端口!\033[0m"
if [ -n "$FW_MSG" ]; then
echo "💡 检测到本地防火墙开启,您可以尝试执行以下命令放行:"
echo -e "\033[36m $FW_MSG\033[0m"
fi
# ====================================================================
fi
echo "🗑️ 若未来需卸载,可重新运行本脚本选择[3]或执行: bash ${INSTALL_DIR}/core/uninstall.sh"
echo "========================================================"
echo "🗑️ 若未来需卸载,可重新运行本脚本选择[2]或执行: bash ${INSTALL_DIR}/core/uninstall.sh"
echo "========================================================"
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名不收集IP)..."
AGENT_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/agent" || echo "")
if [ -n "$AGENT_COUNT" ] && [[ "$AGENT_COUNT" =~ ^[0-9]+$ ]]; then
echo -e "\033[32m✅ 感谢您成为全球第 ${AGENT_COUNT} 名 IP-Sentinel 哨兵!\033[0m"
else
echo -e "\033[32m✅ 感谢您加入 IP-Sentinel 哨兵阵列!\033[0m"
fi
echo -e "\n"

View File

@@ -48,8 +48,8 @@ get_random_coord() {
}
# --- [环境初始化] ---
# 获取当前出口 IP 仅用于日志记录
CURRENT_V4=$(curl -4 -m 10 -s https://api.ip.sb/ip || echo "获取IP失败")
# [v3.0.2修复] 直接读取系统已锁定的锚点 IP彻底杜绝“获取IP失败”及隧道偏移
CURRENT_IP="${BIND_IP:-Unknown}"
# 会话锁定:单次执行内使用固定的浏览器指纹
SESSION_UA=${UA_POOL[$RANDOM % ${#UA_POOL[@]}]}
@@ -60,7 +60,7 @@ SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
# 【核心升级】随机决定本次上网深度 (6 - 10 个复合动作,配合高频长效拉伸)
TOTAL_ACTIONS=$((6 + RANDOM % 5))
log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_V4"
log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_IP"
log "$MODULE_NAME" "INFO " "设备指纹锁定: ${SESSION_UA:0:45}..."
log "$MODULE_NAME" "INFO " "虚拟驻留坐标: $SESSION_BASE_LAT, $SESSION_BASE_LON"
@@ -79,19 +79,19 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
case $ACTION_TYPE in
1) # 搜索行为
CODE=$(curl -4 -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
CODE=$(curl -${IP_PREF:-4} -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://www.google.com/search?q=${ENCODED_KEY}&${LANG_PARAMS}")
;;
2) # 浏览本土新闻
CODE=$(curl -4 -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
CODE=$(curl -${IP_PREF:-4} -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://news.google.com/home?${LANG_PARAMS}")
;;
3) # 地图坐标查询
CODE=$(curl -4 -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
CODE=$(curl -${IP_PREF:-4} -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) # 触发移动端系统底层位置检测像素
CODE=$(curl -4 -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
CODE=$(curl -${IP_PREF:-4} -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://connectivitycheck.gstatic.com/generate_204")
;;
esac
@@ -108,8 +108,8 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
done
# --- [结果纠偏自检] ---
# 去掉所有语言参数,进行一次最干净的直连测试
FINAL_URL=$(curl -4 -m 15 -s -L -o /dev/null -w "%{url_effective}" https://www.google.com)
# 去掉所有语言参数,进行一次最干净的直连测试 (强制遵循锚点协议)
FINAL_URL=$(curl -${IP_PREF:-4} -m 15 -s -L -o /dev/null -w "%{url_effective}" https://www.google.com)
if [[ "$FINAL_URL" == *"$VALID_URL_SUFFIX"* ]]; then
STATUS="✅ 目标区域达成 ($VALID_URL_SUFFIX)"

View File

@@ -19,10 +19,10 @@ LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION}.json"
# 2. 动态获取配置 (解耦核心)
# 兼容旧节点:如果本地没有 json自动拉取最新的云端配置
# 兼容旧节点:如果本地没有 json自动拉取最新的云端配置 (强制遵循锚点协议)
if [ ! -f "$REGION_JSON_FILE" ]; then
mkdir -p "${INSTALL_DIR}/data/regions"
curl -sL "${REPO_RAW_URL}/data/regions/${REGION}.json" -o "$REGION_JSON_FILE"
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/regions/${REGION}.json" -o "$REGION_JSON_FILE"
fi
# 使用 jq 将 json 中的网址数组安全地读入 Bash 数组
@@ -64,7 +64,8 @@ for ((i=1; i<=STEP_COUNT; i++)); do
# 随机抽取本地区域权威网址
TARGET_URL=${TRUST_URLS[$RANDOM % ${#TRUST_URLS[@]}]}
HTTP_CODE=$(curl -A "$CURRENT_UA" \
# [v3.0.1修复] 注入高权重流量时,强制从绑定的 IPv4 或 IPv6 隧道出网
HTTP_CODE=$(curl -${IP_PREF:-4} -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" \
-H "Sec-Fetch-Dest: document" \

View File

@@ -18,16 +18,25 @@ if [ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ]; then
exit 0
fi
# 2. 节点元数据抓取
# 2. 节点元数据抓取 (v3.0.1修复: 严格使用配置中的协议探测出口与多节点容灾)
NODE_NAME=$(hostname | cut -c 1-15)
CURRENT_IP=$(curl -4 -s -m 5 api.ip.sb/ip || echo "Unknown")
# 多节点容灾探测
CURRENT_IP=$( (curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip || curl -${IP_PREF:-4} -s -m 5 ifconfig.me) 2>/dev/null | tr -d '[:space:]' )
# 强制兜底:如果所有外部 API 都挂了,直接使用本地强行锁定的 BIND_IP
[ -z "$CURRENT_IP" ] && CURRENT_IP="$BIND_IP"
# 为可能获取到的 IPv6 自动添加方括号护甲
[[ "$CURRENT_IP" == *":"* ]] && [[ "$CURRENT_IP" != *"["* ]] && CURRENT_IP="[${CURRENT_IP}]"
# 智能判断 IP 属性
ISP_INFO=$(curl -4 -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
ISP_INFO=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
[ -z "$ISP_INFO" ] || [ "$ISP_INFO" == "null" ] && ISP_INFO="未知 ISP"
if [[ "$ISP_INFO" == *"Cloudflare"* ]]; then
IP_TYPE="Cloudflare Warp 🛰️"
else
IP_TYPE="Native 原生网卡 🏠"
IP_TYPE="$ISP_INFO 🏠"
fi
# 动态国旗
@@ -116,8 +125,8 @@ else
fi
# 5. 调用 API 推送
RESPONSE=$(curl -s -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
# 5. 调用 API 推送 (接入安全网关)
RESPONSE=$(curl -s -m 10 -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
-d "text=${MSG}" \
-d "parse_mode=Markdown")

10
data/keywords/kw_DE.txt Normal file
View File

@@ -0,0 +1,10 @@
wetter frankfurt heute
bundesliga ergebnisse
aktuelle nachrichten deutschland
restaurant in der nähe
deutsche bahn fahrplan
urlaub buchen
rezept für kartoffelsalat
dax aktueller stand
apotheke notdienst frankfurt
günstige flüge

10
data/keywords/kw_FR.txt Normal file
View File

@@ -0,0 +1,10 @@
meteo paris
actualités en direct
résultats ligue 1
pharmacie de garde
horaires sncf
recette crêpes
cac 40 en direct
acheter billet louvre
boulangerie autour de moi
carte vitale ameli

10
data/keywords/kw_HK.txt Normal file
View File

@@ -0,0 +1,10 @@
香港天文台天氣預報
MTR 港鐵路線圖
OpenRice 附近美食
LIHKG 討論區
恆生指數今日行情
SCMP breaking news
HKEX 港交所股價
國泰航空航班狀態
香港迪士尼樂園門票
百佳超級市場網購

10
data/keywords/kw_SG.txt Normal file
View File

@@ -0,0 +1,10 @@
singapore weather forecast
mrt map singapore
straitstimes breaking news
cpf board login
hdb bto launch updates
best chicken rice near me
public holidays sg
singpass login portal
changi airport flight status
iras tax filing

10
data/keywords/kw_UK.txt Normal file
View File

@@ -0,0 +1,10 @@
london weather today
bbc news latest
premier league fixtures
tesco near me
tube map london
uk bank holidays
royal family news
how to make english tea
nhs symptom checker
property for sale in london

104
data/map.json Normal file
View File

@@ -0,0 +1,104 @@
{
"version": "3.1.0",
"updated_at": "2026-04-11",
"countries": [
{
"id": "US",
"name": "United States (美国)",
"keyword_file": "kw_US.txt",
"states": [
{
"id": "CA",
"name": "California (加州)",
"cities": [
{ "id": "Los_Angeles", "name": "Los Angeles (洛杉矶)" }
]
}
]
},
{
"id": "JP",
"name": "Japan (日本)",
"keyword_file": "kw_JP.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Tokyo", "name": "Tokyo (东京)" }
]
}
]
},
{
"id": "UK",
"name": "United Kingdom (英国)",
"keyword_file": "kw_UK.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "London", "name": "London (伦敦)" }
]
}
]
},
{
"id": "DE",
"name": "Germany (德国)",
"keyword_file": "kw_DE.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Frankfurt", "name": "Frankfurt (法兰克福)" }
]
}
]
},
{
"id": "FR",
"name": "France (法国)",
"keyword_file": "kw_FR.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Paris", "name": "Paris (巴黎)" }
]
}
]
},
{
"id": "SG",
"name": "Singapore (新加坡)",
"keyword_file": "kw_SG.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Singapore", "name": "Singapore (新加坡)" }
]
}
]
},
{
"id": "HK",
"name": "Hong Kong (香港)",
"keyword_file": "kw_HK.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "HongKong", "name": "Hong Kong (香港)" }
]
}
]
}
]
}

View File

@@ -0,0 +1,20 @@
{
"region_name": "Germany - Frankfurt",
"google_module": {
"base_lat": 50.1109,
"base_lon": 8.6821,
"lang_params": "hl=de&gl=DE",
"valid_url_suffix": "de"
},
"trust_module": {
"white_urls": [
"https://www.amazon.de/",
"https://www.spiegel.de/",
"https://www.tagesschau.de/",
"https://de.wikipedia.org/wiki/Spezial:Zuf%C3%A4llige_Seite",
"https://www.ebay.de/",
"https://www.bild.de/",
"https://www.kicker.de/"
]
}
}

View File

@@ -0,0 +1,20 @@
{
"region_name": "France - Paris",
"google_module": {
"base_lat": 48.8566,
"base_lon": 2.3522,
"lang_params": "hl=fr&gl=FR",
"valid_url_suffix": "fr"
},
"trust_module": {
"white_urls": [
"https://www.lemonde.fr/",
"https://www.lefigaro.fr/",
"https://www.amazon.fr/",
"https://www.service-public.fr/",
"https://fr.wikipedia.org/wiki/Sp%C3%A9cial:Page_au_hasard",
"https://www.cdiscount.com/",
"https://www.fnac.com/"
]
}
}

View File

@@ -0,0 +1,20 @@
{
"region_name": "Hong Kong",
"google_module": {
"base_lat": 22.2847,
"base_lon": 114.1582,
"lang_params": "hl=zh-HK&gl=HK",
"valid_url_suffix": "com.hk"
},
"trust_module": {
"white_urls": [
"https://www.gov.hk/",
"https://www.hko.gov.hk/",
"https://www.scmp.com/",
"https://www.hk01.com/",
"https://zh.wikipedia.org/wiki/Special:Random",
"https://www.hktvmall.com/",
"https://www.mtr.com.hk/"
]
}
}

View File

@@ -0,0 +1,20 @@
{
"region_name": "Singapore - Singapore",
"google_module": {
"base_lat": 1.3521,
"base_lon": 103.8198,
"lang_params": "hl=en-SG&gl=SG",
"valid_url_suffix": "com.sg"
},
"trust_module": {
"white_urls": [
"https://www.straitstimes.com/",
"https://www.channelnewsasia.com/",
"https://www.gov.sg/",
"https://shopee.sg/",
"https://en.wikipedia.org/wiki/Special:Random",
"https://www.fairprice.com.sg/",
"https://www.dbs.com.sg/"
]
}
}

View File

@@ -0,0 +1,20 @@
{
"region_name": "United Kingdom - London",
"google_module": {
"base_lat": 51.5074,
"base_lon": -0.1278,
"lang_params": "hl=en&gl=GB",
"valid_url_suffix": "co.uk"
},
"trust_module": {
"white_urls": [
"https://www.bbc.co.uk/",
"https://www.gov.uk/",
"https://www.amazon.co.uk/",
"https://www.theguardian.com/uk",
"https://www.nhs.uk/",
"https://en.wikipedia.org/wiki/Special:Random",
"https://www.ebay.co.uk/"
]
}
}

View File

@@ -1,5 +1,5 @@
{
"region_name": "美国 (美西)",
"region_name": "United States - Los Angeles",
"google_module": {
"base_lat": 34.0522,
"base_lon": -118.2437,

View File

@@ -1,17 +1,39 @@
#!/bin/bash
# ==========================================================
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本)
# 核心功能: 安装 SQLite3、初始化数据库表、配置后台守护进程
# ==========================================================
# [新增] 提取仓库直链前缀变量,方便后续在官方库和私库间一键切换
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
# 临时改为私库地址用于测试
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
MASTER_DIR="/opt/ip_sentinel_master"
DB_FILE="${MASTER_DIR}/sentinel.db"
echo "========================================================"
echo " 🧠 准备部署 IP-Sentinel Master 控制中枢"
# [修改] 将欢迎语改为更通用的文案,因为现在不仅能部署,还能卸载
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢)"
echo "========================================================"
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
echo -e "\n请选择操作:"
echo " 1) 🚀 部署 Master 控制中枢"
echo " 2) 🗑️ 一键卸载 Master 中枢"
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
if [ "$ACTION_CHOICE" == "2" ]; then
echo -e "\n⏳ 正在拉取卸载程序..."
# [新增逻辑] 使用上面定义的 REPO_RAW_URL 动态拉取卸载脚本,执行后自动销毁临时文件
curl -sL "${REPO_RAW_URL}/master/uninstall_master.sh" -o "/tmp/uninstall_master.sh"
chmod +x "/tmp/uninstall_master.sh"
bash "/tmp/uninstall_master.sh"
rm -f "/tmp/uninstall_master.sh"
exit 0
fi
# ================== [v3.1.1 延续: 安装前环境纯净度清理] ==================
echo -e "\n⏳ 正在清理旧版 Master 守护进程 (绝对安全保留 SQLite 数据库)..."
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
# =======================================================================
# 1. 环境依赖安装
echo "[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
if [ -f /etc/debian_version ]; then
@@ -47,9 +69,15 @@ CREATE TABLE IF NOT EXISTS nodes (
EOF
echo "✅ 数据库创建成功: $DB_FILE"
# ================== [v3.0.3 变更: 敏感文件权限收敛] ==================
chmod 600 "${MASTER_DIR}/master.conf"
chmod 600 "$DB_FILE"
# ====================================================================
# 4. 拉取核心调度代码并运行
echo -e "\n[4/4] 部署 TG 调度守护进程..."
curl -sL "https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main/master/tg_master.sh" -o "${MASTER_DIR}/tg_master.sh"
# [修改] 剥离了写死的网址,改用顶部的 ${REPO_RAW_URL} 变量,确保与卸载脚本的数据源同源
curl -sL "${REPO_RAW_URL}/master/tg_master.sh" -o "${MASTER_DIR}/tg_master.sh"
chmod +x "${MASTER_DIR}/tg_master.sh"
# 写入看门狗 Cron
@@ -64,4 +92,15 @@ pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/de
echo "========================================================"
echo "🎉 Master 控制中枢部署完成!"
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
echo "========================================================"
echo "========================================================"
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名不收集IP)..."
MASTER_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/master" || echo "")
if [ -n "$MASTER_COUNT" ] && [[ "$MASTER_COUNT" =~ ^[0-9]+$ ]]; then
echo -e "\033[32m✅ 感谢您成为全球第 ${MASTER_COUNT} 名 IP-Sentinel 指挥官!\033[0m"
else
echo -e "\033[32m✅ 感谢您建立 IP-Sentinel 司令部!\033[0m"
fi
echo -e "\n"

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# ==========================================================
# 脚本名称: tg_master.sh (Master 端调度枢纽 V2.0 模块化适配版)
# 脚本名称: tg_master.sh (Master 端调度枢纽 V3.0.4 动态签名版)
# 核心功能: 监听 TG、操作 SQLite、Webhook 精准调度、403权限拦截、僵尸节点清理
# ==========================================================
@@ -24,11 +24,36 @@ send_msg() {
-d "chat_id=$1" -d "text=$2" -d "parse_mode=Markdown" > /dev/null
}
# ================== [v3.0.1 新增: 消息原位刷新函数] ==================
edit_msg() {
curl -s -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
}
# 数据库执行函数
db_exec() {
sqlite3 "$DB_FILE" "$1"
}
# ================== [v3.0.4 核心: 动态 HMAC 签名生成器] ==================
# 用法: generate_signed_url <IP> <PORT> <PATH>
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 "http://${target_ip}:${target_port}${action_path}?t=${current_t}&sign=${signature}"
}
# ========================================================================
# --- 核心轮询循环 ---
while true; do
OFFSET=$(cat $OFFSET_FILE)
@@ -44,13 +69,41 @@ 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')
# ================== [v3.0.1 新增: 消除转圈圈与获取消息ID] ==================
CB_ID=$(echo "$UPDATE" | jq -r '.callback_query.id // empty')
MSG_ID=$(echo "$UPDATE" | jq -r '.callback_query.message.message_id // empty')
# 告诉 TG 官方“指令已收到”,立刻消除按钮上的加载圈圈
if [ -n "$CB_ID" ]; then
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/answerCallbackQuery" -d "callback_query_id=${CB_ID}" > /dev/null
fi
# ==========================================
# 1. 节点注册通道
# 1. 节点注册通道 (v3.0.1 终极防注入补丁)
# ==========================================
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
IFS='|' read -r MAGIC NODE_NAME AGENT_IP AGENT_PORT <<< "$REG_LINE"
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
# 🛡️ 强制字符白名单过滤:物理抹杀所有 SQL 注入特殊字符
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-') # ChatID 只能是数字和负号
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30) # 节点名限字母数字横杠下划线
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50) # IP 格式限制
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5) # 端口只能是数字
# ================== [v3.0.2 紧急加固: SSRF 内网隔离墙] ==================
# 拒绝 127.x, 10.x, 192.168.x, 172.16~31.x 以及 IPv6 回环地址的注册
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
fi
# 异常拦截:如果核心字段被过滤成了空值,说明是恶意请求,直接抛弃
if [ -z "$NODE_NAME" ] || [ -z "$AGENT_IP" ] || [ -z "$AGENT_PORT" ] || [ -z "$CHAT_ID" ]; then
send_msg "$CHAT_ID" "⛔ **安全拦截**:检测到非法注册载荷,请求已拒绝。"
continue
fi
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP) ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP;"
send_msg "$CHAT_ID" "✅ 司令部已确认!节点接入成功: \`$NODE_NAME\` ($AGENT_IP:$AGENT_PORT)"
continue
@@ -72,11 +125,29 @@ while true; do
else
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在召唤所有哨兵回传简报...**"
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
curl -s -m 5 "http://${AIP}:${APORT}/trigger_report" > /dev/null &
# 🛡️ [v3.0.4] 动态签名防重放批量下发
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_report")
curl -s -m 5 "$TARGET_URL" > /dev/null &
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
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点。"
else
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在唤醒所有哨兵执行系统维护...**"
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
# 🛡️ [v3.0.4] 动态签名防重放批量下发 (维护模块)
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_run")
curl -s -m 5 "$TARGET_URL" > /dev/null &
done
fi
;;
# ====================================================================
"list_nodes")
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
if [ -z "$NODE_LIST" ]; then
@@ -92,14 +163,18 @@ while true; do
;;
manage:*)
TARGET_NODE=${TEXT#*:}
# 🛡️ 强制过滤节点名,防止面板渲染时发生 XSS 或注入
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
# 【核心升级】拆分下发按钮,精准对应 Google 与 Trust 两个模块,并排版为 3 行 2 列
BTNS="[[{\"text\":\"📍 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}], [{\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回主列表\",\"callback_data\":\"list_nodes\"}]]"
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_NODE\`\n请选择战术动作" "$BTNS"
;;
del:*)
TARGET_NODE=${TEXT#*:}
# 🛡️ 提取并强制过滤节点名与 CHAT_ID 防注入
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
send_msg "$CHAT_ID" "🗑️ 节点 \`$TARGET_NODE\` 的档案已从司令部彻底销毁!"
@@ -118,32 +193,50 @@ while true; do
# 【核心升级】增加拦截规则,支持 google 和 trust 前缀
google:*|trust:*|run:*|report:*|log:*)
ACTION_TYPE=$(echo "$TEXT" | cut -d':' -f1)
TARGET_NODE=$(echo "$TEXT" | cut -d':' -f2)
# 🛡️ 提取并强制过滤动作参数、节点名与 CHAT_ID
ACTION_TYPE=$(echo "$TEXT" | cut -d':' -f1 | tr -cd 'a-z')
TARGET_NODE=$(echo "$TEXT" | cut -d':' -f2 | 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)
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
# 触发 Webhook
RESPONSE=$(curl -s -m 5 "http://${AGENT_IP}:${AGENT_PORT}/trigger_${ACTION_TYPE}" || echo "FAILED")
# 【核心升级】处理 Agent 传回来的 403 拦截状态码
if [ "$RESPONSE" == "FAILED" ]; then
send_msg "$CHAT_ID" "❌ 指令下发超时或失败!请检查节点公网 IP 或防火墙端口 ($AGENT_PORT) 是否放行。"
elif [[ "$RESPONSE" == *"403"* ]]; then
send_msg "$CHAT_ID" "⚠️ **拒绝执行**:该节点未在本地开启此模块,请检查安装时的配置!"
# [v3.0.2 防刷屏] 原位刷新菜单为等待状态
if [ -n "$MSG_ID" ]; then
edit_msg "$CHAT_ID" "$MSG_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
else
# 细化成功提示
if [ "$ACTION_TYPE" == "google" ] || [ "$ACTION_TYPE" == "run" ]; then
send_msg "$CHAT_ID" "✅ 节点 \`$TARGET_NODE\` 回应: 📍 Google 纠偏程序启动。"
elif [ "$ACTION_TYPE" == "trust" ]; then
send_msg "$CHAT_ID" "✅ 节点 \`$TARGET_NODE\` 回应: 🛡️ IP 信用净化程序启动。"
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 -s -m 5 "$TARGET_URL" || echo "FAILED")
# 结果判定
if [ "$RESPONSE" == "FAILED" ]; then
TEXT_RES="❌ 指令下发超时或失败!请检查节点公网 IP 或防火墙端口 ($AGENT_PORT) 是否放行。"
elif [[ "$RESPONSE" == *"403"* ]]; then
TEXT_RES="⚠️ **拒绝执行**:该节点未在本地开启此模块,请检查安装时的配置!"
else
if [ "$ACTION_TYPE" == "google" ] || [ "$ACTION_TYPE" == "run" ]; then
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 回应: 📍 Google 纠偏程序启动。"
elif [ "$ACTION_TYPE" == "trust" ]; then
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 回应: 🛡️ IP 信用净化程序启动。"
elif [ "$ACTION_TYPE" == "log" ]; then
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 正在抓取日志..."
else
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 接收指令: $ACTION_TYPE"
fi
fi
# [v3.0.1 防刷屏] 将等待状态刷新为最终结果
if [ -n "$MSG_ID" ]; then
edit_msg "$CHAT_ID" "$MSG_ID" "$TEXT_RES"
else
send_msg "$CHAT_ID" "$TEXT_RES"
fi
else
send_msg "$CHAT_ID" "❌ 数据库中未找到该节点的通讯地址。"
fi

View File

@@ -0,0 +1,39 @@
#!/bin/bash
# ==========================================================
# 脚本名称: uninstall_master.sh (IP-Sentinel Master 一键卸载脚本)
# 核心功能: 终止调度进程、清理看门狗定时任务、抹除数据库与配置
# ==========================================================
MASTER_DIR="/opt/ip_sentinel_master"
echo "========================================================"
echo " 🗑️ 准备卸载 IP-Sentinel Master (控制中枢)"
echo "========================================================"
echo -e "\n⚠ 警告: 此操作将永久删除包含所有节点档案的 SQLite 数据库!"
read -p "确定要继续卸载吗?(y/n) [默认 n]: " CONFIRM_DEL
if [[ ! "$CONFIRM_DEL" =~ ^[Yy]$ ]]; then
echo "已取消卸载操作。"
exit 0
fi
# 1. 停止运行中的 Master 守护进程
echo "[1/3] 正在终止后台中枢调度进程..."
pgrep -f tg_master.sh | xargs -r kill -9 >/dev/null 2>&1
# 2. 清除看门狗定时任务 (Cron)
echo "[2/3] 正在清理系统定时任务 (Cron)..."
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_backup
crontab /tmp/cron_backup
rm -f /tmp/cron_backup
# 3. 删除所有文件、配置与数据库
echo "[3/3] 正在抹除核心程序、配置文件与 SQLite 数据库..."
if [ -d "$MASTER_DIR" ]; then
rm -rf "$MASTER_DIR"
fi
echo "========================================================"
echo "✅ 卸载彻底完成Master 司令部已从您的系统中无痕移除。"
echo "========================================================"

75
telemetry/worker.js Normal file
View File

@@ -0,0 +1,75 @@
// IP-Sentinel Glasshouse Telemetry (全透明装机量统计中枢)
// 部署环境: Cloudflare Workers + KV
// 隐私声明: 绝对不采集、不存储用户的 IP 地址、Header、Token 及任何系统特征参数。仅做纯粹的原子累加。
export default {
async fetch(request, env) {
const url = new URL(request.url);
const path = url.pathname;
// 全局跨域头,确保 GitHub README 的 Shields.io 徽章能正常读取
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET",
};
// 核心原子操作:无情的 +1 机器
async function incrementCounter(key) {
let count = await env.SENTINEL_KV.get(key);
count = count ? parseInt(count) + 1 : 1;
await env.SENTINEL_KV.put(key, count.toString());
return count;
}
async function getCounter(key) {
let count = await env.SENTINEL_KV.get(key);
return count ? parseInt(count) : 0;
}
try {
// 1. Agent (哨兵) 部署触发接口
if (path === '/ping/agent') {
const count = await incrementCounter('agent_count');
return new Response(count.toString(), { headers: corsHeaders });
}
// 2. Master (指挥部) 部署触发接口
if (path === '/ping/master') {
const count = await incrementCounter('master_count');
return new Response(count.toString(), { headers: corsHeaders });
}
// 3. GitHub README Agent 徽章接口 (输出给 Shields.io)
if (path === '/stats/agent') {
const count = await getCounter('agent_count');
const shield = {
schemaVersion: 1,
label: "Agent Nodes",
message: count.toString(),
color: "blue"
};
return new Response(JSON.stringify(shield), {
headers: { ...corsHeaders, "Content-Type": "application/json" }
});
}
// 4. GitHub README Master 徽章接口 (输出给 Shields.io)
if (path === '/stats/master') {
const count = await getCounter('master_count');
const shield = {
schemaVersion: 1,
label: "Master Commands",
message: count.toString(),
color: "orange"
};
return new Response(JSON.stringify(shield), {
headers: { ...corsHeaders, "Content-Type": "application/json" }
});
}
return new Response("IP-Sentinel Glasshouse Telemetry API (No IP Logged, 100% Transparent)", { status: 200 });
} catch (err) {
return new Response("Error", { status: 500 });
}
}
};