155 lines
5.3 KiB
Python
155 lines
5.3 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
密钥猎手 · Key Hunter · ICE-GL-ZY-AGT-KEY-001
|
|||
|
|
|
|||
|
|
铸渊叫一声,猎手去找密钥。
|
|||
|
|
不需要铸渊知道密钥在哪里——猎手知道。
|
|||
|
|
|
|||
|
|
用法:
|
|||
|
|
python3 hunt.py search NOTION_TOKEN # 搜索指定密钥
|
|||
|
|
python3 hunt.py list BS-SG-001 # 列出服务器上的密钥
|
|||
|
|
python3 hunt.py scan-all # 扫描所有服务器
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import json
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
import urllib.request
|
|||
|
|
import base64
|
|||
|
|
|
|||
|
|
# Gatekeeper密钥映射(从brain/gatekeeper-deployment.json加载)
|
|||
|
|
def load_gatekeepers():
|
|||
|
|
"""加载所有Gatekeeper连接信息"""
|
|||
|
|
repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|||
|
|
gk_path = os.path.join(repo_root, "brain", "gatekeeper-deployment.json")
|
|||
|
|
if os.path.exists(gk_path):
|
|||
|
|
with open(gk_path) as f:
|
|||
|
|
data = json.load(f)
|
|||
|
|
return {s["code"]: s for s in data.get("servers", [])}
|
|||
|
|
return {}
|
|||
|
|
|
|||
|
|
def gatekeeper_exec(server_code, command, timeout=15):
|
|||
|
|
"""通过Gatekeeper在远程服务器上执行命令"""
|
|||
|
|
servers = load_gatekeepers()
|
|||
|
|
if server_code not in servers:
|
|||
|
|
return {"error": f"未知服务器: {server_code}"}
|
|||
|
|
|
|||
|
|
srv = servers[server_code]
|
|||
|
|
url = f"http://{srv['ip']}:{srv['port']}/exec"
|
|||
|
|
headers = {
|
|||
|
|
"Authorization": f"Bearer {srv['key']}",
|
|||
|
|
"Content-Type": "application/json"
|
|||
|
|
}
|
|||
|
|
data = json.dumps({"cmd": command}).encode()
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
req = urllib.request.Request(url, data=data, headers=headers)
|
|||
|
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|||
|
|
return json.loads(resp.read())
|
|||
|
|
except Exception as e:
|
|||
|
|
return {"error": str(e)}
|
|||
|
|
|
|||
|
|
def search_key(server_code, key_name):
|
|||
|
|
"""在指定服务器上搜索密钥"""
|
|||
|
|
# 搜索常见位置
|
|||
|
|
search_cmd = f"""
|
|||
|
|
for path in /opt/zhuyuan/guanghulab/.env /opt/juzi/*/.env /etc/environment ~/.gk/secret; do
|
|||
|
|
if [ -f "$path" ]; then
|
|||
|
|
result=$(grep -E "^{key_name}=" "$path" 2>/dev/null | head -1)
|
|||
|
|
if [ -n "$result" ]; then
|
|||
|
|
echo "FOUND:$path:$result"
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
# 也搜索PM2环境变量
|
|||
|
|
pm2 env 0 2>/dev/null | grep "{key_name}=" | head -1 | sed 's/^/FOUND:pm2:/'
|
|||
|
|
"""
|
|||
|
|
result = gatekeeper_exec(server_code, search_cmd)
|
|||
|
|
if result.get("ok") and result.get("stdout"):
|
|||
|
|
for line in result["stdout"].strip().split("\n"):
|
|||
|
|
if line.startswith("FOUND:"):
|
|||
|
|
parts = line.split(":", 2)
|
|||
|
|
return {
|
|||
|
|
"found": True,
|
|||
|
|
"key_name": key_name,
|
|||
|
|
"server": server_code,
|
|||
|
|
"location": parts[1] if len(parts) > 1 else "unknown",
|
|||
|
|
"value": parts[2].split("=", 1)[1].strip() if len(parts) > 2 and "=" in parts[2] else parts[2] if len(parts) > 2 else "unknown"
|
|||
|
|
}
|
|||
|
|
return {"found": False, "key_name": key_name, "server": server_code}
|
|||
|
|
|
|||
|
|
def search_all_servers(key_name, priority_domains=None):
|
|||
|
|
"""在所有服务器上搜索密钥,优先搜索指定域"""
|
|||
|
|
servers = load_gatekeepers()
|
|||
|
|
if not priority_domains:
|
|||
|
|
priority_domains = ["ice-core"]
|
|||
|
|
|
|||
|
|
results = []
|
|||
|
|
|
|||
|
|
# 优先搜索ice-core域
|
|||
|
|
for code, srv in servers.items():
|
|||
|
|
if srv.get("domain") in priority_domains:
|
|||
|
|
r = search_key(code, key_name)
|
|||
|
|
results.append(r)
|
|||
|
|
if r.get("found"):
|
|||
|
|
return r # 找到了就返回
|
|||
|
|
|
|||
|
|
# 搜索其他域
|
|||
|
|
for code, srv in servers.items():
|
|||
|
|
if srv.get("domain") not in priority_domains:
|
|||
|
|
r = search_key(code, key_name)
|
|||
|
|
results.append(r)
|
|||
|
|
if r.get("found"):
|
|||
|
|
return r
|
|||
|
|
|
|||
|
|
return {"found": False, "key_name": key_name, "searched": len(results), "results": results}
|
|||
|
|
|
|||
|
|
def list_server_keys(server_code):
|
|||
|
|
"""列出服务器上的所有密钥名称"""
|
|||
|
|
result = gatekeeper_exec(server_code, """
|
|||
|
|
for path in /opt/zhuyuan/guanghulab/.env /opt/juzi/*/.env 2>/dev/null; do
|
|||
|
|
if [ -f "$path" ]; then
|
|||
|
|
echo "=== $path ==="
|
|||
|
|
grep -E '^[A-Z_]+=' "$path" | cut -d'=' -f1
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
""")
|
|||
|
|
if result.get("ok"):
|
|||
|
|
return {"server": server_code, "output": result.get("stdout", "")}
|
|||
|
|
return {"server": server_code, "error": result.get("error", "unknown")}
|
|||
|
|
|
|||
|
|
def scan_all():
|
|||
|
|
"""扫描所有服务器状态"""
|
|||
|
|
servers = load_gatekeepers()
|
|||
|
|
results = {}
|
|||
|
|
for code in servers:
|
|||
|
|
result = gatekeeper_exec(code, "echo ONLINE && hostname")
|
|||
|
|
results[code] = {
|
|||
|
|
"online": result.get("ok", False),
|
|||
|
|
"hostname": result.get("stdout", "").strip() if result.get("ok") else "OFFLINE"
|
|||
|
|
}
|
|||
|
|
return results
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
if len(sys.argv) < 2:
|
|||
|
|
print("用法: hunt.py <search|list|scan-all> [参数]")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
action = sys.argv[1]
|
|||
|
|
|
|||
|
|
if action == "search" and len(sys.argv) > 2:
|
|||
|
|
key_name = sys.argv[2]
|
|||
|
|
result = search_all_servers(key_name)
|
|||
|
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|||
|
|
|
|||
|
|
elif action == "list" and len(sys.argv) > 2:
|
|||
|
|
result = list_server_keys(sys.argv[2])
|
|||
|
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|||
|
|
|
|||
|
|
elif action == "scan-all":
|
|||
|
|
result = scan_all()
|
|||
|
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
print("未知操作")
|