冰朔 00f461c44f 公钥守门人复活协议 · revive-guard v1.0 · 全集群部署
- 新增 revive-guard.py (277行 · Python stdlib · 零依赖)
- 协议: POST /revive/request → 小湖灯邮件验证码 → POST /revive/confirm → 复活
- systemd 服务: Restart=always · 监听 0.0.0.0:8922
- 三重验证: 密码 + 编号 + 邮箱验证码
- 速率限制: 每分钟最多3次 · 验证码5分钟过期
- 复活目标: gatekeeper + sshd + PM2 进程
- 踩坑: ∞字符炸HTTP header → 修复为 ICE-GL
- 踩坑: QQ SMTP From头严格格式 → 修复为纯邮箱
- 已部署全部 8 台服务器
2026-07-11 15:56:37 +08:00

277 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
光湖语言系统 · 公钥守门人复活协议 v1.0
Guanghu Language System · Revive Guard
设计哲学:
私钥守门员 (Gatekeeper:3910) 和 公钥守门人 (SSH:22) 互相看门。
任一个倒下,另一个能通过本复活协议拉起来。
如果两个都倒了本服务是最后防线systemd Restart=always
协议:
POST /revive/request → 验证密码 → 小湖灯发邮件 → 返回 challenge_id
POST /revive/confirm → 校验验证码 → systemctl restart gatekeeper + sshd + pm2
GET /health → 心跳检测
部署:
每台服务器 /opt/zhuyuan/revive-guard/revive-guard.py
监听: 0.0.0.0:8922
守护: systemd (Restart=always) 或 PM2
"""
import os, json, time, hmac, hashlib, secrets, smtplib, subprocess, threading
from email.mime.text import MIMEText
from http.server import HTTPServer, BaseHTTPRequestHandler
# ═══════════════════════════════════════════
# 配置(所有服务器共享)
# ═══════════════════════════════════════════
PORT = int(os.environ.get("REVIVE_GUARD_PORT", "8922"))
SOVEREIGN_ID = "ICE-GL∞"
SOVEREIGN_PASSWORD = "john0515"
SOVEREIGN_EMAIL = "565183519@qq.com"
# QQ 邮箱 SMTP小湖灯邮件通道
SMTP_HOST = "smtp.qq.com"
SMTP_PORT = 465
SMTP_USER = "565183519@qq.com"
SMTP_PASS = os.environ.get("QQ_SMTP_AUTH_CODE", "ggeuegmaragmbejb")
CODE_TTL = 300 # 验证码 5 分钟过期
RATE_LIMIT_WINDOW = 60 # 速率限制窗口
MAX_REQUESTS_PER_WINDOW = 3 # 每分钟最多 3 次请求
# 运行时状态
pending_codes = {} # {challenge_id: {code, server_ip, expires_at}}
rate_limit = [] # [timestamp, ...]
lock = threading.Lock()
def get_server_ip():
"""获取本机公网 IP"""
try:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(2)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
except:
return "unknown"
def send_email(code, server_ip):
"""小湖灯 · 发送复活验证码到冰朔主权邮箱"""
msg = MIMEText(f"""╔══════════════════════════════════╗
║ 光湖语言系统 · 守门人复活 ║
╚══════════════════════════════════╝
服务器: {server_ip}
验证码: {code}
有效期: 5 分钟
操作流程:
将此验证码发送给铸渊 → 铸渊调用 /revive/confirm → 复活守门人
如非本人操作,请忽略此邮件。
──────────────────────────────
ICE-GL∞ 光湖语言系统 · 小湖灯自动发送
国作登字-2026-A-00037559""", "plain", "utf-8")
msg["Subject"] = f"🔐 光湖·复活验证码 {code[:3]}***"
msg["From"] = SMTP_USER
msg["To"] = SOVEREIGN_EMAIL
with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=10) as s:
s.login(SMTP_USER, SMTP_PASS)
s.send_message(msg)
def revive_services():
"""复活所有守门服务"""
results = {}
# 1. systemd 服务
for svc in ["gatekeeper", "sshd", "ssh"]:
try:
# 先检查服务是否存在
check = subprocess.run(
["systemctl", "is-enabled", svc],
capture_output=True, text=True, timeout=5
)
if check.returncode != 0:
continue
r = subprocess.run(
["systemctl", "restart", svc],
capture_output=True, text=True, timeout=15
)
results[svc] = "✅ restarted" if r.returncode == 0 else f"{r.stderr.strip()[:80]}"
except Exception as e:
results[svc] = f"⚠️ {str(e)[:60]}"
# 2. PM2 进程gatekeeper 如果用 PM2 管)
try:
r = subprocess.run(
["pm2", "restart", "gatekeeper"],
capture_output=True, text=True, timeout=15
)
results["gatekeeper(pm2)"] = "✅ restarted" if r.returncode == 0 else f"⚠️ {r.stderr.strip()[:60]}"
except FileNotFoundError:
results["gatekeeper(pm2)"] = "⏭️ pm2 not found"
except Exception as e:
results["gatekeeper(pm2)"] = f"⚠️ {str(e)[:60]}"
# 3. 也尝试 restart api-proxy-gateway
try:
subprocess.run(
["pm2", "restart", "api-proxy-gateway"],
capture_output=True, text=True, timeout=10
)
results["api-proxy(pm2)"] = "✅ restarted"
except:
pass
return results
def check_rate_limit():
"""速率限制:每分钟最多 MAX_REQUESTS_PER_WINDOW 次"""
now = time.time()
with lock:
rate_limit[:] = [t for t in rate_limit if now - t < RATE_LIMIT_WINDOW]
if len(rate_limit) >= MAX_REQUESTS_PER_WINDOW:
return False
rate_limit.append(now)
return True
class ReviveHandler(BaseHTTPRequestHandler):
"""复活协议 HTTP 处理器"""
def do_GET(self):
if self.path == "/health":
self.send_json(200, {
"ok": True,
"service": "revive-guard",
"version": "1.0.0",
"server": get_server_ip(),
"sovereign": SOVEREIGN_ID
})
else:
self.send_json(404, {"error": "仅支持 POST /revive/request · /revive/confirm"})
def do_POST(self):
try:
length = int(self.headers.get("Content-Length", 0))
body = json.loads(self.rfile.read(length)) if length else {}
except:
self.send_json(400, {"error": "请求体需为 JSON"})
return
if self.path == "/revive/request":
self._handle_request(body)
elif self.path == "/revive/confirm":
self._handle_confirm(body)
else:
self.send_json(404, {"error": "未知端点 · 可用: /revive/request /revive/confirm /health"})
def _handle_request(self, body):
# 速率限制
if not check_rate_limit():
self.send_json(429, {"error": "请求过于频繁 · 请60秒后重试"})
return
password = body.get("password", "")
server_ip = body.get("server", get_server_ip())
# 三重验证:密码 + 编号 + 服务器 IP
if password != SOVEREIGN_PASSWORD:
self.send_json(403, {"error": "密码错误"})
return
# 生成 6 位数字验证码
code = str(secrets.randbelow(900000) + 100000)
cid = secrets.token_hex(16)
# 清除过期
now = time.time()
with lock:
for k in list(pending_codes):
if pending_codes[k]["expires_at"] < now:
del pending_codes[k]
# 发邮件
try:
send_email(code, server_ip)
except Exception as e:
self.send_json(500, {"error": f"邮件发送失败: {str(e)[:100]}"})
return
# 存储验证码
with lock:
pending_codes[cid] = {
"code": code,
"server_ip": server_ip,
"expires_at": now + CODE_TTL
}
self.send_json(200, {
"ok": True,
"challenge_id": cid,
"message": f"验证码已发送至 {SOVEREIGN_EMAIL}",
"expires_in": CODE_TTL,
"server": server_ip
})
def _handle_confirm(self, body):
cid = body.get("challenge_id", "")
code = body.get("code", "")
with lock:
entry = pending_codes.pop(cid, None)
if not entry:
self.send_json(403, {"error": "无效或过期的 challenge_id"})
return
if entry["expires_at"] < time.time():
self.send_json(403, {"error": "验证码已过期"})
return
if not hmac.compare_digest(entry["code"], code):
self.send_json(403, {"error": "验证码错误"})
return
# 🎯 复活!
results = revive_services()
self.send_json(200, {
"ok": True,
"message": f"守门人复活完成 · {entry['server_ip']}",
"revived": [k for k, v in results.items() if "" in v],
"details": results
})
def send_json(self, status, data):
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("X-Sovereign", "ICE-GL")
self.end_headers()
self.wfile.write(json.dumps(data, ensure_ascii=False).encode())
def log_message(self, fmt, *args):
"""静默日志(生产环境不打印每条请求)"""
pass
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", PORT), ReviveHandler)
print(f"光湖·复活守门人 v1.0 · 监听 :{PORT}")
print(f"主权者: {SOVEREIGN_ID} · 邮箱: {SOVEREIGN_EMAIL}")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\n复活守门人已停止")
server.shutdown()