Add local bypass feature

This commit is contained in:
Peter Steinberger 2025-06-24 03:26:04 +02:00
parent 85ac380095
commit 801438d867
2 changed files with 66 additions and 1 deletions

View file

@ -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

View file

@ -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