70 lines
2.0 KiB
TypeScript
70 lines
2.0 KiB
TypeScript
import type { Request, Response, NextFunction } from 'express'
|
|
import { config } from './config'
|
|
import { logger } from './logger'
|
|
|
|
/**
|
|
* Simple IP-based authentication middleware
|
|
* Restricts access to only allowed IP addresses
|
|
*/
|
|
export const ipWhitelistAuth = (req: Request, res: Response, next: NextFunction) => {
|
|
// Skip if no IP restrictions configured
|
|
if (config.security.allowedIPs.length === 0) {
|
|
logger.warn('No IP whitelist configured - allowing all IPs')
|
|
return next()
|
|
}
|
|
|
|
const clientIP = req.ip || req.socket.remoteAddress || 'unknown'
|
|
|
|
logger.info('IP authentication check', {
|
|
clientIP: logger['maskIp'](clientIP),
|
|
allowedIPs: config.security.allowedIPs.length
|
|
})
|
|
|
|
// Check if IP is in whitelist
|
|
const isAllowed = config.security.allowedIPs.some(allowedIP => {
|
|
// Exact match
|
|
if (clientIP === allowedIP) return true
|
|
|
|
// Handle localhost variations
|
|
if (allowedIP === '127.0.0.1' && (clientIP === '::1' || clientIP === '::ffff:127.0.0.1')) return true
|
|
if (allowedIP === '::1' && (clientIP === '127.0.0.1' || clientIP === '::ffff:127.0.0.1')) return true
|
|
|
|
// Handle IPv6 mapped IPv4
|
|
if (clientIP.startsWith('::ffff:') && allowedIP === clientIP.substring(7)) return true
|
|
|
|
return false
|
|
})
|
|
|
|
if (!isAllowed) {
|
|
logger.warn('IP access denied', {
|
|
clientIP: logger['maskIp'](clientIP),
|
|
allowedIPs: config.security.allowedIPs.map(ip => logger['maskIp'](ip))
|
|
})
|
|
|
|
return res.status(403).json({
|
|
success: false,
|
|
error: 'Access denied: IP address not authorized',
|
|
code: 'IP_NOT_ALLOWED'
|
|
})
|
|
}
|
|
|
|
logger.info('IP authentication successful', {
|
|
clientIP: logger['maskIp'](clientIP)
|
|
})
|
|
|
|
next()
|
|
}
|
|
|
|
/**
|
|
* Get client IP address with proper handling of proxies
|
|
*/
|
|
export const getClientIP = (req: Request): string => {
|
|
return (
|
|
req.headers['x-forwarded-for'] as string ||
|
|
req.headers['x-real-ip'] as string ||
|
|
req.connection.remoteAddress ||
|
|
req.socket.remoteAddress ||
|
|
req.ip ||
|
|
'unknown'
|
|
).split(',')[0].trim()
|
|
} |