Files
felhom.eu/documentation/audits/permanent-gate-2026-10-01/tools/sp.py
T

56 lines
3.2 KiB
Python

"""sp.py — the permanent-gate spike's client (2026-10-01, 9202). curl through 9202's traefik over the LAN, or through the
SIMULATED tunnel (a curl container at 172.16.253.2 on 9202, sending what Cloudflare sends). Secrets live in the 0600
scratch files named by $SPIKE; never printed."""
import json, os, re, subprocess, time
SPIKE = os.environ["SPIKE"] # scratch dir: family-pw.json, secrets.json
BASE = "https://192.168.0.114"
DOM = "enkisfelhom.hu"
GM, MT = f"gm-spike.{DOM}", f"mt-spike.{DOM}"
FAM = json.load(open(f"{SPIKE}/family-pw.json"))
SEC = json.load(open(f"{SPIKE}/secrets.json"))
GSH = "/tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/05c3d295-b388-481a-8c36-44a2a80b7d7d/scratchpad/g.sh"
def curl(host, path, *a, method=None, data=None, cookie=None, hdrs=(), out_body=True, timeout=15):
"""LAN request. Returns (code, headers-text, body)."""
args = ["curl", "-sk", "--max-time", str(timeout), "-D", "-", "-H", f"Host: {host}"]
for h in hdrs: args += ["-H", h]
if cookie: args += ["-H", f"Cookie: {cookie}"]
if method: args += ["-X", method]
if data is not None: args += ["--data", data]
args += list(a) + [f"{BASE}{path}"]
r = subprocess.run(args, capture_output=True, text=True, errors="replace")
raw = r.stdout
raw = raw.replace("\r\n", "\n")
head, _, body = raw.partition("\n\n")
while head.startswith("HTTP/") and (" 100 " in head.split("\n")[0]) and body:
head, _, body = body.partition("\n\n")
m = re.match(r"HTTP/\S+ (\d+)", head)
return (m.group(1) if m else "000"), head, body
def tunnel(host, path, visitor, forged_left=None, *a):
"""Through the SIMULATED tunnel: a curl container at 172.16.253.2 with Cloudflare's headers. Returns (code, headers, body)."""
xff = f"{forged_left}, {visitor}" if forged_left else visitor
extra = " ".join("'" + x.replace("'", "'\\''") + "'" for x in a)
cmd = (f"docker run --rm --network felhom-tunnel --ip 172.16.253.2 curlimages/curl:8.11.1 -sk --max-time 15 -D - "
f"'https://traefik{path}' -H 'Host: {host}' -H 'X-Forwarded-For: {xff}' -H 'CF-Connecting-IP: {visitor}' {extra}")
r = subprocess.run([GSH, "9202", cmd], capture_output=True, text=True, errors="replace")
head, _, body = r.stdout.replace("\r\n", "\n").partition("\n\n")
m = re.match(r"HTTP/\S+ (\d+)", head)
return (m.group(1) if m else "000"), head, body
def cookie_from(head, name="felhom_family"):
m = re.search(rf"(?im)^set-cookie: {name}=([^;]*)", head)
return f"{name}={m.group(1)}" if m and m.group(1) else None
def family_login(host, member, password=None):
from urllib.parse import urlencode
code, head, body = curl(host, "/__family/login", method="POST",
data=urlencode({"user": member, "password": password or FAM[member], "rd": "/"}),
hdrs=("Content-Type: application/x-www-form-urlencoded",))
return code, cookie_from(head), head
def is_gate(code, head, body):
"""True when the answer is the GATE's (302 to /__family/login, or its 401 JSON) — never the app's."""
if code == "302" and re.search(r"(?im)^location: https://[^/]+/__family/login", head): return True
return code == "401" and "sign in with your family login" in body