KeyHunter parser v10 + 18 scanners + tests (synthetic fixtures only, no keys/db/tokens)

This commit is contained in:
2026-10-20 07:50:07 +00:00
commit a5008d837b
178 changed files with 36909 additions and 0 deletions

123
vdsok_provision.py Normal file
View File

@@ -0,0 +1,123 @@
"""VDSok provisioner — idempotent order + deploy for the KeyFarm VPS plane.
Reads the API key from tmp/vdsok_token.txt (never from argv/env literals).
Flow: /me -> /servers (reuse same-name box if exists) -> /account balance ->
POST /servers (Idempotency-Key, cheapest tariff by default) -> poll order ->
save creds to tmp/vdsok_server_<name>.txt -> optional deploy via kf_install.sh.
usage: python tmp/vdsok_provision.py [--name keyfarm-nl] [--tariff 16]
[--os ubuntu-24-04] [--months 1] [--shard 1] [--shards 4] [--deploy]
"""
import argparse
import json
import os
import sys
import time
import urllib.error
import urllib.request
import uuid
TOK_FILE = os.path.expanduser(r"~\tmp\vdsok_token.txt")
BASE = "https://vdsok.guru/api/v1"
def call(path, method="GET", body=None, idem=None, tok=None):
h = {"Authorization": "***" + tok, "Accept": "application/json"}
if idem:
h["Idempotency-Key"] = idem
data = json.dumps(body).encode() if body is not None else None
if data:
h["Content-Type"] = "application/json"
req = urllib.request.Request(BASE + path, method=method, headers=h, data=data)
try:
with urllib.request.urlopen(req, timeout=60) as r:
return r.status, json.loads(r.read().decode() or "null")
except urllib.error.HTTPError as e:
raw = e.read().decode(errors="replace")
try:
raw = json.loads(raw)
except Exception:
pass
return e.code, raw
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--name", default="keyfarm-hunter")
ap.add_argument("--tariff", type=int, default=0, help="0 = cheapest available")
ap.add_argument("--os", dest="os_slug", default="ubuntu-24-04")
ap.add_argument("--months", type=int, default=1)
ap.add_argument("--shard", type=int, default=0)
ap.add_argument("--shards", type=int, default=4)
ap.add_argument("--deploy", action="store_true")
a = ap.parse_args()
tok = open(TOK_FILE, encoding="utf-8").read().strip()
st, me = call("/me", tok=tok)
if st != 200:
sys.exit(f"/me failed: {st} {me}")
print(f"key ok: account_id={me.get('account_id')} sandbox={me.get('is_test')}")
st, servers = call("/servers?limit=100", tok=tok)
for s in (servers.get("data") or []):
if s.get("name") == a.name:
print(f"REUSE existing server id={s['id']} ip={s.get('primary_ip')} "
f"status={s.get('status')}")
save(a.name, s, None)
return
st, acct = call("/account", tok=tok)
bal = (acct.get("balance") or {}).get("amount") if isinstance(acct.get("balance"), dict) else acct.get("balance")
print(f"balance: {bal} {acct.get('currency')}")
st, tars = call("/catalog/tariffs?limit=100", tok=tok)
avail = [t for t in tars.get("data", []) if t.get("available")]
if not avail:
sys.exit("no available tariffs")
t = next((x for x in avail if x["id"] == a.tariff), None) if a.tariff else None
t = t or min(avail, key=lambda x: float(x["price_monthly"]))
price = float(t["price_monthly"]) * a.months
print(f"tariff: id={t['id']} {t['name']} {t['price_monthly']}/mo x{a.months} "
f"= {price:.2f} {t['currency']} | {t['vcpu']}vCPU "
f"{int(t['ram_mb'])//1024}GB {int(t['disk_mb'])//1024}GB {t['location']}")
if bal is not None and float(bal) < price:
sys.exit(f"INSUFFICIENT BALANCE {bal} < {price:.2f} — top up first")
st, res = call("/servers", "POST",
{"tariff_id": t["id"], "os": a.os_slug, "name": a.name,
"months": a.months},
idem=str(uuid.uuid4()), tok=tok)
if st not in (200, 201, 202):
sys.exit(f"order failed: {st} {res}")
srv = res.get("server", {})
pw = res.get("root_password")
print(f"ordered: id={srv.get('id')} charged={res.get('charged')} "
f"balance_after={res.get('balance_after')}")
# poll for IP
for i in range(24):
st, s2 = call(f"/servers/{srv.get('id')}", tok=tok)
ip = (s2 or {}).get("primary_ip")
if ip:
srv = s2
break
time.sleep(10)
save(a.name, srv, pw)
if a.deploy and srv.get("primary_ip") and pw:
print("deploy: scp tmp/kf_install.sh + tree, then "
f"bash install.sh {a.shard} {a.shards} (see handoff)")
def save(name, srv, pw):
out = os.path.expanduser(rf"~\tmp\vdsok_server_{name}.txt")
rec = {"server_id": srv.get("id"), "name": name,
"primary_ip": srv.get("primary_ip"), "status": srv.get("status"),
"tariff": (srv.get("tariff") or {}).get("name"),
"root_password": pw or "(shown once at order; see earlier record)"}
with open(out, "w", encoding="utf-8") as f:
json.dump(rec, f, indent=1)
os.chmod(out, 0o600)
print(f"saved: {out} ip={rec['primary_ip']}")
if __name__ == "__main__":
main()