Compare commits

...

18 Commits

Author SHA1 Message Date
hotyue
01305f98b5 feat(master): Master 脚本接入透明统计,并新增部署前旧进程自洁功能 2026-04-11 07:08:21 +00:00
hotyue
a3ae3b29f9 feat(core): Agent 安装脚本接入透明统计,安装尾声展示全球哨兵编号 2026-04-11 07:08:13 +00:00
hotyue
053c9805d4 feat(telemetry): 开源玻璃房计划 (Glasshouse) Cloudflare Worker 计数器源码 2026-04-11 07:08:05 +00:00
hotyue
be51aab0e0 feat(core): 升级全球节点拓扑图接入香港阵地,正式发布 v3.1.1 2026-04-11 05:32:03 +00:00
hotyue
426fa74ff1 feat(data): 新增亚太核心枢纽 中国香港(HK) 节点配置文件与本土化搜索词库 2026-04-11 05:31:56 +00:00
hotyue
9904246e45 feat(core): 优化 Agent 安装逻辑,新增部署前环境自洁功能 (自动清理僵尸进程与冗余定时任务) 2026-04-11 05:31:52 +00:00
hotyue
06f52cbe1d feat(core): 升级全球节点拓扑图,正式跃升至 v3.1.0 2026-04-11 02:38:27 +00:00
hotyue
33d75925a6 feat(data): 新增亚太枢纽 新加坡 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
52fe92efe5 feat(data): 新增欧洲枢纽 法国(巴黎) 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
7fec21280f feat(data): 新增欧洲枢纽 德国(法兰克福) 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
84b5fef640 feat(data): 新增欧洲枢纽 英国(伦敦) 节点配置文件与本土化搜索词库 2026-04-11 02:38:25 +00:00
hotyue
9d2aa9fcd5 fix(agent): 提拔 urllib 依赖至全局作用域,修复局部变量遮蔽导致的 Webhook 空回复崩溃 bug (v3.0.4-hotfix) 2026-04-11 02:23:52 +00:00
hotyue
ca2608756d security(core): 引入时间戳与动态 HMAC-SHA256 签名,彻底斩断明文 PSK 泄露与重放攻击隐患 (v3.0.4) 2026-04-11 01:51:03 +00:00
hotyue
27ebcfd418 security(agent): Webhook 引入 ThreadingMixIn 升级为多线程并发模型,彻底免疫 Slowloris 慢速网络耗尽攻击 (v3.0.3-part4) 2026-04-11 00:34:53 +00:00
hotyue
62deadda1e security(master): 部署脚本新增配置文件与数据库权限收敛 (chmod 600),防止 Bot Token 与节点网络拓扑泄露 (v3.0.3-part3) 2026-04-11 00:30:58 +00:00
hotyue
990d60f63a security(agent): 部署脚本新增配置文件权限收敛 (chmod 600),防止敏感 Token 发生本地提权泄露 (v3.0.3-part2) 2026-04-11 00:28:22 +00:00
hotyue
0571fcfd32 feat(install): 引入随机高位端口探测与占用校验,配合本地防火墙智能放行指引,提升防扫描隐蔽性 (v3.0.3-part1) 2026-04-11 00:12:12 +00:00
hotyue
4a77cb66d4 fix(agent): 引入 HTML 转义与 UA 伪装,修复日志回传中的 Markdown 解析冲突 (v3.0.2-final) 2026-04-10 16:25:04 +00:00
16 changed files with 528 additions and 48 deletions

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# ==========================================================
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 V2.0)
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 V3.0.3)
# 核心功能: 智能防打扰注册、进程自检、模块级路由分发(403拦截)
# ==========================================================
@@ -55,17 +55,25 @@ 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])
# 🛡️ [v3.0.2 紧急加固] 提取全局鉴权 Token (利用 CHAT_ID 作为 PSK 预共享密钥)
# 🛡️ 提取全局鉴权 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:
@@ -77,16 +85,49 @@ if os.path.exists('/opt/ip_sentinel/config.conf'):
class AgentHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
# 🛡️ 鉴权拦截器:防非法扫描与 DDoS 资源耗尽
if AUTH_TOKEN and f"auth={AUTH_TOKEN}" not in self.path:
self.send_response(401)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"401 Unauthorized: Access Denied\n")
return
# 🛡️ [v3.0.4 核心] URL 解析与动态 HMAC-SHA256 签名校验
parsed = urllib.parse.urlparse(self.path)
req_path = parsed.path
if AUTH_TOKEN:
query = urllib.parse.parse_qs(parsed.query)
req_t = query.get('t', [''])[0]
req_sign = query.get('sign', [''])[0]
# 校验 1参数是否齐全
if not req_t or not req_sign:
self.send_response(401)
self.end_headers()
self.wfile.write(b"401 Unauthorized: Missing Signature\n")
return
try:
# 校验 2时间戳防重放 (误差 ±60秒 内有效,拒绝隔夜抓包重放)
if abs(int(time.time()) - int(req_t)) > 60:
self.send_response(401)
self.end_headers()
self.wfile.write(b"401 Unauthorized: Request Expired\n")
return
except ValueError:
self.send_response(401)
self.end_headers()
return
# 校验 3HMAC 数据完整性与身份合法性校验
msg = f"{req_path}:{req_t}".encode('utf-8')
expected_sign = hmac.new(AUTH_TOKEN.encode('utf-8'), msg, hashlib.sha256).hexdigest()
# 使用 compare_digest 防御时序攻击
if not hmac.compare_digest(expected_sign, req_sign):
self.send_response(401)
self.end_headers()
self.wfile.write(b"401 Unauthorized: Signature Mismatch\n")
return
# 路由 1: Google 区域纠偏 (由于 URL 带有 auth 参数,必须由 == 改为 startswith)
if self.path.startswith('/trigger_google') or self.path.startswith('/trigger_run'):
# ================== 路由分发 (恢复为安全的精确匹配) ==================
# 路由 1: Google 区域纠偏
if req_path == '/trigger_google' or req_path == '/trigger_run':
if os.path.exists('/opt/ip_sentinel/core/mod_google.sh'):
self.send_response(200)
self.send_header("Content-type", "text/plain")
@@ -100,7 +141,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
self.wfile.write(b"403 Forbidden: Google Module Disabled\n")
# 路由 2: IP 信用净化
elif self.path.startswith('/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")
@@ -114,26 +155,21 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
self.wfile.write(b"403 Forbidden: Trust Module Disabled\n")
# 路由 3: 触发战报推送
elif self.path.startswith('/trigger_report'):
elif req_path == '/trigger_report':
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"Action Accepted: tg_report\n")
subprocess.Popen(['bash', '/opt/ip_sentinel/core/tg_report.sh'])
# 路由 4: 抓取并回传实时日志 (v3.0.2 RCE 防御重构)
elif self.path.startswith('/trigger_log'):
# 路由 4: 抓取并回传实时日志
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 拼接,改用纯 Python 安全实现
import urllib.request
import urllib.parse
try:
# 1. 安全读取配置项 (不执行 source)
config = {}
if os.path.exists('/opt/ip_sentinel/config.conf'):
with open('/opt/ip_sentinel/config.conf', 'r') as f:
@@ -143,32 +179,32 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
key, val = line.split('=', 1)
config[key] = val.strip('"\'')
# 2. 安全截取日志最后15行
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 = "".join(lines[-15:])
log_data = html.escape("".join(lines[-15:]))
# 3. 安全获取主机名
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>"
# 4. 构建并发送请求
text_msg = f"📄 **[{node_name}] 实时运行日志:**\n```log\n{log_data}\n```"
data = urllib.parse.urlencode({
'chat_id': config.get('CHAT_ID', ''),
'text': text_msg,
'parse_mode': 'Markdown'
'parse_mode': 'HTML'
}).encode('utf-8')
req = urllib.request.Request(config.get('TG_API_URL', ''), data=data)
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 fetch error: {e}")
print(f"Log transmission failed: {e}")
else:
self.send_response(404)
@@ -178,16 +214,18 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
pass
import socket
# [v3.0.1修复] 自定义支持双栈/IPv6的 Server 类
class DualStackServer(socketserver.TCPServer):
# ================== [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:
bind_addr = "::" if socket.has_ipv6 else ""
with DualStackServer((bind_addr, PORT), AgentHandler) as httpd:
with ThreadedDualStackServer((bind_addr, PORT), AgentHandler) as httpd:
httpd.serve_forever()
except Exception as e:
sys.exit(1)
# ====================================================================================
EOF
# --- [重点升级 3: 真正的静默后台启动] ---

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# ==========================================================
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v3.0.0 - Global Nexus)
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v3.0.3 - Global Nexus)
# 核心功能: 区域选择、模块按需开启、官方机器人一键配置
# ==========================================================
@@ -51,6 +51,27 @@ if [ "$ACTION_CHOICE" == "2" ]; then
exit 0
fi
# ================== [v3.1.1 新增: 安装前环境纯净度清理 (严格保留日志)] ==================
echo -e "\n⏳ 正在清理旧版守护进程与冗余任务 (保留历史日志)..."
# 1. 强制超度可能存活的 Webhook 及各类看门狗进程,释放端口
pkill -9 -f "webhook.py" >/dev/null 2>&1 || true
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1 || true
pkill -9 -f "runner.sh" >/dev/null 2>&1 || true
# 2. 清除系统定时任务 (Cron) 中的旧版条目
if crontab -l >/dev/null 2>&1; then
crontab -l | grep -v "ip_sentinel" > /tmp/cron_clean
crontab /tmp/cron_clean
rm -f /tmp/cron_clean
fi
# 3. 抹除旧版核心代码与配置文件,杜绝代码冲突 (精准避开 logs 目录)
if [ -d "$INSTALL_DIR" ]; then
rm -rf "${INSTALL_DIR}/core" "${INSTALL_DIR}/data" "${INSTALL_DIR}/config.conf" "${INSTALL_DIR}/.last_ip" 2>/dev/null
fi
echo -e "\033[32m✅ 环境清理完毕,幽灵进程已肃清!\033[0m"
# ========================================================================================
# 📍 动态一级菜单:国家选择
echo -e "\n\033[36m📍 【第一级】请选择目标国家/地区:\033[0m"
jq -r '.countries[] | "\(.id)|\(.name)|\(.keyword_file)"' /tmp/map.json > /tmp/countries.txt
@@ -157,8 +178,45 @@ if [[ "$TG_CHOICE" =~ ^[Yy]$ ]]; then
echo -e "\033[33m💡 提示:如果您不知道自己的 Chat ID可以关注 @userinfobot 获取。\033[0m"
read -p "请输入你的 Chat ID (与主控一致): " CHAT_ID
read -p "请输入本机用于接收指令的 Webhook 端口 (默认 9527): " INPUT_PORT
[ -n "$INPUT_PORT" ] && AGENT_PORT="$INPUT_PORT"
# ================== [v3.0.3 变更: 智能随机高位端口生成系统] ==================
echo -e "\n\033[36m[4.2/7] 正在构建 Webhook 安全通信隧道...\033[0m"
echo -n "🎲 正在探测可用随机端口..."
while true; do
RANDOM_PORT=$((RANDOM % 55536 + 10000))
# 同时兼容 ss (新) 和 netstat (旧) 检查端口占用
if ! (ss -tuln 2>/dev/null | grep -q ":$RANDOM_PORT " || netstat -tuln 2>/dev/null | grep -q ":$RANDOM_PORT "); then
break
fi
echo -n "."
done
echo -e " 完成!"
echo -e "💡 系统为您生成的推荐随机高位端口为: \033[32m$RANDOM_PORT\033[0m"
echo -e "\033[33m(该端口已通过本地占用校验,可直接使用)\033[0m"
while true; do
read -p "请输入 Webhook 监听端口 (回车采用推荐, 或手动输入): " INPUT_PORT
if [ -z "$INPUT_PORT" ]; then
AGENT_PORT="$RANDOM_PORT"
break
else
# 校验手动输入的合法性与可用性
if [[ "$INPUT_PORT" =~ ^[0-9]+$ ]] && [ "$INPUT_PORT" -ge 1 ] && [ "$INPUT_PORT" -le 65535 ]; then
if (ss -tuln 2>/dev/null | grep -q ":$INPUT_PORT " || netstat -tuln 2>/dev/null | grep -q ":$INPUT_PORT "); then
echo -e "\033[31m❌ 端口 $INPUT_PORT 已被占用,请重新输入或使用推荐端口。\033[0m"
else
AGENT_PORT="$INPUT_PORT"
break
fi
else
echo -e "\033[31m❌ 输入非法!端口范围应为 1-65535。\033[0m"
fi
fi
done
echo -e "✅ 已锁定 Webhook 通讯端口: \033[32m$AGENT_PORT\033[0m"
# ====================================================================
fi
# ================== [v3.0.1新增修改 1: 冗余网络栈探测与锚点锁定] ==================
@@ -261,6 +319,10 @@ IP_PREF="$IP_PREF"
BIND_IP="$BIND_IP"
EOF
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
chmod 600 "$CONFIG_FILE"
# ====================================================================
# 6. 拉取全套组件 (按需下载,绝不浪费空间)
echo -e "\n[6/7] 正在根据模块开关部署核心引擎与热数据..."
# 基础公共组件
@@ -345,7 +407,34 @@ echo "📍 你的本地守护区域已锁定为: $REGION_NAME"
echo "⚙️ 哨兵现已开启 [每30分钟] 的高频高拟真养护循环。"
if [[ -n "$TG_TOKEN" ]]; then
echo "📡 Webhook 监听已启动 (端口: $AGENT_PORT) 并向中枢发送了注册请求。"
echo "⚠️ 请务必确保本机的防火墙放行了 TCP $AGENT_PORT 端口!"
# ================== [v3.0.3 变更: 智能防火墙检测与放行指引] ==================
FW_MSG=""
if command -v ufw >/dev/null 2>&1 && ufw status | grep -qw active; then
FW_MSG="ufw allow $AGENT_PORT/tcp"
elif command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active firewalld | grep -qw active; then
FW_MSG="firewall-cmd --zone=public --add-port=$AGENT_PORT/tcp --permanent && firewall-cmd --reload"
elif command -v iptables >/dev/null 2>&1; then
FW_MSG="iptables -I INPUT -p tcp --dport $AGENT_PORT -j ACCEPT"
fi
echo -e "\033[33m⚠ 警告:请务必确保本机及云服务商安全组放行了 TCP $AGENT_PORT 端口!\033[0m"
if [ -n "$FW_MSG" ]; then
echo "💡 检测到本地防火墙开启,您可以尝试执行以下命令放行:"
echo -e "\033[36m $FW_MSG\033[0m"
fi
# ====================================================================
fi
echo "🗑️ 若未来需卸载,可重新运行本脚本选择[3]或执行: bash ${INSTALL_DIR}/core/uninstall.sh"
echo "========================================================"
echo "🗑️ 若未来需卸载,可重新运行本脚本选择[2]或执行: bash ${INSTALL_DIR}/core/uninstall.sh"
echo "========================================================"
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名不收集IP)..."
AGENT_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/agent" || echo "")
if [ -n "$AGENT_COUNT" ] && [[ "$AGENT_COUNT" =~ ^[0-9]+$ ]]; then
echo -e "\033[32m✅ 感谢您成为全球第 ${AGENT_COUNT} 名 IP-Sentinel 哨兵!\033[0m"
else
echo -e "\033[32m✅ 感谢您加入 IP-Sentinel 哨兵阵列!\033[0m"
fi
echo -e "\n"

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

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

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

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

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

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

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

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

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

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

View File

@@ -1,6 +1,6 @@
{
"version": "3.0.0",
"updated_at": "2026-04-09",
"version": "3.1.0",
"updated_at": "2026-04-11",
"countries": [
{
"id": "US",
@@ -29,6 +29,76 @@
]
}
]
},
{
"id": "UK",
"name": "United Kingdom (英国)",
"keyword_file": "kw_UK.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "London", "name": "London (伦敦)" }
]
}
]
},
{
"id": "DE",
"name": "Germany (德国)",
"keyword_file": "kw_DE.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Frankfurt", "name": "Frankfurt (法兰克福)" }
]
}
]
},
{
"id": "FR",
"name": "France (法国)",
"keyword_file": "kw_FR.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Paris", "name": "Paris (巴黎)" }
]
}
]
},
{
"id": "SG",
"name": "Singapore (新加坡)",
"keyword_file": "kw_SG.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Singapore", "name": "Singapore (新加坡)" }
]
}
]
},
{
"id": "HK",
"name": "Hong Kong (香港)",
"keyword_file": "kw_HK.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "HongKong", "name": "Hong Kong (香港)" }
]
}
]
}
]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,6 +29,11 @@ if [ "$ACTION_CHOICE" == "2" ]; then
exit 0
fi
# ================== [v3.1.1 延续: 安装前环境纯净度清理] ==================
echo -e "\n⏳ 正在清理旧版 Master 守护进程 (绝对安全保留 SQLite 数据库)..."
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
# =======================================================================
# 1. 环境依赖安装
echo "[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
if [ -f /etc/debian_version ]; then
@@ -64,6 +69,11 @@ 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 调度守护进程..."
# [修改] 剥离了写死的网址,改用顶部的 ${REPO_RAW_URL} 变量,确保与卸载脚本的数据源同源
@@ -82,4 +92,15 @@ pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/de
echo "========================================================"
echo "🎉 Master 控制中枢部署完成!"
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
echo "========================================================"
echo "========================================================"
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名不收集IP)..."
MASTER_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/master" || echo "")
if [ -n "$MASTER_COUNT" ] && [[ "$MASTER_COUNT" =~ ^[0-9]+$ ]]; then
echo -e "\033[32m✅ 感谢您成为全球第 ${MASTER_COUNT} 名 IP-Sentinel 指挥官!\033[0m"
else
echo -e "\033[32m✅ 感谢您建立 IP-Sentinel 司令部!\033[0m"
fi
echo -e "\n"

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# ==========================================================
# 脚本名称: tg_master.sh (Master 端调度枢纽 V2.0 模块化适配版)
# 脚本名称: tg_master.sh (Master 端调度枢纽 V3.0.4 动态签名版)
# 核心功能: 监听 TG、操作 SQLite、Webhook 精准调度、403权限拦截、僵尸节点清理
# ==========================================================
@@ -35,6 +35,25 @@ db_exec() {
sqlite3 "$DB_FILE" "$1"
}
# ================== [v3.0.4 核心: 动态 HMAC 签名生成器] ==================
# 用法: generate_signed_url <IP> <PORT> <PATH>
generate_signed_url() {
local target_ip=$1
local target_port=$2
local action_path=$3
local current_t=$(date +%s)
# 构建加密载荷: "路径:时间戳"
local payload="${action_path}:${current_t}"
# 使用 CHAT_ID 作为密钥,生成 SHA256 HMAC 签名
local signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$CHAT_ID" | awk '{print $NF}')
# 返回最终带签名的 URL
echo "http://${target_ip}:${target_port}${action_path}?t=${current_t}&sign=${signature}"
}
# ========================================================================
# --- 核心轮询循环 ---
while true; do
OFFSET=$(cat $OFFSET_FILE)
@@ -106,12 +125,29 @@ while true; do
else
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在召唤所有哨兵回传简报...**"
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
# [v3.0.2 紧急加固] 批量下发战报时,必须同步追加 ?auth 鉴权令牌,防止被 Agent 拒绝
curl -s -m 5 "http://${AIP}:${APORT}/trigger_report?auth=${CHAT_ID}" > /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
@@ -174,8 +210,9 @@ while true; do
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
fi
# 触发 Webhook(v3.0.2 避免DDoS攻击加固)
RESPONSE=$(curl -s -m 5 "http://${AGENT_IP}:${AGENT_PORT}/trigger_${ACTION_TYPE}?auth=${CHAT_ID}" || echo "FAILED")
# 🛡️ [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

75
telemetry/worker.js Normal file
View File

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