92a60c62bd
gates / gates (push) Successful in 27s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
73 lines
4.5 KiB
Python
73 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""R-752 on 9202 with the LIVE catalog templates (no change yet): BookStack, Grafana, calibre-web-automated.
|
|
Per app: install through the product, wait for the install hold to open, positive control, a stranger's wrong tries
|
|
with the PUBLIC login name, the household's right password right after, then how long until it works again, then
|
|
a wrong one still refused. One app at a time; each removed at the end."""
|
|
import sys, time
|
|
import walk as w
|
|
import lk
|
|
|
|
APPS = sys.argv[1:] or ["bookstack", "grafana", "calibre-web"]
|
|
SUB = {"bookstack": "wiki", "grafana": "grafana", "calibre-web": "books"}
|
|
KEY = {"bookstack": "ADMIN_PASSWORD", "grafana": "GF_SECURITY_ADMIN_PASSWORD", "calibre-web": "ADMIN_PASSWORD"}
|
|
NAME = {"bookstack": "admin@admin.com", "grafana": "admin", "calibre-web": "admin"}
|
|
PROBE = {"bookstack": lk.bookstack, "grafana": lk.grafana, "calibre-web": lk.calibre}
|
|
|
|
w.login()
|
|
for app in APPS:
|
|
sub, name, probe = SUB[app], NAME[app], PROBE[app]
|
|
lk.p(f"##### {app} — controller {w.guest('docker inspect felhom-controller --format {{.Config.Image}}').strip()}")
|
|
lk.p("deploy:", w.deploy(app, sub))
|
|
for _ in range(120):
|
|
time.sleep(5)
|
|
logs = w.guest(f"docker logs --since 20m felhom-controller 2>&1 | grep -E '{app}: install hold OPENED|{app}.*deployed successfully'")
|
|
if "hold OPENED" in logs or (app == "grafana" and "deployed successfully" in logs):
|
|
break
|
|
w.wait_app(sub, "/login", want=("200",), tries=60)
|
|
pw = (w.GENERATED.get(app) or {}).get(KEY[app], "")
|
|
lk.p("generated password length:", len(pw))
|
|
lk.p("positive control — right password:", probe(name, pw))
|
|
tries = 6 if app != "calibre-web" else 4
|
|
for i in range(1, tries + 1):
|
|
lk.p(f"stranger wrong try {i}:", probe(name, f"wrong-{i}-{time.time()}"))
|
|
lk.p("household, RIGHT password, right after:", probe(name, pw))
|
|
# calibre-web counts EVERY try (the household's too) against 3/minute and 40/day — poll it slowly
|
|
every, cap = (70, 6 * 60) if app == "calibre-web" else (15, 20 * 60)
|
|
waited = lk.wait_until(lambda: probe(name, pw), "ok", every, cap, "right password")
|
|
lk.p(f"RESULT {app}: the right password worked again after {waited if waited is None else round(waited, 1)} min")
|
|
lk.p("a wrong one after:", probe(name, "wrong-after"), "| right again:", probe(name, pw))
|
|
if app == "calibre-web":
|
|
lk.p("## calibre-web: OPDS basic auth (the book-feed door) — 60 wrong tries as a stranger")
|
|
res = [lk.calibre_opds(name, f"opds-wrong-{i}") for i in range(60)]
|
|
lk.p("OPDS wrong tries:", {r: res.count(r) for r in set(res)})
|
|
lk.p("OPDS right password after:", lk.calibre_opds(name, pw), "| the login form, right password:", lk.calibre(name, pw))
|
|
lk.p("## calibre-web: the DAILY limit — 40 wrong tries at the 3/minute pace (~14 min), then the right password")
|
|
n = 0
|
|
while n < 40:
|
|
for _ in range(3):
|
|
if n < 40:
|
|
n += 1
|
|
r = lk.calibre(name, f"daily-{n}")
|
|
if n % 10 == 0 or r not in ("wrong",):
|
|
lk.p(f" wrong try {n}: {r}")
|
|
time.sleep(61)
|
|
lk.p("household, RIGHT password after 40:", lk.calibre(name, pw))
|
|
time.sleep(130)
|
|
lk.p("household, RIGHT password 2 min later (past the minute window):", lk.calibre(name, pw))
|
|
lk.p("restart the app (the limiter's store is in memory):", w.guest("docker restart calibre-web >/dev/null && echo restarted").strip())
|
|
w.wait_app(sub, "/login", want=("200",), tries=60)
|
|
lk.p("household, RIGHT password after the restart:", lk.calibre(name, pw))
|
|
if app == "grafana":
|
|
lk.p("## grafana: the TRICKLE — 5 wrong, then one wrong try per minute for 8 minutes; the right password each minute")
|
|
for i in range(5):
|
|
lk.p(f"burst wrong {i + 1}:", probe(name, f"burst-{i}-{time.time()}"))
|
|
for m in range(8):
|
|
time.sleep(60)
|
|
lk.p(f" minute {m + 1}: stranger wrong -> {probe(name, f'trickle-{m}')}, household right -> {probe(name, pw)}")
|
|
lk.p("stop the trickle; wait for the right password:")
|
|
waited = lk.wait_until(lambda: probe(name, pw), "ok", 15, 10 * 60, "right password")
|
|
lk.p(f"RESULT grafana trickle: open again {waited if waited is None else round(waited, 1)} min after the last wrong try")
|
|
lk.p(w.guest(f"docker logs --since 30m {app} 2>&1 | grep -i -E 'lock|block|throttl|limit' | tail -4 | cut -c1-200").strip())
|
|
w.remove(app)
|
|
lk.p("removed; deployed =", w.stack(app).get("deployed"))
|