mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
fix(auth): real client IP rate-limiting + remote default-password guard
- Add custom-server.js: inject unspoofable socket IP, strip client XFF (wired into Docker CMD + CLI spawn + build-cli copy) - loginLimiter: key on trusted x-9r-real-ip, TRUST_PROXY opt-in, global fallback - Force password change on first remote login while default is in use - Add /api/auth/reset-password (local-only) so CLI reset writes live SQLite - CLI settings: reset via API instead of stale db.json - Fix OAuth modals opening duplicate browser tabs on add-connection - Add cli:pack / cli:publish scripts Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -46,7 +46,15 @@ export function recordSuccess(ip) {
|
||||
}
|
||||
|
||||
export function getClientIp(request) {
|
||||
const xff = request.headers.get("x-forwarded-for");
|
||||
if (xff) return xff.split(",")[0].trim();
|
||||
return request.headers.get("x-real-ip") || "unknown";
|
||||
// Trusted: set from TCP socket by custom-server.js (client cannot spoof).
|
||||
const realIp = request.headers.get("x-9r-real-ip");
|
||||
if (realIp) return realIp;
|
||||
// Behind a trusted reverse proxy that overwrites XFF with the real client IP.
|
||||
if (process.env.TRUST_PROXY === "true") {
|
||||
const xff = request.headers.get("x-forwarded-for");
|
||||
if (xff) return xff.split(",")[0].trim();
|
||||
}
|
||||
// Direct exposure without custom-server: single bucket so spoofed XFF
|
||||
// rotation cannot escape the limiter.
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ export {
|
||||
isTailscaleLoggedIn,
|
||||
isTailscaleLoggedInStrict,
|
||||
isSystemDaemonRunning,
|
||||
isDaemonAlive,
|
||||
startFunnel,
|
||||
getTailscaleBin,
|
||||
installTailscale,
|
||||
startLogin,
|
||||
|
||||
@@ -519,6 +519,11 @@ function isDaemonTunMode() {
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
/** Daemon process alive (independent of funnel state) — mirrors cloudflared PID check semantic. */
|
||||
export function isDaemonAlive() {
|
||||
return isDaemonTunMode() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tailscaled.
|
||||
* - With sudoPassword: TUN mode (root) → Funnel TLS works
|
||||
@@ -550,8 +555,9 @@ export async function startDaemonWithPassword(sudoPassword) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wantTun = !!sudoPassword;
|
||||
const currentMode = isDaemonTunMode(); // true=TUN, false=userspace, null=not running
|
||||
// No password but a healthy TUN daemon already runs → keep TUN, never downgrade-kill it.
|
||||
const wantTun = sudoPassword ? true : currentMode === true;
|
||||
|
||||
// Daemon already running in correct mode → reuse
|
||||
if (currentMode !== null && currentMode === wantTun) {
|
||||
|
||||
Reference in New Issue
Block a user