Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c7491449c | ||
|
|
f698bc4b92 | ||
|
|
c18a10dbd5 | ||
|
|
69e7803c40 | ||
|
|
b2a5afe562 | ||
|
|
45fda5f498 | ||
|
|
1459d5efec | ||
|
|
b7375e5e7d | ||
|
|
4a7f88a0da | ||
|
|
a0ec759dd7 | ||
|
|
fe1e4c0e6f | ||
|
|
62770ba50c | ||
|
|
11bdece9e2 | ||
|
|
2c5df879c0 | ||
|
|
862de2e49e | ||
|
|
26e3fd435d | ||
|
|
e279fc6e38 | ||
|
|
acff304a35 | ||
|
|
9fe39fc274 | ||
|
|
9eccace3bc | ||
|
|
cf410fce61 | ||
|
|
0ffb173bb5 | ||
|
|
edd6425fe5 | ||
|
|
16b6c3f20a | ||
|
|
e3750f4565 | ||
|
|
d3face9621 | ||
|
|
aeeb9e1a43 | ||
|
|
09c6bab04a | ||
|
|
2e73aa8833 | ||
|
|
03d88f4856 | ||
|
|
3cab2b2379 | ||
|
|
87faeae8f7 | ||
|
|
19f0bb7c36 | ||
|
|
01305f98b5 | ||
|
|
a3ae3b29f9 | ||
|
|
053c9805d4 | ||
|
|
be51aab0e0 | ||
|
|
426fa74ff1 | ||
|
|
9904246e45 | ||
|
|
06f52cbe1d | ||
|
|
33d75925a6 | ||
|
|
52fe92efe5 | ||
|
|
7fec21280f | ||
|
|
84b5fef640 | ||
|
|
9d2aa9fcd5 | ||
|
|
ca2608756d | ||
|
|
27ebcfd418 | ||
|
|
62deadda1e | ||
|
|
990d60f63a | ||
|
|
0571fcfd32 | ||
|
|
4a77cb66d4 | ||
|
|
26fdb8f899 | ||
|
|
5635c5d851 | ||
|
|
00ec07b88a | ||
|
|
ef84979f91 | ||
|
|
d63df0c48e | ||
|
|
002e5f304d | ||
|
|
4bfe9ea9ca | ||
|
|
b7988dc666 | ||
|
|
5463372b66 | ||
|
|
85525580c6 | ||
|
|
c1cfdb17f3 | ||
|
|
f8b4777ed8 | ||
|
|
f7bbfe9010 | ||
|
|
690aa2c189 | ||
|
|
989db0b49b | ||
|
|
90fc8adb74 | ||
|
|
1b7d7e7b64 | ||
|
|
472ead88b0 | ||
|
|
cb788ab730 |
33
.github/workflows/ua_factory.yml
vendored
Normal file
33
.github/workflows/ua_factory.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Automated Massive UA Factory
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 每个月 1 号凌晨 00:00 执行
|
||||
- cron: '0 0 1 * *'
|
||||
workflow_dispatch: # 允许手动点击运行
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # 必须赋予写入权限,否则无法更新仓库文件
|
||||
|
||||
steps:
|
||||
- name: 📥 Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: ⚙️ Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: 🚀 Run UA Factory Generator
|
||||
run: python scripts/ua_generator.py
|
||||
|
||||
- name: 📤 Commit and Push to Main
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add data/user_agents.txt
|
||||
# 只有在文件内容确实发生变化时才执行提交
|
||||
git diff --quiet && git diff --staged --quiet || (git commit -m "chore(data): 🤖 自动机兵:刷新 4000 条绝对坐标指纹库" && git push)
|
||||
86
README.md
86
README.md
@@ -1,19 +1,42 @@
|
||||
# 🛡️ IP-Sentinel (分布式 IP 哨兵集群)
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
> **一个极度轻量、零感知、支持中枢遥控的 VPS IP 自动化养护与区域纠偏引擎。**
|
||||
|
||||
📢 官方战术交流频道: 🛰️ [IP-Sentinel Matrix](https://t.me/IP_Sentinel_Matrix)
|
||||
|
||||
专为解决 VPS IPv4 被 Google 等数据库错误定位到中国大陆/香港(俗称“送中”)等问题而生。IP-Sentinel 已从单机脚本全面跃升为 **Master-Agent 分布式架构**。它像影子一样潜伏在全球各地的服务器后台,通过高度拟真的真实用户行为为你默默积累 IP 权重,并允许你通过 Telegram 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
|
||||
专为解决 VPS IP 被 Google 等数据库错误定位到中国大陆/香港(俗称“送中”)等问题而生。IP-Sentinel 已从单机脚本全面跃升为 **Master-Agent 分布式架构**。它像影子一样潜伏在全球各地的服务器后台,通过高度拟真的真实用户行为为你默默积累 IP 权重,并允许你通过 Telegram 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
|
||||
|
||||
## ✨ 核心极客特性
|
||||
|
||||
* ☁️ **云端中枢 (Public Master)**:**v2.1.0 新特性**。引入官方公共机器人 [@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 转发授权机制,杜绝野生节点恶意接入。
|
||||
* 👻 **高仿真人类行为 (Human-Like)**:摒弃死板的 Ping/Curl,引入单次会话指纹锁定、10 米级 GPS 坐标微抖动、以及 60~150 秒的真实阅读停顿拉伸,完美避开 AI 封控。
|
||||
* 📡 **OTA 静默进化 (Smart Updates)**:系统每周日凌晨自动从云端拉取最新的“热搜词汇”和“真实设备指纹池”,确保养护行为与时俱进、永不过时。
|
||||
- 🗺️ **全球拓扑矩阵 (Global Nexus)**:v3.1 跨洲际跃升。守护版图现已横跨亚、欧、美三大洲(美、日、英、德、法、新、港)。为每个国家注入极其硬核的“原生本地化”搜索词库与本土高权重站点(如政府、权威媒体、高铁网),真正实现“拟真融入”。
|
||||
|
||||
- 👻 **设备资产持久化 (Hash-Seeded Persona)**:v3.2 核心换代。彻底摒弃传统的“随机抽取指纹”,引入基于节点物理 IP 的哈希锚定引擎。利用不可变哈希种子,为您的每台 VPS 在千万级指纹库中永久锁定 3 个绝对专属设备(如固定表现为 1台 Mac、1台 iPhone、1台 PC 交替上网)。完美构建高权重真实家庭内网画像,根除“僵尸网络”同质化特征!
|
||||
|
||||
- 🏭 **自动化指纹兵工厂 (Automated UA Factory)**:依托 GitHub Actions CI/CD 流水线,每月 1 日无人值守全自动生成 4000+ 带绝对物理分区的真实终端设备数据。配合边缘节点的守护进程静默拉取,实现千万级指纹资产的“自动驾驶”级演进。
|
||||
|
||||
- 🖧 **底层路由死锁 (Hard-Bind Routing)**:v3.2.1 热修复升级。底层探测引擎强力接管 curl 核心参数 (--interface),强制将发出的每一滴伪装流量死死绑定在您设定的物理网卡或隧道 IP 上,彻底杜绝双栈或多网卡环境下的流量溢出漏洞。
|
||||
|
||||
- 🎯 **多级容灾与高精度探针 (High-Precision Probe)**:v3.2.2 底层重构。重写战报模块与底层协议自适应逻辑,植入多级 ISP 容灾探针链路,并按“底层数据共识原则”智能清洗冗余 AS 号。确保在纯 V6、隧道或弱网环境下,数据获取依然 100% 精准畅通。
|
||||
|
||||
- 🔄 **平滑热更新装甲 (Smooth Upgrade Engine)**:v3.2.2 体验进化。全系植入状态机嗅探逻辑。无论是 Master 司令部还是 Agent 边缘节点,再次执行部署脚本时将自动识别并继承历史配置、SQLite 数据库与锚定 IP,一键回车即可瞬间完成无损换代,告别繁琐的重复配置。
|
||||
|
||||
- ☁️ **云端中枢 (Public Master)**:引入官方公共机器人 @OmniBeacon_bot,新手无需部署 Master 司令部,部署 Agent 时一键回车即可调用官方加密网关,30 秒极速入伍!
|
||||
|
||||
- 🧠 **分布式中枢 (Master-Agent)**:对于硬核极客,支持私有化部署。一台 Master 主控集成 SQLite 数据库,统管无数台 Agent 边缘节点,确保数据绝对私有。
|
||||
|
||||
- 🔒 **叹息之墙 (Zero-Trust HMAC)**:全面废弃明文 Token,底层通讯引入 时间戳 + HMAC-SHA256 军用级动态签名。指令有效期仅 60 秒(阅后即焚),彻底免疫中间人抓包、重放攻击与端口爆破。
|
||||
|
||||
- 🛡️ **工业级并发与自净引擎**:底层 Webhook 采用多线程模型彻底免疫慢速耗尽攻击;独创“智能清道夫”逻辑,覆盖安装/升级时自动绞杀僵尸进程与冗余定时任务,绝对纯净,告别玄学冲突。
|
||||
|
||||
- 🎮 **TG 战术面板 (Command Center)**:无需记忆繁琐命令,全 Inline Keyboard 交互。支持一键下发伪装指令、一键索要精准战报、毫秒级抓取边缘节点实时运行日志。
|
||||
|
||||
- 👁️🗨️ **玻璃房透明遥测 (Glasshouse Telemetry)**:引入基于 Cloudflare Workers 的全透明计数中枢,首页动态徽章实时展示全球真实装机与调用量。绝对零隐私收集,仅作原子累加,底层网关源码全开源,接受全网极客审计。
|
||||
|
||||
- ⚡ **丝滑战术交互 (Seamless UI)**:司令部交互面板像素级打磨。新节点发送暗号入伍成功后,司令部将无缝零延迟自动呼出最新的活跃节点阵列面板,彻底免除重复输入命令的繁琐,掌控感拉满。
|
||||
|
||||
## 📂 项目架构 (Monorepo)
|
||||
|
||||
@@ -21,17 +44,21 @@
|
||||
|
||||
```text
|
||||
📦 IP-Sentinel
|
||||
┣ 📂 .github/workflows/ # 🏭 自动化兵工厂:每月定时触发指纹生成的 CI/CD 流水线
|
||||
┣ 📂 master/ # 🧠 司令部:SQLite 存储、TG 监听与 Webhook 调度中心
|
||||
┣ 📂 core/ # 🛡️ 边缘哨兵:Webhook 被动监听、高拟真养护引擎
|
||||
┗ 📂 data/ # 🗂️ 全球数据规则库
|
||||
┣ 📂 regions/ # 🧊 冷数据:各地区 GPS 基准配置 (固化)
|
||||
┣ 📂 keywords/ # 🔥 热数据:动态搜索词库 (OTA 自动更新)
|
||||
┗ 📜 user_agents.txt # 🔥 热数据:全局真实设备指纹池
|
||||
┣ 📂 core/ # 🛡️ 边缘哨兵:Webhook 被动监听、哈希锚定执行引擎
|
||||
┣ 📂 scripts/ # 🐍 兵工厂引擎:基于 Python 的多物理分区 UA 生成器
|
||||
┣ 📂 data/ # 🗂️ 全球数据规则库 (动态拓扑)
|
||||
┃ ┣ 📜 map.json # 🌐 全球区域索引大脑 (Master Index)
|
||||
┃ ┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
|
||||
┃ ┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
|
||||
┃ ┗ 📜 user_agents.txt # 🔥 热数据:由兵工厂每月锻造的绝对坐标专属设备库
|
||||
┗ 📂 telemetry/ # 👁️🗨️ 玻璃房计划:Cloudflare Workers 透明计数器网关源码
|
||||
```
|
||||
|
||||
## 🚀 极速部署 (Quick Start)
|
||||
|
||||
v2.1.0 提供了两种接入模式,请根据您的战术需求选择:
|
||||
v3.2.x 提供了两种接入模式,请根据您的战术需求选择:
|
||||
|
||||
### 🔹 模式 A:官方公共模式 (最简、推荐)
|
||||
**适合不想折腾、只想快速养护 IP 的新兵。**
|
||||
@@ -39,7 +66,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# 暗号,将其转发给机器人即可完成入库。
|
||||
|
||||
@@ -53,16 +81,34 @@ bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master
|
||||
```
|
||||
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. **激活节点**:同上,将暗号转发给您自己的机器人即可。
|
||||
|
||||
### ⚠️ 平滑升级指引 (Upgrade to v3.2.2)
|
||||
|
||||
得益于 **v3.2.2 全新引入的平滑热更新引擎 (Smooth Upgrade Engine)**,系统升级现已变得极其优雅与安全。
|
||||
|
||||
无需卸载旧版本,无论您是要升级 Agent 边缘节点还是 Master 控制中枢,只需在您的终端中**再次运行上方对应的官方部署指令**。
|
||||
|
||||
安装雷达会自动嗅探您的历史部署状态(包括您的 Token、区域设定、SQLite 数据库及物理网卡锚点)。当询问是否平滑升级时,您只需**一路回车 (默认选 y)**,脚本将在短短 3 秒内瞬间完成核心装甲的无损换脑手术,您的所有战术资产将得到 100% 保留!
|
||||
|
||||
🗑️ 一键无痕卸载
|
||||
如果你需要清理某个边缘节点,只需重新运行 core/install.sh 并选择 [3],或直接在节点终端执行:
|
||||
如果你需要清理某个边缘节点,只需重新运行 `core/install.sh` 并选择 **[2]**,或直接在节点终端执行:
|
||||
|
||||
```Bash
|
||||
bash /opt/ip_sentinel/core/uninstall.sh
|
||||
|
||||
```
|
||||
|
||||
### 🧓 传家宝老旧系统专用通道 (Debian 9)
|
||||
|
||||
如果你的小鸡系统版本过低(如 Debian 9),由于官方 APT 源已关闭且 Python 版本过旧,无法使用主线版本,请使用 **Legacy 兼容分支** 部署。
|
||||
*(注意:该分支仅作基础维护,不享受新功能迭代,请尽可能升级你的系统)*
|
||||
|
||||
```bash
|
||||
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/legacy/core/install.sh)
|
||||
```
|
||||
|
||||
📡 战术联络 (Community)
|
||||
@@ -71,8 +117,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
|
||||
[](https://starchart.cc/hotyue/IP-Sentinel)
|
||||
@@ -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: 智能防打扰注册机制] ---
|
||||
@@ -34,9 +41,10 @@ if [ -n "$AGENT_IP" ]; then
|
||||
|
||||
# 只有当这是第一次运行,或者公网 IP 发生变动时,才发送 Telegram 申请
|
||||
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}\`"
|
||||
# V3.1.3 协议升级: 在底部暗号中精准嵌入 ${REGION_CODE} 大区标识
|
||||
REG_MSG="👋 **[边缘节点接入申请]**%0A大区: \`${REGION_CODE}\`%0A节点: \`${NODE_NAME}\`%0A地址: \`${AGENT_IP}:${AGENT_PORT}\`%0A%0A⚠️ **安全验证**: 为防止非法节点接入,请长按复制下方代码,并**发送给我**以完成最终授权录入:%0A%0A\`#REGISTER#|${REGION_CODE}|${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 +56,91 @@ 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
|
||||
|
||||
# 校验 3:HMAC 数据完整性与身份合法性校验
|
||||
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
|
||||
|
||||
# ================== 路由分发 (恢复为安全的精确匹配) ==================
|
||||
|
||||
# 路由 0: 全局统筹调度 (处理 /trigger_run 一键全节点维护)
|
||||
if req_path == '/trigger_run':
|
||||
if os.path.exists('/opt/ip_sentinel/core/runner.sh'):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: runner\n")
|
||||
subprocess.Popen(['bash', '/opt/ip_sentinel/core/runner.sh'])
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
# 路由 1: Google 区域纠偏
|
||||
elif req_path == '/trigger_google':
|
||||
if os.path.exists('/opt/ip_sentinel/core/mod_google.sh'):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
@@ -75,7 +154,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 +168,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 +176,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 +226,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: 真正的静默后台启动] ---
|
||||
|
||||
483
core/install.sh
483
core/install.sh
@@ -1,12 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v2.1.0)
|
||||
# 核心功能: 区域选择、模块按需开启、官方机器人一键配置
|
||||
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v3.2.2 - 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,79 +51,290 @@ if [ "$REGION_CHOICE" == "3" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 正常安装流程匹配区域
|
||||
case ${REGION_CHOICE:-1} in
|
||||
2) REGION_CODE="US" ;;
|
||||
*) REGION_CODE="JP" ;;
|
||||
esac
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_LOGS="true"
|
||||
|
||||
# 本地工作目录初始化
|
||||
mkdir -p "${INSTALL_DIR}/core"
|
||||
mkdir -p "${INSTALL_DIR}/data/keywords"
|
||||
mkdir -p "${INSTALL_DIR}/data/regions"
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
|
||||
# 3. 功能模块前置开关 (按需加载)
|
||||
echo -e "\n[3/7] 请选择需要开启的养护模块 (按需开启,节省资源):"
|
||||
echo " 1) 📍 仅开启 [Google 区域纠偏] (默认,适合流媒体解锁机位漂移)"
|
||||
echo " 2) 🛡️ 仅开启 [IP 信用净化] (适合高风险机房 IP 降低 Scamalytics 分数)"
|
||||
echo " 3) 🔥 双管齐下 (同时开启以上两项)"
|
||||
read -p "请输入选择 [1-3] (默认1): " MODULE_CHOICE
|
||||
|
||||
ENABLE_GOOGLE="true"
|
||||
ENABLE_TRUST="false"
|
||||
case ${MODULE_CHOICE:-1} in
|
||||
2) ENABLE_GOOGLE="false"; ENABLE_TRUST="true" ;;
|
||||
3) ENABLE_GOOGLE="true"; ENABLE_TRUST="true" ;;
|
||||
*) ENABLE_GOOGLE="true"; ENABLE_TRUST="false" ;;
|
||||
esac
|
||||
|
||||
# 4. 接入 Master 中枢配置
|
||||
echo -e "\n[4/7] 是否接入 Master 司令部?(需要配置与主控相同的 TG 机器人) (y/n)"
|
||||
read -p "请输入选择 [y/n] (默认n): " TG_CHOICE
|
||||
TG_TOKEN=""
|
||||
CHAT_ID=""
|
||||
AGENT_PORT="9527"
|
||||
if [[ "$TG_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
echo -e "\n\033[33m💡 提示:您可以选择使用自己的机器人,或者直接回车使用官方公共机器人。\033[0m"
|
||||
echo -e "\033[33m⚠️ 注意:若使用官方机器人,请务必先在 TG 中关注 @OmniBeacon_bot 并发送 /start\033[0m"
|
||||
|
||||
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"
|
||||
echo -e "\033[33m👉 请确保您已关注官方机器人并发送过 /start,否则将无法接收消息。\033[0m"
|
||||
if [ "$ACTION_CHOICE" == "1" ] && [ -f "$CONFIG_FILE" ]; then
|
||||
echo -e "\n\033[33m💡 哨兵雷达提示:检测到本机已部署过 IP-Sentinel。\033[0m"
|
||||
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
|
||||
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
UPGRADE_MODE="true"
|
||||
read -p "👉 是否保留历史运行日志?(y/n, 默认y): " LOG_CHOICE
|
||||
if [[ "$LOG_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
KEEP_LOGS="false"
|
||||
fi
|
||||
|
||||
# 将原配置读入环境变量,为后续跳过配置步骤提供燃料
|
||||
source "$CONFIG_FILE"
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],即将跳过基础配置,直接更新核心装甲...\033[0m"
|
||||
else
|
||||
TG_TOKEN="$USER_TOKEN"
|
||||
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的哨兵数据将被彻底抹除。\033[0m"
|
||||
fi
|
||||
fi
|
||||
# ====================================================================
|
||||
|
||||
# ================== [v3.1.1/v3.2.2 优化: 安装前环境纯净度清理] ==================
|
||||
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. 抹除旧版核心代码,杜绝代码冲突 (根据模式分流)
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
# 升级模式:仅销毁核心引擎,严格保留 config 与 data
|
||||
rm -rf "${INSTALL_DIR}/core" 2>/dev/null
|
||||
if [ "$KEEP_LOGS" == "false" ]; then
|
||||
rm -rf "${INSTALL_DIR}/logs" 2>/dev/null
|
||||
echo -e "🗑️ 历史日志已按指令清空。"
|
||||
else
|
||||
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📍 【第一级】请选择目标国家/地区:\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 "\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"
|
||||
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)
|
||||
|
||||
# 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"
|
||||
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
|
||||
|
||||
if [ ! -s "$REGION_JSON_FILE" ]; then
|
||||
echo "❌ 拉取或解析规则失败!请检查 Forgejo 仓库是否公开或网络是否畅通。"
|
||||
exit 1
|
||||
fi
|
||||
# 清理临时文件
|
||||
rm -f /tmp/map.json /tmp/countries.txt /tmp/states.txt /tmp/cities.txt
|
||||
|
||||
# 使用 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.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"
|
||||
|
||||
# 写入本地静态配置文件
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
# 3. 功能模块前置开关 (按需加载)
|
||||
echo -e "\n[3/7] 请选择需要开启的养护模块 (按需开启,节省资源):"
|
||||
echo " 1) 📍 仅开启 [Google 区域纠偏] (默认,适合流媒体解锁机位漂移)"
|
||||
echo " 2) 🛡️ 仅开启 [IP 信用净化] (适合高风险机房 IP 降低 Scamalytics 分数)"
|
||||
echo " 3) 🔥 双管齐下 (同时开启以上两项)"
|
||||
read -p "请输入选择 [1-3] (默认1): " MODULE_CHOICE
|
||||
|
||||
ENABLE_GOOGLE="true"
|
||||
ENABLE_TRUST="false"
|
||||
case ${MODULE_CHOICE:-1} in
|
||||
2) ENABLE_GOOGLE="false"; ENABLE_TRUST="true" ;;
|
||||
3) ENABLE_GOOGLE="true"; ENABLE_TRUST="true" ;;
|
||||
*) ENABLE_GOOGLE="true"; ENABLE_TRUST="false" ;;
|
||||
esac
|
||||
|
||||
# 4. 接入 Master 中枢配置
|
||||
echo -e "\n[4/7] 是否接入 Master 司令部?(需要配置与主控相同的 TG 机器人) (y/n)"
|
||||
read -p "请输入选择 [y/n] (默认n): " TG_CHOICE
|
||||
TG_TOKEN=""
|
||||
CHAT_ID=""
|
||||
AGENT_PORT="9527"
|
||||
if [[ "$TG_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
echo -e "\n\033[33m💡 提示:您可以选择使用自己的机器人,或者直接回车使用官方公共机器人。\033[0m"
|
||||
echo -e "\033[33m⚠️ 注意:若使用官方机器人,请务必先在 TG 中关注 @OmniBeacon_bot 并发送 /start\033[0m"
|
||||
|
||||
read -p "请输入您的 Telegram Bot Token (回车使用官方默认): " USER_TOKEN
|
||||
|
||||
if [ -z "$USER_TOKEN" ]; then
|
||||
TG_TOKEN="OFFICIAL_GATEWAY_MODE"
|
||||
TG_API_URL="https://omni-gateway.samanthaestime296.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
|
||||
|
||||
# ================== [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] 正在从云端数据仓库拉取 [${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 仓库是否公开或网络是否畅通。"
|
||||
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")
|
||||
|
||||
# 写入本地静态配置文件
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
# IP-Sentinel 本地固化配置 (生成时间: $(date '+%Y-%m-%d %H:%M:%S'))
|
||||
REGION_CODE="$REGION_CODE"
|
||||
REGION_NAME="$REGION_NAME"
|
||||
@@ -129,14 +348,30 @@ 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"
|
||||
# ====================================================================
|
||||
|
||||
fi
|
||||
# 🛑 拦截块结束 (全套交互配置跳过完毕)
|
||||
|
||||
# 6. 拉取全套组件 (按需下载,绝不浪费空间)
|
||||
echo -e "\n[6/7] 正在根据模块开关部署核心引擎与热数据..."
|
||||
# 确保目录在升级模式下也能被正确建立
|
||||
mkdir -p "${INSTALL_DIR}/core"
|
||||
mkdir -p "${INSTALL_DIR}/data/keywords"
|
||||
|
||||
# 基础公共组件
|
||||
curl -sL "${REPO_RAW_URL}/core/runner.sh" -o "${INSTALL_DIR}/core/runner.sh"
|
||||
curl -sL "${REPO_RAW_URL}/core/updater.sh" -o "${INSTALL_DIR}/core/updater.sh"
|
||||
@@ -148,7 +383,13 @@ 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"
|
||||
# [v3.2.2 修复] 动态匹配词库下载逻辑
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
curl -sL "${REPO_RAW_URL}/data/keywords/${KEYWORD_FILE}" -o "${INSTALL_DIR}/data/keywords/${KEYWORD_FILE}"
|
||||
else
|
||||
# 升级模式:利用已有的 REGION_CODE 更新通用词库
|
||||
curl -sL "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$ENABLE_TRUST" == "true" ]; then
|
||||
@@ -171,6 +412,11 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
# 每天早上 8 点发送昨天的统计战报
|
||||
echo "0 8 * * * ${INSTALL_DIR}/core/tg_report.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||
|
||||
# [v3.0.1新增修改 3: 删除原来的 curl 取 IP,直接使用我们上方锁定的 BIND_IP]
|
||||
# 并提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的重复推送
|
||||
# [修复竞态]: 提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的抢跑推送
|
||||
echo "$BIND_IP" > "${INSTALL_DIR}/core/.last_ip"
|
||||
|
||||
# 双保险守护进程看门狗
|
||||
echo "@reboot nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
||||
echo "* * * * * nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
||||
@@ -182,41 +428,84 @@ fi
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
|
||||
# ================== [v3.2.2 优化: 战报通知分流 (注册/升级)] ==================
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
echo -e "\n📡 正在向指挥部发送注册暗号..."
|
||||
NODE_NAME=$(hostname | cut -c 1-15)
|
||||
|
||||
# 获取公网 IP
|
||||
PUBLIC_IP=$(curl -s https://api64.ipify.org || curl -s https://ifconfig.me || echo "未知IP")
|
||||
|
||||
# 构造注册暗号
|
||||
REG_MSG="#REGISTER#:${REGION_NAME}:${PUBLIC_IP}:${AGENT_PORT}"
|
||||
|
||||
# 执行主动推送
|
||||
PUSH_RESULT=$(curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${CHAT_ID}" \
|
||||
-d "parse_mode=Markdown" \
|
||||
-d "text=✨ *IP-Sentinel 部署成功!*
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo -e "\n📡 正在向指挥部发送升级成功战报..."
|
||||
curl -s -X POST "${TG_API_URL}" \
|
||||
-d "chat_id=${CHAT_ID}" \
|
||||
-d "parse_mode=Markdown" \
|
||||
-d "text=✨ *IP-Sentinel 引擎热更新完成!*
|
||||
📍 节点:\`${NODE_NAME}\`
|
||||
🌐 IP:\`${BIND_IP}\`
|
||||
🚀 状态:v3.2.2 协议自适应与高精度探针已就绪" >/dev/null 2>&1
|
||||
echo -e "\033[32m✅ 升级成功通知已推送到您的 Telegram!\033[0m"
|
||||
else
|
||||
echo -e "\n📡 正在向指挥部发送注册暗号..."
|
||||
# 构造注册暗号 (V3.1.3 协议升级: 携带 REGION_CODE 大区标识)
|
||||
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${BIND_IP}|${AGENT_PORT}"
|
||||
|
||||
# 执行主动推送
|
||||
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}
|
||||
|
||||
🔑 *请点击下方指令复制并回复给机器人:*
|
||||
\`${REG_MSG}\`")
|
||||
|
||||
if echo "$PUSH_RESULT" | grep -q '"ok":true'; then
|
||||
echo -e "\033[32m✅ 注册信息已推送到您的 Telegram,请按指令完成最终激活!\033[0m"
|
||||
else
|
||||
echo -e "\033[31m❌ 消息推送失败,请检查 Chat ID 是否正确或是否已关注机器人。\033[0m"
|
||||
if echo "$PUSH_RESULT" | grep -q '"ok":true'; then
|
||||
echo -e "\033[32m✅ 注册信息已推送到您的 Telegram,请按指令完成最终激活!\033[0m"
|
||||
else
|
||||
echo -e "\033[31m❌ 消息推送失败,请检查 Chat ID 是否正确或是否已关注机器人。\033[0m"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# =========================================================================
|
||||
|
||||
echo "========================================================"
|
||||
echo "🎉 边缘节点 (Agent) 部署流程彻底完成!"
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo "🎉 边缘节点 (Agent) 平滑热更新已彻底完成!"
|
||||
else
|
||||
echo "🎉 边缘节点 (Agent) 部署流程彻底完成!"
|
||||
fi
|
||||
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"
|
||||
@@ -48,11 +48,32 @@ 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[@]}]}
|
||||
# -----------------------------------------------------------
|
||||
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
|
||||
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
|
||||
# -----------------------------------------------------------
|
||||
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 公里内随机生成本次上网的“固定咖啡馆”坐标
|
||||
SESSION_BASE_LAT=$(get_random_coord $BASE_LAT 270)
|
||||
SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
|
||||
@@ -60,10 +81,29 @@ 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"
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# [V3.2.1 热修复] 网络锚定与协议自适应构建
|
||||
# 强制 curl 绑定网卡,并自动匹配 IPv4/v6 协议,杜绝 curl 冲突报错
|
||||
# -----------------------------------------------------------
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- [行为循环模拟] ---
|
||||
for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
# 模拟真实移动设备拿在手里时的 GPS 信号微抖动 (范围约 10 米)
|
||||
@@ -77,21 +117,22 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
# 随机选择一种上网行为
|
||||
ACTION_TYPE=$((1 + RANDOM % 4))
|
||||
|
||||
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
|
||||
case $ACTION_TYPE in
|
||||
1) # 搜索行为
|
||||
CODE=$(curl -4 -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
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) # 浏览本土新闻
|
||||
CODE=$(curl -4 -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
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) # 地图坐标查询
|
||||
CODE=$(curl -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}")
|
||||
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) # 触发移动端系统底层位置检测像素
|
||||
CODE=$(curl -4 -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
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")
|
||||
;;
|
||||
esac
|
||||
@@ -107,16 +148,45 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
fi
|
||||
done
|
||||
|
||||
# --- [结果纠偏自检] ---
|
||||
# 去掉所有语言参数,进行一次最干净的直连测试
|
||||
FINAL_URL=$(curl -4 -m 15 -s -L -o /dev/null -w "%{url_effective}" https://www.google.com)
|
||||
# --- [结果纠偏自检 (V3.2.1 高精度容错版)] ---
|
||||
# [V3.2.1 热修复] 探针同样应用 $DYNAMIC_IP_PREF 协议自适应
|
||||
PROBE_RESULT=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}|%{url_effective}" https://www.google.com)
|
||||
|
||||
if [[ "$FINAL_URL" == *"$VALID_URL_SUFFIX"* ]]; then
|
||||
STATUS="✅ 目标区域达成 ($VALID_URL_SUFFIX)"
|
||||
elif [[ "$FINAL_URL" == *"google.com.hk"* ]]; then
|
||||
STATUS="❌ 判定为送中区 (CN/HK)"
|
||||
# 分离状态码与 URL
|
||||
PROBE_CODE=$(echo "$PROBE_RESULT" | cut -d'|' -f1)
|
||||
FINAL_URL=$(echo "$PROBE_RESULT" | cut -d'|' -f2)
|
||||
|
||||
# 0. 致命拦截:网络断开、DNS 解析失败或严重超时
|
||||
if [ "$PROBE_CODE" == "000" ] || [ -z "$FINAL_URL" ]; then
|
||||
STATUS="🚨 探针失效 (网络阻断或底层路由异常)"
|
||||
else
|
||||
STATUS="⚠️ 其他分站跳板 ($FINAL_URL)"
|
||||
# 核心战术:精准提取最终 URL 的域名部分
|
||||
ACTUAL_DOMAIN=$(echo "$FINAL_URL" | awk -F/ '{print $3}')
|
||||
|
||||
# [V3.2.1 优化] 使用通配符 * 剔除任意前缀 (无论是 www.google. 还是 ipv4.google.)
|
||||
ACTUAL_SUFFIX=${ACTUAL_DOMAIN#*google.}
|
||||
|
||||
# 1. 优先验证:绝对匹配目标后缀 (彻底杜绝 com 包含于 com.hk 的陷阱)
|
||||
if [ "$ACTUAL_SUFFIX" == "$VALID_URL_SUFFIX" ]; then
|
||||
STATUS="✅ 目标区域达成 ($ACTUAL_SUFFIX)"
|
||||
|
||||
# 2. 核心拦截:精准捕捉送中特征 (com.hk)
|
||||
elif [ "$ACTUAL_SUFFIX" == "com.hk" ]; then
|
||||
if [ "$REGION_CODE" == "HK" ]; then
|
||||
STATUS="✅ 目标区域达成 (HK 专属 com.hk)"
|
||||
else
|
||||
STATUS="❌ 严重漂移!判定为送中区 (实际跳往 $ACTUAL_SUFFIX)"
|
||||
fi
|
||||
|
||||
# 3. 宽容处理:遵守 Google 无跳转新规 (严格限定必须是纯粹的 com)
|
||||
# [视觉优化] 留在 .com 代表 IP 极度纯净未被区域沙盒锁定,计入成功战绩!
|
||||
elif [ "$ACTUAL_SUFFIX" == "com" ]; then
|
||||
STATUS="✅ 目标区域达成 (免签停留 .com 通用主站)"
|
||||
|
||||
# 4. 跨区漂移:所有预判之外的后缀,全部视为异常
|
||||
else
|
||||
STATUS="⚠️ 跨区跳板漂移 (当前实际归属: $ACTUAL_SUFFIX)"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "$MODULE_NAME" "SCORE" "自检结论: $STATUS"
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: mod_trust.sh (IP 信用净化模块 V2.0 数据解耦版)
|
||||
# 核心功能: 动态读取云端/本地 JSON 规则池,模拟访问高权重网站稀释恶意流量
|
||||
# 脚本名称: mod_trust.sh (IP 信用净化模块 V3.1.4 拓扑自适应版)
|
||||
# 核心功能: 动态扫描本地 LBS 冷数据,提取权威白名单,执行流量净化
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
|
||||
REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
||||
# 你的 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"
|
||||
|
||||
# 1. 基础环境校验
|
||||
[ ! -f "$CONFIG_FILE" ] && exit 1
|
||||
@@ -16,13 +19,16 @@ source "$CONFIG_FILE"
|
||||
|
||||
REGION=${REGION_CODE:-"US"}
|
||||
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
||||
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION}.json"
|
||||
|
||||
# 2. 动态获取配置 (解耦核心)
|
||||
# 兼容旧节点:如果本地没有 json,自动拉取最新的云端配置
|
||||
if [ ! -f "$REGION_JSON_FILE" ]; then
|
||||
# 2. 动态获取配置 (V3 拓扑自适应与兜底)
|
||||
# 利用 find 穿透多级子目录,自动抓取安装时落地的那份专属 json 文件
|
||||
REGION_JSON_FILE=$(find "${INSTALL_DIR}/data/regions" -name "*.json" 2>/dev/null | head -n 1)
|
||||
|
||||
# 兼容旧节点兜底:如果本地真没找到 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 -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 数组
|
||||
@@ -44,10 +50,34 @@ log_msg() {
|
||||
}
|
||||
|
||||
# 4. 锁定单次会话指纹
|
||||
# -----------------------------------------------------------
|
||||
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
|
||||
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
|
||||
# -----------------------------------------------------------
|
||||
if [ -f "$UA_FILE" ]; then
|
||||
CURRENT_UA=$(shuf -n 1 "$UA_FILE")
|
||||
mapfile -t UA_POOL < <(grep -v '^$' "$UA_FILE")
|
||||
TOTAL_UA=${#UA_POOL[@]}
|
||||
|
||||
if [ "$TOTAL_UA" -gt 0 ]; then
|
||||
# 以本地锁定的公网 IP (BIND_IP) 为种子计算 CRC32 哈希值
|
||||
SEED=$(echo -n "${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/123.0.0.0 Safari/537.36"
|
||||
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
|
||||
|
||||
# ==========================================
|
||||
@@ -57,6 +87,25 @@ 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_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
fi
|
||||
fi
|
||||
|
||||
STEP_COUNT=$((RANDOM % 4 + 3))
|
||||
SUCCESS_INJECT=0
|
||||
|
||||
@@ -64,7 +113,9 @@ 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 隧道出网
|
||||
# [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" \
|
||||
-H "Sec-Fetch-Dest: document" \
|
||||
@@ -73,7 +124,8 @@ for ((i=1; i<=STEP_COUNT; i++)); do
|
||||
--compressed \
|
||||
-s -o /dev/null -w "%{http_code}" -m 15 "$TARGET_URL")
|
||||
|
||||
if [[ "$HTTP_CODE" =~ ^(200|301|302)$ ]]; then
|
||||
# 扩大 HTTP 状态码容错区间:包含所有 20x (如亚马逊的 202) 和 30x 重定向
|
||||
if [[ "$HTTP_CODE" =~ ^(20[0-9]|30[1-8])$ ]]; then
|
||||
log_msg "EXEC " "动作[$i/$STEP_COUNT]完成 | 状态: $HTTP_CODE | 注入: $TARGET_URL"
|
||||
((SUCCESS_INJECT++))
|
||||
else
|
||||
|
||||
@@ -28,10 +28,14 @@ export -f log
|
||||
export CONFIG_FILE INSTALL_DIR
|
||||
|
||||
# 3. 防僵尸网络特征 (Cron Jitter) - 核心隐蔽逻辑
|
||||
# 配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒 (3分钟) 内,彻底打散全球并发请求
|
||||
JITTER_TIME=$((RANDOM % 180))
|
||||
log "SYSTEM" "INFO" "主控引擎被 Cron 唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
|
||||
sleep $JITTER_TIME
|
||||
# 配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒内,彻底打散全球并发请求
|
||||
if [ -t 1 ]; then
|
||||
log "SYSTEM" "INFO " "💻 检测到人工终端干预,跳过静默休眠,立即执行任务!"
|
||||
else
|
||||
JITTER_TIME=$((RANDOM % 180))
|
||||
log "SYSTEM" "INFO " "⏱️ 主控引擎由后台唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
|
||||
sleep $JITTER_TIME
|
||||
fi
|
||||
|
||||
# 4. 唤醒并读取功能开关,执行智能调度 (Feature Flag)
|
||||
log "SYSTEM" "INFO" "休眠结束,开始计算本轮任务轮盘..."
|
||||
|
||||
@@ -18,16 +18,59 @@ if [ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2. 节点元数据抓取
|
||||
# 2. 节点元数据抓取 (v3.2.2 协议自适应与多级容灾版)
|
||||
NODE_NAME=$(hostname | cut -c 1-15)
|
||||
CURRENT_IP=$(curl -4 -s -m 5 api.ip.sb/ip || echo "Unknown")
|
||||
|
||||
# 智能判断 IP 属性
|
||||
ISP_INFO=$(curl -4 -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
|
||||
# --- [防线 1: 底层路由锁定与协议自适应] ---
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 多节点容灾探测出口 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:]' )
|
||||
# 强制兜底:如果所有外部 API 都挂了,直接使用本地强行锁定的 BIND_IP
|
||||
[ -z "$CURRENT_IP" ] && CURRENT_IP="$BIND_IP"
|
||||
|
||||
# 为可能获取到的 IPv6 自动添加方括号护甲
|
||||
[[ "$CURRENT_IP" == *":"* ]] && [[ "$CURRENT_IP" != *"["* ]] && CURRENT_IP="[${CURRENT_IP}]"
|
||||
|
||||
# --- [防线 2: 多级 ISP 容灾探针链路] ---
|
||||
ISP_INFO=""
|
||||
|
||||
# 探针 A: 纯文本 API (免 jq,极速稳定)
|
||||
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ipinfo.io/org 2>/dev/null)
|
||||
|
||||
# 探针 B: 备用纯文本 API
|
||||
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 兜底)
|
||||
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
|
||||
IP_TYPE="Cloudflare Warp 🛰️"
|
||||
else
|
||||
IP_TYPE="Native 原生网卡 🏠"
|
||||
IP_TYPE="$ISP_INFO 🏠"
|
||||
fi
|
||||
|
||||
# 动态国旗
|
||||
@@ -116,8 +159,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")
|
||||
|
||||
@@ -1,48 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 V2.0)
|
||||
# 核心功能: 清除所有世代的守护进程、清理系统定时任务、删除程序文件
|
||||
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 V3.1.4 焦土版)
|
||||
# 核心功能: 无痕清理守护进程、定时任务、运行目录及临时缓存
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
|
||||
echo "========================================================"
|
||||
echo " 🗑️ 准备卸载 IP-Sentinel (VPS IP 自动养护哨兵)"
|
||||
echo " 🗑️ 准备卸载 IP-Sentinel (边缘节点 Edge Agent)"
|
||||
echo "========================================================"
|
||||
|
||||
# 1. 停止运行中的守护进程与主控模块 (涵盖 V1.0 至 V2.0 架构全量进程)
|
||||
# 1. 停止运行中的守护进程与主控模块 (涵盖所有历史版本进程)
|
||||
echo "[1/3] 正在终止后台守护进程与所有养护任务..."
|
||||
|
||||
# 击杀旧版 (V1.x) 遗留进程
|
||||
pgrep -f tg_daemon.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
|
||||
# 击杀新版 (V2.x) 神经末梢守护进程
|
||||
pgrep -f agent_daemon.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
pgrep -f webhook.py | xargs -r kill -9 >/dev/null 2>&1
|
||||
|
||||
# 击杀调度引擎与更新/战报模块
|
||||
pgrep -f runner.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
pgrep -f updater.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
pgrep -f tg_report.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
|
||||
# 击杀所有具体的业务执行模块
|
||||
pgrep -f mod_google.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
pgrep -f mod_trust.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
# 使用 pkill 替代传统的 pgrep | xargs,指令更短、容错率更高
|
||||
pkill -9 -f "tg_daemon.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "webhook.py" >/dev/null 2>&1
|
||||
pkill -9 -f "runner.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "updater.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "tg_report.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "mod_google.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "mod_trust.sh" >/dev/null 2>&1
|
||||
|
||||
# 2. 清除系统定时任务 (Cron)
|
||||
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
if crontab -l >/dev/null 2>&1; then
|
||||
crontab -l | grep -v "ip_sentinel" > /tmp/cron_backup
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
fi
|
||||
|
||||
# 3. 删除所有文件与日志
|
||||
echo "[3/3] 正在抹除核心程序、配置文件与系统日志..."
|
||||
# 3. 删除所有文件、日志与临时缓存
|
||||
echo "[3/3] 正在抹除核心程序、配置文件与系统痕迹..."
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
rm -rf "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
# 拔除 /tmp 目录下的所有更新下载临时文件和 V1/V2 遗留的偏移量记录
|
||||
rm -f /tmp/ip_sentinel_*.txt
|
||||
rm -f /tmp/ip_sentinel_*.json
|
||||
|
||||
echo "========================================================"
|
||||
echo "✅ 卸载彻底完成!IP-Sentinel 已从您的系统中无痕移除。"
|
||||
echo "👋 感谢您的使用,期待未来再次为您守护 IP!"
|
||||
echo "💡 提示:如果安装时在防火墙放行了 Webhook 随机端口,请您按需手动关闭。"
|
||||
echo "👋 感谢您的使用,期待未来再次为您守护资产!"
|
||||
echo "========================================================"
|
||||
@@ -1,14 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: updater.sh (IP-Sentinel 养料注入与系统维护模块)
|
||||
# 核心功能: 定期静默更新热数据、清理瘦身日志文件
|
||||
# 脚本名称: updater.sh (IP-Sentinel V3.1.4 养料注入与维护中枢)
|
||||
# 核心功能: 静默更新热数据(指纹/词库/LBS规则)、清理瘦身日志
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
# 你的专属 Forgejo 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
||||
# 你的 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"
|
||||
|
||||
# 1. 加载本地冷数据配置
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
@@ -24,20 +26,20 @@ log() {
|
||||
|
||||
log "Updater" "INFO " "========== 触发后台静默 OTA 热数据更新 =========="
|
||||
|
||||
# 3. 容灾机制拉取 UA 池
|
||||
# 3. 容灾机制拉取 UA 指纹池 (强制遵循锚点协议)
|
||||
TMP_UA="/tmp/ip_sentinel_ua.txt"
|
||||
curl -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
|
||||
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
|
||||
if [ -s "$TMP_UA" ]; then
|
||||
mv "$TMP_UA" "${INSTALL_DIR}/data/user_agents.txt"
|
||||
log "Updater" "INFO " "✅ 设备指纹池 (User-Agents) 更新成功"
|
||||
else
|
||||
log "Updater" "WARN " "❌ UA 池拉取失败或为空,保留本地旧数据防崩溃"
|
||||
log "Updater" "WARN " "❌ UA 池拉取失败,保留本地旧数据防崩溃"
|
||||
rm -f "$TMP_UA"
|
||||
fi
|
||||
|
||||
# 4. 容灾机制拉取当地最新搜索词库
|
||||
TMP_KW="/tmp/ip_sentinel_kw.txt"
|
||||
curl -sL "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "$TMP_KW"
|
||||
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "$TMP_KW"
|
||||
if [ -s "$TMP_KW" ]; then
|
||||
mv "$TMP_KW" "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
|
||||
log "Updater" "INFO " "✅ 区域搜索词库 (kw_${REGION_CODE}) 更新成功"
|
||||
@@ -46,7 +48,27 @@ else
|
||||
rm -f "$TMP_KW"
|
||||
fi
|
||||
|
||||
# 5. 【升级点】日志防满瘦身机制 (保留最近 2000 行)
|
||||
# 5. 【V3.1.4 核心升级】自适应拉取本地 LBS 专属 JSON 规则库
|
||||
# 利用 find 穿透寻找本地唯一的 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
|
||||
# 提取本地文件的相对路径 (例如: data/regions/US/CA/San_Jose.json)
|
||||
REL_PATH=${REGION_JSON_FILE#*${INSTALL_DIR}/}
|
||||
TMP_JSON="/tmp/ip_sentinel_region.json"
|
||||
|
||||
# 按照相同的相对路径去云端拉取更新
|
||||
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/${REL_PATH}" -o "$TMP_JSON"
|
||||
if [ -s "$TMP_JSON" ]; then
|
||||
mv "$TMP_JSON" "$REGION_JSON_FILE"
|
||||
log "Updater" "INFO " "✅ 核心战区规则库 ($REL_PATH) 更新成功"
|
||||
else
|
||||
log "Updater" "WARN " "❌ 战区规则库拉取失败,保留本地旧数据"
|
||||
rm -f "$TMP_JSON"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 6. 日志防满瘦身机制 (保留最近 2000 行)
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
tail -n 2000 "$LOG_FILE" > "${LOG_FILE}.tmp"
|
||||
mv "${LOG_FILE}.tmp" "$LOG_FILE"
|
||||
|
||||
10
data/keywords/kw_DE.txt
Normal file
10
data/keywords/kw_DE.txt
Normal 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
10
data/keywords/kw_FR.txt
Normal 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
10
data/keywords/kw_HK.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
香港天文台天氣預報
|
||||
MTR 港鐵路線圖
|
||||
OpenRice 附近美食
|
||||
LIHKG 討論區
|
||||
恆生指數今日行情
|
||||
SCMP breaking news
|
||||
HKEX 港交所股價
|
||||
國泰航空航班狀態
|
||||
香港迪士尼樂園門票
|
||||
百佳超級市場網購
|
||||
10
data/keywords/kw_SG.txt
Normal file
10
data/keywords/kw_SG.txt
Normal 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
10
data/keywords/kw_UK.txt
Normal 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
|
||||
@@ -3,4 +3,7 @@ S&P 500 stock chart
|
||||
local coffee shops near me
|
||||
latest tech news
|
||||
California traffic updates
|
||||
AI startups in Silicon Valley
|
||||
AI startups in Silicon ValleySan Jose weather this weekend
|
||||
Silicon Valley tech news
|
||||
best tacos in San Jose
|
||||
Apple park visitor center hours
|
||||
|
||||
105
data/map.json
Normal file
105
data/map.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"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": "San_Jose", "name": "San Jose (圣何塞)" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 (香港)" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
20
data/regions/DE/Default/Frankfurt.json
Normal file
20
data/regions/DE/Default/Frankfurt.json
Normal 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/"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
data/regions/FR/Default/Paris.json
Normal file
20
data/regions/FR/Default/Paris.json
Normal 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/"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
data/regions/HK/Default/HongKong.json
Normal file
20
data/regions/HK/Default/HongKong.json
Normal 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/"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
data/regions/SG/Default/Singapore.json
Normal file
20
data/regions/SG/Default/Singapore.json
Normal 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/"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
data/regions/UK/Default/London.json
Normal file
20
data/regions/UK/Default/London.json
Normal 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/"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"region_name": "美国 (美西)",
|
||||
"region_name": "United States - Los Angeles",
|
||||
"google_module": {
|
||||
"base_lat": 34.0522,
|
||||
"base_lon": -118.2437,
|
||||
21
data/regions/US/CA/San_Jose.json
Normal file
21
data/regions/US/CA/San_Jose.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"region_name": "United States - San Jose",
|
||||
"google_module": {
|
||||
"base_lat": 37.3382,
|
||||
"base_lon": -121.8863,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://en.wikipedia.org/wiki/Special:Random",
|
||||
"https://www.yahoo.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.npr.org/",
|
||||
"https://www.weather.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.mercurynews.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
4004
data/user_agents.txt
4004
data/user_agents.txt
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本)
|
||||
# 核心功能: 安装 SQLite3、初始化数据库表、配置后台守护进程
|
||||
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本 v3.2.2)
|
||||
# 核心功能: 部署/卸载调度中枢、SQLite 资产管理、平滑热更新引擎
|
||||
# ==========================================================
|
||||
|
||||
# [新增] 提取仓库直链前缀变量,方便后续在官方库和私库间一键切换
|
||||
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 (控制中枢) v3.2.2"
|
||||
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.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_DB="true"
|
||||
|
||||
if [ "$ACTION_CHOICE" == "1" ] && [ -f "${MASTER_DIR}/master.conf" ]; then
|
||||
echo -e "\n\033[33m💡 司令部雷达提示:检测到本机已部署过 Master 中枢。\033[0m"
|
||||
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
|
||||
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
UPGRADE_MODE="true"
|
||||
read -p "👉 是否保留历史节点数据库 (SQLite)?(y/n, 默认y): " DB_CHOICE
|
||||
if [[ "$DB_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
KEEP_DB="false"
|
||||
fi
|
||||
|
||||
# 汲取原配置进入内存
|
||||
source "${MASTER_DIR}/master.conf"
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],即将跳过基础配置,直接更新核心中枢...\033[0m"
|
||||
else
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的中枢数据将被彻底抹除。\033[0m"
|
||||
fi
|
||||
fi
|
||||
# ====================================================================
|
||||
|
||||
# ================== [v3.2.2 优化: 安装前环境纯净度清理与数据保护] ==================
|
||||
echo -e "\n⏳ 正在清理旧版 Master 守护进程..."
|
||||
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
if [ "$KEEP_DB" == "false" ]; then
|
||||
rm -f "$DB_FILE" 2>/dev/null
|
||||
echo -e "🗑️ 历史节点数据库已按指令清空。"
|
||||
else
|
||||
echo -e "📦 历史节点数据库 (SQLite) 已绝密保留。"
|
||||
fi
|
||||
# 删除旧的核心脚本,准备拉取新的
|
||||
rm -f "${MASTER_DIR}/tg_master.sh" 2>/dev/null
|
||||
else
|
||||
# 焦土政策:如果不是升级模式,直接扬了整个司令部目录
|
||||
rm -rf "$MASTER_DIR" 2>/dev/null
|
||||
fi
|
||||
echo -e "\033[32m✅ 旧进程已肃清!\033[0m"
|
||||
# =======================================================================
|
||||
|
||||
# 1. 环境依赖安装
|
||||
echo "[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
|
||||
echo -e "\n[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
|
||||
if [ -f /etc/debian_version ]; then
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
apt-get install -y curl jq sqlite3 procps >/dev/null 2>&1
|
||||
@@ -23,17 +88,23 @@ fi
|
||||
|
||||
mkdir -p "$MASTER_DIR"
|
||||
|
||||
# 2. 交互配置机器人
|
||||
echo -e "\n[2/4] 配置控制中枢机器人:"
|
||||
read -p "请输入 Telegram Bot Token: " TG_TOKEN
|
||||
# ==========================================================
|
||||
# 🛑 如果是全新部署,才询问 Token 并写入配置
|
||||
# ==========================================================
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
# 2. 交互配置机器人
|
||||
echo -e "\n[2/4] 配置控制中枢机器人:"
|
||||
read -p "请输入 Telegram Bot Token: " TG_TOKEN
|
||||
|
||||
cat > "${MASTER_DIR}/master.conf" << EOF
|
||||
cat > "${MASTER_DIR}/master.conf" << EOF
|
||||
TG_TOKEN="$TG_TOKEN"
|
||||
DB_FILE="$DB_FILE"
|
||||
MASTER_DIR="$MASTER_DIR"
|
||||
EOF
|
||||
fi
|
||||
# 🛑 拦截块结束
|
||||
|
||||
# 3. 初始化 SQLite 数据库
|
||||
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下可安全修补表结构)
|
||||
echo -e "\n[3/4] 正在初始化 SQLite 数据库表结构..."
|
||||
sqlite3 "$DB_FILE" <<EOF
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
@@ -47,9 +118,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
|
||||
@@ -61,7 +138,25 @@ rm -f /tmp/cron_master
|
||||
# 立刻启动
|
||||
pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 &
|
||||
|
||||
# ================== [v3.2.2 优化: 战报文案分流] ==================
|
||||
echo "========================================================"
|
||||
echo "🎉 Master 控制中枢部署完成!"
|
||||
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
||||
echo "========================================================"
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo "🎉 Master 控制中枢平滑热更新完成!"
|
||||
echo "🤖 新版中枢引擎已接管数据库,继续等待边缘节点汇报。"
|
||||
else
|
||||
echo "🎉 Master 控制中枢部署完成!"
|
||||
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
||||
fi
|
||||
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"
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: tg_master.sh (Master 端调度枢纽 V2.0 模块化适配版)
|
||||
# 脚本名称: tg_master.sh (Master 端调度枢纽 V3.0.4 动态签名版)
|
||||
# 核心功能: 监听 TG、操作 SQLite、Webhook 精准调度、403权限拦截、僵尸节点清理
|
||||
# ==========================================================
|
||||
|
||||
@@ -9,7 +9,7 @@ CONF="/opt/ip_sentinel_master/master.conf"
|
||||
[ ! -f "$CONF" ] && exit 1
|
||||
source "$CONF"
|
||||
|
||||
OFFSET_FILE="/tmp/tg_master_offset"
|
||||
OFFSET_FILE="${MASTER_DIR}/.tg_offset"
|
||||
[[ -f $OFFSET_FILE ]] || echo "0" > $OFFSET_FILE
|
||||
|
||||
# --- 工具函数 ---
|
||||
@@ -24,11 +24,41 @@ 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}"
|
||||
}
|
||||
# ========================================================================
|
||||
|
||||
# ================== [v3.1.3 核心: 数据库结构无损热升级] ==================
|
||||
# 自动探测并增加 region 字段,屏蔽已存在的报错,保护老节点数据
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN region TEXT DEFAULT 'UNKNOWN';" 2>/dev/null
|
||||
# ========================================================================
|
||||
|
||||
# --- 核心轮询循环 ---
|
||||
while true; do
|
||||
OFFSET=$(cat $OFFSET_FILE)
|
||||
@@ -44,15 +74,69 @@ 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.1.3 大区拓扑升级版)
|
||||
# ==========================================
|
||||
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"
|
||||
|
||||
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;"
|
||||
# V3.1.3 兼容性拆包: 判断是新版协议 (5个字段) 还是老版协议 (4个字段)
|
||||
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
|
||||
if [ "$FIELD_COUNT" -ge 5 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||
else
|
||||
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||
RAW_REGION="UNKNOWN"
|
||||
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)
|
||||
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50)
|
||||
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5)
|
||||
|
||||
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
|
||||
|
||||
# 入库时追加 region 字段
|
||||
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION';"
|
||||
send_msg "$CHAT_ID" "✅ 司令部已确认!节点接入成功: \`$NODE_NAME\` ($AGENT_IP:$AGENT_PORT)"
|
||||
|
||||
# ================== [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="["
|
||||
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||
FLAG="🌐"
|
||||
case "$REGION_NAME" in
|
||||
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||
esac
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区:" "$BTNS"
|
||||
fi
|
||||
# ========================================================================
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -72,78 +156,165 @@ 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
|
||||
# 【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" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
|
||||
else
|
||||
BTNS="["
|
||||
for N in $NODE_LIST; do
|
||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
||||
done
|
||||
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||
FLAG="🌐"
|
||||
case "$REGION_NAME" in
|
||||
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||
esac
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🔍 您名下的活跃节点:" "$BTNS"
|
||||
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-')
|
||||
|
||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID' AND region='$TARGET_REGION';")
|
||||
if [ -z "$NODE_LIST" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 该战区下暂无可用节点。"
|
||||
else
|
||||
BTNS="["
|
||||
COL=0
|
||||
ROW_STR="["
|
||||
for N in $NODE_LIST; do
|
||||
ROW_STR="$ROW_STR{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"},"
|
||||
COL=$((COL+1))
|
||||
if [ $COL -eq 2 ]; then
|
||||
ROW_STR="${ROW_STR%,}]"
|
||||
BTNS="$BTNS$ROW_STR,"
|
||||
COL=0
|
||||
ROW_STR="["
|
||||
fi
|
||||
done
|
||||
# 如果是奇数,补齐最后的尾巴
|
||||
if [ $COL -eq 1 ]; then
|
||||
ROW_STR="${ROW_STR%,}]"
|
||||
BTNS="$BTNS$ROW_STR,"
|
||||
fi
|
||||
# 添加返回上级大区雷达的按钮
|
||||
BTNS="$BTNS[{\"text\":\"⬅️ 返回全球战区分布\",\"callback_data\":\"list_nodes\"}]]"
|
||||
send_ui "$CHAT_ID" "📍 **[$TARGET_REGION] 战区哨兵矩阵**\n请下达控制指令:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
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\"}]]"
|
||||
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\` 的档案已从司令部彻底销毁!"
|
||||
|
||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
if [ -z "$NODE_LIST" ]; then
|
||||
# 剔除后直接返回上级一级雷达菜单
|
||||
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" "⚠️ 当前司令部已无任何节点挂载。"
|
||||
else
|
||||
BTNS="["
|
||||
for N in $NODE_LIST; do
|
||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
||||
done
|
||||
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||
FLAG="🌐"
|
||||
case "$REGION_NAME" in
|
||||
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||
esac
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🔍 刷新后的节点列表:" "$BTNS"
|
||||
send_ui "$CHAT_ID" "🌍 刷新后的全视界雷达:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
# 【核心升级】增加拦截规则,支持 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
|
||||
|
||||
39
master/uninstall_master.sh
Normal file
39
master/uninstall_master.sh
Normal 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 "========================================================"
|
||||
77
scripts/ua_generator.py
Normal file
77
scripts/ua_generator.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import random
|
||||
import os
|
||||
|
||||
# ==========================================
|
||||
# IP-Sentinel 超大型高保真指纹工厂 (V3.1.5)
|
||||
# 无需第三方库,直接生成千万级组合的真实指纹
|
||||
# ==========================================
|
||||
|
||||
def generate_chrome_version():
|
||||
# 模拟 2024 年主流 Chrome 内核号 (122 - 125)
|
||||
major = random.randint(122, 125)
|
||||
build = random.randint(5000, 6500)
|
||||
patch = random.randint(10, 150)
|
||||
return f"{major}.0.{build}.{patch}"
|
||||
|
||||
def generate_windows_ua(count=1000):
|
||||
uas = set()
|
||||
while len(uas) < count:
|
||||
# 现代 Windows UA 已经固化为 Windows NT 10.0
|
||||
uas.add(f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Safari/537.36")
|
||||
return list(uas)
|
||||
|
||||
def generate_macos_ua(count=1000):
|
||||
uas = set()
|
||||
while len(uas) < count:
|
||||
mac_os_minor = random.randint(11, 15)
|
||||
mac_os_patch = random.randint(1, 6)
|
||||
if random.choice([True, False]):
|
||||
# Chrome on Mac
|
||||
uas.add(f"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_{mac_os_minor}_{mac_os_patch}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Safari/537.36")
|
||||
else:
|
||||
# Safari on Mac
|
||||
safari_build = f"605.1.{random.randint(10, 15)}"
|
||||
safari_version = f"17.{random.randint(1, 4)}"
|
||||
uas.add(f"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_{mac_os_minor}_{mac_os_patch}) AppleWebKit/{safari_build} (KHTML, like Gecko) Version/{safari_version} Safari/{safari_build}")
|
||||
return list(uas)
|
||||
|
||||
def generate_ios_ua(count=1000):
|
||||
uas = set()
|
||||
devices = ["iPhone", "iPad"]
|
||||
while len(uas) < count:
|
||||
device = random.choice(devices)
|
||||
ios_major = random.randint(16, 17)
|
||||
ios_minor = random.randint(1, 5)
|
||||
ios_patch = random.randint(1, 3)
|
||||
safari_build = f"605.1.{random.randint(10, 15)}"
|
||||
safari_version = f"{ios_major}.{random.choice(['0', '1', '2', '3'])}"
|
||||
|
||||
uas.add(f"Mozilla/5.0 ({device}; CPU {'iPhone ' if device=='iPhone' else ''}OS {ios_major}_{ios_minor}_{ios_patch} like Mac OS X) AppleWebKit/{safari_build} (KHTML, like Gecko) Version/{safari_version} Mobile/15E148 Safari/604.1")
|
||||
return list(uas)
|
||||
|
||||
def generate_android_ua(count=1000):
|
||||
uas = set()
|
||||
# 主流 Android 机型库
|
||||
models = ["Pixel 8 Pro", "Pixel 8", "Pixel 7a", "Pixel 7 Pro", "SM-S928B", "SM-S928U", "SM-S918B", "SM-A546B", "SM-A346B", "23113RKC6C", "23049PCD8G", "CPH2437", "V2227A", "PGT-AN10", "NX729J"]
|
||||
while len(uas) < count:
|
||||
android_ver = random.randint(12, 14)
|
||||
model = random.choice(models)
|
||||
uas.add(f"Mozilla/5.0 (Linux; Android {android_ver}; {model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Mobile Safari/537.36")
|
||||
return list(uas)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 确保输出目录存在
|
||||
os.makedirs('data', exist_ok=True)
|
||||
|
||||
# 严格按照“绝对坐标”顺序生成 4000 条数据
|
||||
pool = []
|
||||
pool.extend(generate_windows_ua(1000)) # 行 1-1000
|
||||
pool.extend(generate_macos_ua(1000)) # 行 1001-2000
|
||||
pool.extend(generate_ios_ua(1000)) # 行 2001-3000
|
||||
pool.extend(generate_android_ua(1000)) # 行 3001-4000
|
||||
|
||||
with open('data/user_agents.txt', 'w') as f:
|
||||
for ua in pool:
|
||||
f.write(ua + '\n')
|
||||
|
||||
print(f"✅ 成功生成 4000 条高保真绝对坐标指纹库!")
|
||||
75
telemetry/worker.js
Normal file
75
telemetry/worker.js
Normal 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 });
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user