Rigel: auth SOCKS5 frontend (atyp=3 length-prefix fix, UTC auth, per-upstream creds), full-creds dashboard, honest provision_proxy
This commit is contained in:
41
app.py
41
app.py
@@ -38,6 +38,10 @@ BTCPAY_URL = os.environ.get("BTCPAY_URL", "https://10.30.20.140")
|
||||
BTCPAY_STORE = os.environ.get("BTCPAY_STORE_ID", "")
|
||||
BTCPAY_TOKEN = os.environ.get("BTCPAY_API_KEY", "")
|
||||
|
||||
# Public SOCKS5 endpoint customers connect to (authenticated frontend on CT158)
|
||||
PROXY_HOST = os.environ.get("PROXY_PUBLIC_HOST", "10.30.20.116")
|
||||
PROXY_PORT = os.environ.get("PROXY_PUBLIC_PORT", "1081")
|
||||
|
||||
def db():
|
||||
con = getattr(g, "_db", None)
|
||||
if con is None:
|
||||
@@ -108,7 +112,8 @@ def index():
|
||||
user = current_user()
|
||||
if not user:
|
||||
return render_template_string(AUTH_HTML, mode="login", error="")
|
||||
return render_template_string(DASH_HTML, user=user, locations=LOCATIONS, plans=PLANS)
|
||||
return render_template_string(DASH_HTML, user=user, locations=LOCATIONS, plans=PLANS,
|
||||
proxy_host=PROXY_HOST, proxy_port=PROXY_PORT)
|
||||
|
||||
@app.route("/health")
|
||||
def health():
|
||||
@@ -255,12 +260,12 @@ def btcpay_webhook():
|
||||
return jsonify({"ok": True})
|
||||
|
||||
def provision_proxy(location, user, pw):
|
||||
"""Best-effort: add user auth to the gost frontend for this location.
|
||||
The gost SOCKS5 frontends need per-user auth; for now we record the creds
|
||||
and the actual gost auth layer is wired per-location (see rigel-proxy setup)."""
|
||||
# Placeholder — the real gost auth is applied via the proxy gateway config.
|
||||
# Creds are persisted on the subscription row so the customer sees them.
|
||||
app.logger.info(f"provision proxy {location} for {user}")
|
||||
"""No per-node writes needed: the authenticated SOCKS5 frontend
|
||||
(rigel-proxy.service on CT158) validates every connection live against the
|
||||
subscriptions table, so persisting the row IS the provisioning step.
|
||||
The upstream exits are dumb unauthenticated relays on the LAN."""
|
||||
up = LOCATIONS.get(location, {}).get("upstream", "?")
|
||||
app.logger.info(f"provisioned {location} (upstream {up}) for {user}")
|
||||
|
||||
# ── templates ───────────────────────────────────────────────────────────
|
||||
AUTH_HTML = r"""
|
||||
@@ -332,6 +337,10 @@ main{padding:28px;max-width:900px;margin:0 auto}
|
||||
.subs{margin-top:32px}.subs h2{font-size:17px}.subs table{width:100%;border-collapse:collapse;font-size:13px}
|
||||
.subs th,.subs td{padding:10px;text-align:left;border-bottom:1px solid #1e2742}.subs th{color:var(--muted);font-weight:500}
|
||||
.badge{padding:3px 8px;border-radius:10px;font-size:11px}.active{background:#14321f;color:#2ecc71}.pending{background:#332a14;color:#f1c40f}.expired{background:#33171a;color:#e74c3c}
|
||||
.subcard{background:var(--card);border:1px solid #1e2742;border-radius:12px;padding:18px;margin-top:14px}
|
||||
.srow{font-size:15px;margin-bottom:6px}
|
||||
.lbl{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-top:10px}
|
||||
code{display:block;background:#0d1325;border:1px solid #26304f;border-radius:6px;padding:8px 10px;font-size:12px;color:#9fd0ff;word-break:break-all;margin-top:3px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
|
||||
</style></head><body>
|
||||
<header><h1><span>✦</span> Rigel <span style="color:var(--muted);font-weight:400;font-size:13px">— {{user["username"]}}</span></h1><button onclick="logout()">Logout</button></header>
|
||||
<main>
|
||||
@@ -359,9 +368,21 @@ async function logout(){await fetch('/api/logout',{method:'POST'});location.relo
|
||||
async function loadSubs(){
|
||||
let r=await fetch('/api/subscriptions').then(x=>x.json());
|
||||
if(!r.length){document.getElementById('subs').innerHTML='<p style="color:var(--muted)">No subscriptions yet.</p>';return;}
|
||||
let html='<table><tr><th>Location</th><th>Plan</th><th>Status</th><th>Expires</th><th>Proxy creds</th></tr>';
|
||||
for(const s of r){html+=`<tr><td>${s.location}</td><td>${s.plan}</td><td><span class="badge ${s.status}">${s.status}</span></td><td>${s.expires_at||'—'}</td><td>${s.proxy_user||'—'}</td></tr>`;}
|
||||
html+='</table>';document.getElementById('subs').innerHTML=html;
|
||||
let h='';
|
||||
for(const s of r){
|
||||
h+=`<div class="subcard"><div class="srow"><b style="text-transform:capitalize">${s.location}</b> · ${s.plan} · <span class="badge ${s.status}">${s.status}</span></div>`;
|
||||
if(s.status==='active'&&s.proxy_user){
|
||||
h+=`<div class="lbl">SOCKS5 endpoint</div><code>{{proxy_host}}:{{proxy_port}}</code>`
|
||||
+ `<div class="lbl">Username</div><code>${s.proxy_user}</code>`
|
||||
+ `<div class="lbl">Password</div><code>${s.proxy_pass||'\u2014'}</code>`
|
||||
+ `<div class="lbl">Expires (UTC)</div><code>${s.expires_at||'\u2014'}</code>`
|
||||
+ `<div class="lbl">Test it</div><code>curl --socks5-hostname ${s.proxy_user}:${s.proxy_pass}@{{proxy_host}}:{{proxy_port}} https://api.ipify.org</code>`;
|
||||
} else {
|
||||
h+=`<div style="color:var(--muted);font-size:13px;margin-top:8px">Awaiting payment confirmation\u2026 credentials appear here automatically.</div>`;
|
||||
}
|
||||
h+='</div>';
|
||||
}
|
||||
document.getElementById('subs').innerHTML=h;
|
||||
}
|
||||
loadSubs();
|
||||
</script></body></html>
|
||||
|
||||
Reference in New Issue
Block a user