Files
MoShiro/scripts/smoke-wsl.py
T
justme e014a19554
ci/woodpecker/push/main-amd64-build Pipeline was successful
docs: reorganize into design, security, api, ops and dev; drop stale designs; refresh the diagrams [CI]
- Group docs by category with an index (docs/README.md); fix every link,
  code comment and the protocol conformance test that read docs/protocol.md.
- access.md and security.md rewritten to what is implemented: removed the
  unadopted member-proof chain, roles and recovery key that do not exist,
  the old implementation order and the budgets; gaps now carry a status.
- enrollment, deployment, api: the install flow is the static, hash-checked
  script with environment variables; no default admin.
- README and architecture: new topology and update-flow diagrams (operator
  passkey, CI signing, registry feed, auth and trust modules).
2026-10-08 19:37:56 +08:00

78 lines
4.3 KiB
Python
Raw Permalink 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.
"""跨机器的真实链路验证:Windows 上的 Probe(NativeAOT exe)→ WSL2 里的 Agent 和 Backend(真实的虚拟网络、不同的单调时钟原点)。
前提:WSL 里有 Debian(root)、tc(netem 注入丢包/延迟/黑洞用);已经用 scripts/publish.sh 发布了 linux-x64 的 agent、backend、fleet-enroll,
并且 Windows 上发布了 probe(dotnet publish backend/probe/src/MoShiro.Probe -c Release -r win-x64 -p:PublishAot=true -o backend/artifacts/aot-probe)。
把 linux-x64 的产物复制到 WSL 的 /root/mon/,在 WSL 里用 setsid -f nohup 启动 moshiro-backend(监听 0.0.0.0:29181),再运行本脚本。
故障注入见 docs/ops/deployment.md 第 3 节。
"""
import json, os, subprocess, sys, time, urllib.request, urllib.error
# 要连的是本机的虚拟网络(WSL),不能走环境变量里的 HTTP(S)_PROXY
OPENER = urllib.request.build_opener(urllib.request.ProxyHandler({}))
ROOT = r"C:\Users\JustMe\Desktop\moshiro-proj"
PROBE_EXE = os.path.join(ROOT, "backend", "artifacts", "aot-probe", "moshiro-probe.exe")
WORK = r"C:\Users\JustMe\AppData\Local\Temp\realnet"
PORT = 29181
def wsl(cmd, check=True):
r = subprocess.run(["wsl", "-d", "Debian", "-u", "root", "-e", "bash", "-c", cmd], capture_output=True, text=True)
if check and r.returncode != 0:
print("WSL FAILED:", cmd, r.stdout, r.stderr)
raise SystemExit(1)
return r.stdout.strip()
WSLIP = wsl("ip -4 addr show eth0 | grep -oP '(?<=inet )[0-9.]+'")
BASE = f"http://{WSLIP}:{PORT}"
def req(method, path, body=None, headers=None, ctype="application/json"):
data = None if body is None else json.dumps(body).encode()
h = {"Content-Type": ctype}
h.update(headers or {})
r = urllib.request.Request(BASE + path, data=data, method=method, headers=h)
try:
with OPENER.open(r, timeout=30) as resp:
text = resp.read().decode()
return resp, (json.loads(text) if text else None)
except urllib.error.HTTPError as e:
print("HTTP", e.code, e.read().decode())
raise
def main():
os.makedirs(WORK, exist_ok=True)
print("wsl ip", WSLIP)
resp, login = req("POST", "/api/admin/auth/login", {"username": "admin", "password": os.environ.get("MOSHIRO_ADMIN_PASSWORD", "correct horse battery")})
cookie = resp.headers["Set-Cookie"].split(";")[0]
_, tok = req("POST", "/api/admin/api-tokens", {"name": "net", "scope": "admin"}, {"Cookie": cookie, "X-CSRF-Token": login["csrf_token"]})
auth = {"Authorization": "Bearer " + tok["plaintext"]}
json.dump({"base": BASE, "token": tok["plaintext"], "wslip": WSLIP}, open(WORK + r"\state.json", "w"))
_, join = req("POST", "/api/admin/join-tokens", {"auto_approve": True, "max_uses": 1, "expires_in_hours": 1}, auth)
wsl("pkill moshiro-agent; cd /root/mon && rm -rf id agent.json")
print(wsl(f"cd /root/mon && ./fleet-enroll --server http://127.0.0.1:{PORT} --join {join['plaintext']} --name wsl-agent --identity-dir /root/mon/id"))
cfg = {"fleet": {"server": f"http://127.0.0.1:{PORT}", "identity_dir": "/root/mon/id"}, "listen": {"bind": "0.0.0.0"}}
wsl("cat > /root/mon/agent.json <<'EOF'\n" + json.dumps(cfg) + "\nEOF")
wsl("cd /root/mon && (setsid nohup ./moshiro-agent --config /root/mon/agent.json > agent.log 2>&1 < /dev/null &)")
time.sleep(3)
_, agents = req("GET", "/api/admin/agents", None, auth)
agent = agents["items"][0]
print("agent", agent["id"], agent["status"], "online", agent.get("online"))
# the backend only sees 127.0.0.1 as the agent's address: the panel needs the address the probe can reach
req("PATCH", "/api/admin/agents/" + agent["id"], {"public_host": WSLIP}, auth, "application/merge-patch+json")
_, ident = req("POST", "/api/admin/probes", {"name": "home"}, auth)
print("bootstrap", ident["bootstrap"])
os.makedirs(WORK + r"\probe", exist_ok=True)
json.dump(ident, open(WORK + r"\probe\probe.json", "w"))
json.dump({"base": BASE, "token": tok["plaintext"], "wslip": WSLIP, "agent_id": agent["id"]}, open(WORK + r"\state.json", "w"))
log = open(WORK + r"\probe.log", "w")
p = subprocess.Popen([PROBE_EXE, "--config", WORK + r"\probe\probe.json"], stdout=log, stderr=subprocess.STDOUT)
open(WORK + r"\probe.pid", "w").write(str(p.pid))
print("probe pid", p.pid)
main()