mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
- Improved dashboard access control by blocking tunnel/Tailscale access when disabled.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "9router-app",
|
||||
"version": "0.3.87",
|
||||
"version": "0.3.88",
|
||||
"description": "9Router web dashboard",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -121,8 +121,8 @@ export default function APIPageClient({ machineId }) {
|
||||
// Ping once to verify reachable
|
||||
const healthUrl = `${tPublicUrl || tUrl}/api/health`;
|
||||
try {
|
||||
const ping = await fetch(healthUrl, { mode: "no-cors", cache: "no-store" });
|
||||
if (ping.ok || ping.type === "opaque") {
|
||||
const ping = await fetch(healthUrl, { cache: "no-store" });
|
||||
if (ping.ok) {
|
||||
setTunnelEnabled(true);
|
||||
} else {
|
||||
pingTunnelHealth(tPublicUrl || tUrl);
|
||||
@@ -769,7 +769,7 @@ export default function APIPageClient({ machineId }) {
|
||||
/>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<p className="font-medium text-sm">Allow dashboard access via tunnel</p>
|
||||
<Tooltip text="When enabled, the dashboard can be accessed through your tunnel or Tailscale URL without requiring login. Only enable if you trust everyone who can reach your tunnel URL." />
|
||||
<Tooltip text="When enabled, the dashboard can be accessed through your tunnel or Tailscale URL (login still required). When disabled, dashboard access via tunnel/Tailscale is completely blocked." />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -8,11 +8,23 @@ const SECRET = new TextEncoder().encode(
|
||||
process.env.JWT_SECRET || "9router-default-secret-change-me"
|
||||
);
|
||||
|
||||
function isTunnelRequest(request, settings) {
|
||||
const host = (request.headers.get("host") || "").split(":")[0].toLowerCase();
|
||||
const tunnelHost = settings.tunnelUrl ? new URL(settings.tunnelUrl).hostname.toLowerCase() : "";
|
||||
const tailscaleHost = settings.tailscaleUrl ? new URL(settings.tailscaleUrl).hostname.toLowerCase() : "";
|
||||
return (tunnelHost && host === tunnelHost) || (tailscaleHost && host === tailscaleHost);
|
||||
}
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
const { password } = await request.json();
|
||||
const settings = await getSettings();
|
||||
|
||||
// Block login via tunnel/tailscale if dashboard access is disabled
|
||||
if (isTunnelRequest(request, settings) && settings.tunnelDashboardAccess !== true) {
|
||||
return NextResponse.json({ error: "Dashboard access via tunnel is disabled" }, { status: 403 });
|
||||
}
|
||||
|
||||
// Default password is '123456' if not set
|
||||
const storedHash = settings.password;
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ export async function GET() {
|
||||
const settings = await getSettings();
|
||||
const requireLogin = settings.requireLogin !== false;
|
||||
const tunnelDashboardAccess = settings.tunnelDashboardAccess === true;
|
||||
return NextResponse.json({ requireLogin, tunnelDashboardAccess });
|
||||
const tunnelUrl = settings.tunnelUrl || "";
|
||||
const tailscaleUrl = settings.tailscaleUrl || "";
|
||||
return NextResponse.json({ requireLogin, tunnelDashboardAccess, tunnelUrl, tailscaleUrl });
|
||||
} catch (error) {
|
||||
return NextResponse.json({ requireLogin: true }, { status: 200 });
|
||||
}
|
||||
|
||||
@@ -81,15 +81,20 @@ export async function proxy(request) {
|
||||
const data = await res.json();
|
||||
requireLogin = data.requireLogin !== false;
|
||||
tunnelDashboardAccess = data.tunnelDashboardAccess === true;
|
||||
|
||||
// Block tunnel/tailscale access if disabled (redirect to login)
|
||||
if (!tunnelDashboardAccess) {
|
||||
const host = (request.headers.get("host") || "").split(":")[0].toLowerCase();
|
||||
const tunnelHost = data.tunnelUrl ? new URL(data.tunnelUrl).hostname.toLowerCase() : "";
|
||||
const tailscaleHost = data.tailscaleUrl ? new URL(data.tailscaleUrl).hostname.toLowerCase() : "";
|
||||
if ((tunnelHost && host === tunnelHost) || (tailscaleHost && host === tailscaleHost)) {
|
||||
return NextResponse.redirect(new URL("/login", request.url));
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// On error, keep defaults (require login, block tunnel)
|
||||
}
|
||||
|
||||
// Block tunnel access if disabled (checked before token to enforce the setting)
|
||||
if (!isLocalRequest(request) && !tunnelDashboardAccess) {
|
||||
return NextResponse.redirect(new URL("/login", request.url));
|
||||
}
|
||||
|
||||
// If login not required, allow through
|
||||
if (!requireLogin) return NextResponse.next();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user