mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
Add local bypass feature
This commit is contained in:
parent
85ac380095
commit
801438d867
2 changed files with 66 additions and 1 deletions
|
|
@ -11,14 +11,42 @@ interface AuthConfig {
|
||||||
isHQMode: boolean;
|
isHQMode: boolean;
|
||||||
bearerToken?: string; // Token that HQ must use to authenticate with this remote
|
bearerToken?: string; // Token that HQ must use to authenticate with this remote
|
||||||
authService?: AuthService; // Enhanced auth service for JWT tokens
|
authService?: AuthService; // Enhanced auth service for JWT tokens
|
||||||
|
allowLocalBypass?: boolean; // Allow localhost connections to bypass auth
|
||||||
|
localAuthToken?: string; // Token for localhost authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AuthenticatedRequest extends Request {
|
interface AuthenticatedRequest extends Request {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
authMethod?: 'ssh-key' | 'password' | 'hq-bearer' | 'no-auth';
|
authMethod?: 'ssh-key' | 'password' | 'hq-bearer' | 'no-auth' | 'local-bypass';
|
||||||
isHQRequest?: boolean;
|
isHQRequest?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to check if request is from localhost
|
||||||
|
function isLocalRequest(req: Request): boolean {
|
||||||
|
// Get the real client IP
|
||||||
|
const clientIp = req.ip || req.socket.remoteAddress || '';
|
||||||
|
|
||||||
|
// Check for localhost IPs
|
||||||
|
const localIPs = ['127.0.0.1', '::1', '::ffff:127.0.0.1', 'localhost'];
|
||||||
|
const ipIsLocal = localIPs.includes(clientIp);
|
||||||
|
|
||||||
|
// Additional security checks to prevent spoofing
|
||||||
|
const noForwardedFor = !req.headers['x-forwarded-for'];
|
||||||
|
const noRealIP = !req.headers['x-real-ip'];
|
||||||
|
const noForwardedHost = !req.headers['x-forwarded-host'];
|
||||||
|
|
||||||
|
// Check hostname
|
||||||
|
const hostIsLocal =
|
||||||
|
req.hostname === 'localhost' || req.hostname === '127.0.0.1' || req.hostname === '[::1]';
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`Local request check - IP: ${clientIp}, Host: ${req.hostname}, ` +
|
||||||
|
`Forwarded headers: ${!noForwardedFor || !noRealIP || !noForwardedHost}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return ipIsLocal && noForwardedFor && noRealIP && noForwardedHost && hostIsLocal;
|
||||||
|
}
|
||||||
|
|
||||||
export function createAuthMiddleware(config: AuthConfig) {
|
export function createAuthMiddleware(config: AuthConfig) {
|
||||||
return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
||||||
// Skip auth for health check endpoint, auth endpoints, client logging, and push notifications
|
// Skip auth for health check endpoint, auth endpoints, client logging, and push notifications
|
||||||
|
|
@ -37,6 +65,28 @@ export function createAuthMiddleware(config: AuthConfig) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for local bypass if enabled
|
||||||
|
if (config.allowLocalBypass && isLocalRequest(req)) {
|
||||||
|
// If a local auth token is configured, check for it
|
||||||
|
if (config.localAuthToken) {
|
||||||
|
const providedToken = req.headers['x-vibetunnel-local'] as string;
|
||||||
|
if (providedToken === config.localAuthToken) {
|
||||||
|
logger.debug('Local request authenticated with token');
|
||||||
|
req.authMethod = 'local-bypass';
|
||||||
|
req.userId = 'local-user';
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
logger.debug('Local request missing or invalid token');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No token required for local bypass
|
||||||
|
logger.debug('Local request authenticated without token');
|
||||||
|
req.authMethod = 'local-bypass';
|
||||||
|
req.userId = 'local-user';
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only log auth requests that might be problematic (no header or failures)
|
// Only log auth requests that might be problematic (no header or failures)
|
||||||
// Remove verbose logging for successful token auth to reduce spam
|
// Remove verbose logging for successful token auth to reduce spam
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ interface Config {
|
||||||
vapidEmail: string | null;
|
vapidEmail: string | null;
|
||||||
generateVapidKeys: boolean;
|
generateVapidKeys: boolean;
|
||||||
bellNotificationsEnabled: boolean;
|
bellNotificationsEnabled: boolean;
|
||||||
|
// Local bypass configuration
|
||||||
|
allowLocalBypass: boolean;
|
||||||
|
localAuthToken: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show help message
|
// Show help message
|
||||||
|
|
@ -78,6 +81,8 @@ Options:
|
||||||
--enable-ssh-keys Enable SSH key authentication UI and functionality
|
--enable-ssh-keys Enable SSH key authentication UI and functionality
|
||||||
--disallow-user-password Disable password auth, SSH keys only (auto-enables --enable-ssh-keys)
|
--disallow-user-password Disable password auth, SSH keys only (auto-enables --enable-ssh-keys)
|
||||||
--no-auth Disable authentication (auto-login as current user)
|
--no-auth Disable authentication (auto-login as current user)
|
||||||
|
--allow-local-bypass Allow localhost connections to bypass authentication
|
||||||
|
--local-auth-token <token> Token for localhost authentication bypass
|
||||||
--debug Enable debug logging
|
--debug Enable debug logging
|
||||||
|
|
||||||
Push Notification Options:
|
Push Notification Options:
|
||||||
|
|
@ -141,6 +146,9 @@ function parseArgs(): Config {
|
||||||
vapidEmail: null as string | null,
|
vapidEmail: null as string | null,
|
||||||
generateVapidKeys: true, // Generate keys automatically
|
generateVapidKeys: true, // Generate keys automatically
|
||||||
bellNotificationsEnabled: true, // Enable bell notifications by default
|
bellNotificationsEnabled: true, // Enable bell notifications by default
|
||||||
|
// Local bypass configuration
|
||||||
|
allowLocalBypass: false,
|
||||||
|
localAuthToken: null as string | null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for help flag first
|
// Check for help flag first
|
||||||
|
|
@ -197,6 +205,11 @@ function parseArgs(): Config {
|
||||||
i++; // Skip the email value in next iteration
|
i++; // Skip the email value in next iteration
|
||||||
} else if (args[i] === '--generate-vapid-keys') {
|
} else if (args[i] === '--generate-vapid-keys') {
|
||||||
config.generateVapidKeys = true;
|
config.generateVapidKeys = true;
|
||||||
|
} else if (args[i] === '--allow-local-bypass') {
|
||||||
|
config.allowLocalBypass = true;
|
||||||
|
} else if (args[i] === '--local-auth-token' && i + 1 < args.length) {
|
||||||
|
config.localAuthToken = args[i + 1];
|
||||||
|
i++; // Skip the token value in next iteration
|
||||||
} else if (args[i].startsWith('--')) {
|
} else if (args[i].startsWith('--')) {
|
||||||
// Unknown argument
|
// Unknown argument
|
||||||
logger.error(`Unknown argument: ${args[i]}`);
|
logger.error(`Unknown argument: ${args[i]}`);
|
||||||
|
|
@ -428,6 +441,8 @@ export async function createApp(): Promise<AppInstance> {
|
||||||
isHQMode: config.isHQMode,
|
isHQMode: config.isHQMode,
|
||||||
bearerToken: remoteBearerToken || undefined, // Token that HQ must use to auth with us
|
bearerToken: remoteBearerToken || undefined, // Token that HQ must use to auth with us
|
||||||
authService, // Add enhanced auth service for JWT tokens
|
authService, // Add enhanced auth service for JWT tokens
|
||||||
|
allowLocalBypass: config.allowLocalBypass,
|
||||||
|
localAuthToken: config.localAuthToken || undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Serve static files with .html extension handling
|
// Serve static files with .html extension handling
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue