268
DEPLOYMENT.md
Archivo normal
268
DEPLOYMENT.md
Archivo normal
@@ -0,0 +1,268 @@
|
||||
# Deployment con Nginx - API Ping Service
|
||||
|
||||
Esta guía explica cómo configurar nginx como proxy reverso para el servicio de ping, asegurando que se obtenga correctamente la IP real de los clientes.
|
||||
|
||||
## 🔧 Configuración de Nginx
|
||||
|
||||
### 1. Configuración Básica
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
# Configuración para obtener IP real del cliente
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
# IPs confiables (ajustar según tu infraestructura)
|
||||
set_real_ip_from 10.0.0.0/8;
|
||||
set_real_ip_from 172.16.0.0/12;
|
||||
set_real_ip_from 192.168.0.0/16;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Rate Limiting en Nginx
|
||||
|
||||
```nginx
|
||||
# En el bloque http {}
|
||||
http {
|
||||
# Zona de rate limiting para API general
|
||||
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
|
||||
|
||||
# Zona específica para ping (más restrictiva)
|
||||
limit_req_zone $binary_remote_addr zone=ping:10m rate=10r/m;
|
||||
|
||||
# Rate limiting por IP real (después del proxy)
|
||||
limit_req_zone $realip_remote_addr zone=real_ip:10m rate=10r/m;
|
||||
}
|
||||
|
||||
# En el bloque server {}
|
||||
server {
|
||||
location /api/ping {
|
||||
limit_req zone=ping burst=5 nodelay;
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
# ... otros headers de proxy
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. SSL/HTTPS (Recomendado)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
ssl_certificate /path/to/certificate.pem;
|
||||
ssl_certificate_key /path/to/private.key;
|
||||
|
||||
# Headers de seguridad
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
|
||||
# ... resto de la configuración
|
||||
}
|
||||
```
|
||||
|
||||
## 🌐 Configuración para Cloudflare
|
||||
|
||||
Si usas Cloudflare, agrega estas IPs confiables:
|
||||
|
||||
```nginx
|
||||
# IPs de Cloudflare (actualizar periódicamente)
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
# ... (ver archivo nginx.conf completo)
|
||||
|
||||
# Headers específicos para Cloudflare
|
||||
real_ip_header CF-Connecting-IP;
|
||||
```
|
||||
|
||||
## 🚀 Pasos de Deployment
|
||||
|
||||
### 1. Preparar la Aplicación
|
||||
|
||||
```bash
|
||||
# Compilar para producción
|
||||
npm run build
|
||||
|
||||
# Iniciar en modo producción
|
||||
npm start
|
||||
|
||||
# O usar PM2 para gestión de procesos
|
||||
npm install -g pm2
|
||||
pm2 start npm --name "api-ping" -- start
|
||||
pm2 save
|
||||
pm2 startup
|
||||
```
|
||||
|
||||
### 2. Configurar Nginx
|
||||
|
||||
```bash
|
||||
# Copiar configuración
|
||||
sudo cp nginx.conf /etc/nginx/sites-available/api-ping
|
||||
sudo ln -s /etc/nginx/sites-available/api-ping /etc/nginx/sites-enabled/
|
||||
|
||||
# Verificar configuración
|
||||
sudo nginx -t
|
||||
|
||||
# Recargar nginx
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 3. Variables de Entorno
|
||||
|
||||
```bash
|
||||
# .env.production
|
||||
NODE_ENV=production
|
||||
TRUST_PROXY=true
|
||||
RATE_LIMIT_MAX=10
|
||||
RATE_LIMIT_WINDOW_MS=60000
|
||||
```
|
||||
|
||||
## 🔍 Verificación del Setup
|
||||
|
||||
### 1. Verificar Headers de Proxy
|
||||
|
||||
```bash
|
||||
# Verificar que nginx está pasando los headers correctos
|
||||
curl -H "X-Forwarded-For: 203.0.113.1" http://your-domain.com/api/status
|
||||
```
|
||||
|
||||
### 2. Test de Rate Limiting
|
||||
|
||||
```bash
|
||||
# Script para probar rate limiting
|
||||
for i in {1..15}; do
|
||||
echo "Request $i:"
|
||||
curl -s http://your-domain.com/api/ping \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"target":"8.8.8.8","count":1}' \
|
||||
| jq '.rateLimit // .error'
|
||||
sleep 1
|
||||
done
|
||||
```
|
||||
|
||||
### 3. Verificar IP del Cliente
|
||||
|
||||
El endpoint `/api/status` ahora incluye información de debugging:
|
||||
|
||||
```json
|
||||
{
|
||||
"clientInfo": {
|
||||
"ip": "203.0.113.1",
|
||||
"isProxied": true,
|
||||
"userAgent": "curl/7.68.0",
|
||||
"proxyHeaders": {
|
||||
"x-forwarded-for": "203.0.113.1",
|
||||
"x-real-ip": "203.0.113.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🛡️ Seguridad Adicional
|
||||
|
||||
### 1. Fail2ban para Protección DDoS
|
||||
|
||||
```ini
|
||||
# /etc/fail2ban/jail.local
|
||||
[api-ping]
|
||||
enabled = true
|
||||
port = http,https
|
||||
filter = api-ping
|
||||
logpath = /var/log/nginx/api-ping-access.log
|
||||
maxretry = 20
|
||||
bantime = 3600
|
||||
findtime = 300
|
||||
```
|
||||
|
||||
### 2. Monitoreo de Logs
|
||||
|
||||
```bash
|
||||
# Monitorear requests sospechosos
|
||||
tail -f /var/log/nginx/api-ping-access.log | grep -E "(429|5[0-9]{2})"
|
||||
|
||||
# Analizar IPs con más requests
|
||||
awk '{print $1}' /var/log/nginx/api-ping-access.log | sort | uniq -c | sort -nr | head -10
|
||||
```
|
||||
|
||||
## 📊 Monitoreo y Métricas
|
||||
|
||||
### 1. Nginx Status
|
||||
|
||||
```nginx
|
||||
location /nginx_status {
|
||||
stub_status on;
|
||||
access_log off;
|
||||
allow 127.0.0.1;
|
||||
deny all;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Logs Estructurados
|
||||
|
||||
```nginx
|
||||
log_format api_ping '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" '
|
||||
'"$http_x_forwarded_for" "$http_x_real_ip" '
|
||||
'$request_time $upstream_response_time';
|
||||
|
||||
access_log /var/log/nginx/api-ping-access.log api_ping;
|
||||
```
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Problema: IP incorrecta
|
||||
|
||||
```bash
|
||||
# Verificar headers que llegan a la aplicación
|
||||
curl -H "X-Debug: true" http://your-domain.com/api/status
|
||||
|
||||
# Verificar configuración de nginx
|
||||
sudo nginx -T | grep -A 10 -B 10 real_ip
|
||||
```
|
||||
|
||||
### Problema: Rate limiting no funciona
|
||||
|
||||
```bash
|
||||
# Verificar que la IP se está obteniendo correctamente
|
||||
# La aplicación debe usar la IP real, no la del proxy (127.0.0.1)
|
||||
```
|
||||
|
||||
### Problema: CORS
|
||||
|
||||
```bash
|
||||
# Verificar headers CORS
|
||||
curl -H "Origin: http://example.com" -H "Access-Control-Request-Method: POST" \
|
||||
-X OPTIONS http://your-domain.com/api/ping -v
|
||||
```
|
||||
|
||||
## 📝 Checklist de Deployment
|
||||
|
||||
- [ ] Aplicación compilada para producción
|
||||
- [ ] PM2 o similar configurado para gestión de procesos
|
||||
- [ ] Nginx configurado con proxy_pass
|
||||
- [ ] Headers X-Forwarded-* configurados
|
||||
- [ ] real_ip_header configurado
|
||||
- [ ] IPs confiables (set_real_ip_from) configuradas
|
||||
- [ ] Rate limiting configurado en nginx (opcional)
|
||||
- [ ] SSL/HTTPS configurado
|
||||
- [ ] Headers de seguridad configurados
|
||||
- [ ] Logs configurados
|
||||
- [ ] Monitoring configurado
|
||||
- [ ] Backup y recovery plan preparado
|
||||
|
||||
Con esta configuración, el servicio obtendrá correctamente la IP real de los clientes incluso cuando esté detrás de nginx, Cloudflare u otros proxies. 🎉
|
||||
@@ -1,4 +1,29 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {};
|
||||
const nextConfig = {
|
||||
// Configuración para proxy reverso (nginx)
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'X-Forwarded-Proto',
|
||||
value: 'https',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
// Configuración experimental para mejorar el manejo de headers
|
||||
experimental: {
|
||||
serverComponentsExternalPackages: ['ping'],
|
||||
},
|
||||
|
||||
// Configuración de red
|
||||
env: {
|
||||
TRUST_PROXY: 'true',
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
167
nginx.conf
Archivo normal
167
nginx.conf
Archivo normal
@@ -0,0 +1,167 @@
|
||||
# Configuración de nginx para API Ping Service
|
||||
# Este archivo debe ser adaptado según tu configuración específica
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name your-domain.com; # Cambiar por tu dominio
|
||||
|
||||
# Redirección HTTPS (recomendado para producción)
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name your-domain.com; # Cambiar por tu dominio
|
||||
|
||||
# Configuración SSL (ajustar rutas según tu setup)
|
||||
ssl_certificate /path/to/your/certificate.pem;
|
||||
ssl_certificate_key /path/to/your/private.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
|
||||
# Headers de seguridad
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
|
||||
|
||||
# Configuración para obtener la IP real del cliente
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
# IPs confiables (ajustar según tu infraestructura)
|
||||
# Cloudflare IPs (si usas Cloudflare)
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
set_real_ip_from 2400:cb00::/32;
|
||||
set_real_ip_from 2606:4700::/32;
|
||||
set_real_ip_from 2803:f800::/32;
|
||||
set_real_ip_from 2405:b500::/32;
|
||||
set_real_ip_from 2405:8100::/32;
|
||||
set_real_ip_from 2a06:98c0::/29;
|
||||
set_real_ip_from 2c0f:f248::/32;
|
||||
|
||||
# IPs locales/privadas (ajustar según tu red)
|
||||
set_real_ip_from 10.0.0.0/8;
|
||||
set_real_ip_from 172.16.0.0/12;
|
||||
set_real_ip_from 192.168.0.0/16;
|
||||
|
||||
# Proxy a Next.js
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000; # Ajustar puerto si es necesario
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Configuración específica para API endpoints
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# Rate limiting adicional en nginx (opcional)
|
||||
limit_req zone=api burst=20 nodelay;
|
||||
|
||||
# Headers CORS para API
|
||||
add_header Access-Control-Allow-Origin "*" always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
|
||||
|
||||
# Manejar preflight requests
|
||||
if ($request_method = OPTIONS) {
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||
add_header Content-Length 0;
|
||||
add_header Content-Type text/plain;
|
||||
return 200;
|
||||
}
|
||||
}
|
||||
|
||||
# Rate limiting para proteger la API
|
||||
location /api/ping {
|
||||
limit_req zone=ping burst=5 nodelay;
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Archivos estáticos con cache
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
add_header X-Cache-Status "STATIC";
|
||||
}
|
||||
|
||||
# Logs
|
||||
access_log /var/log/nginx/api-ping-access.log;
|
||||
error_log /var/log/nginx/api-ping-error.log;
|
||||
}
|
||||
|
||||
# Rate limiting zones (definir en http block)
|
||||
# Agregar estas líneas al bloque http {} en nginx.conf:
|
||||
#
|
||||
# http {
|
||||
# # Rate limiting para API general
|
||||
# limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
|
||||
#
|
||||
# # Rate limiting específico para ping (más restrictivo)
|
||||
# limit_req_zone $binary_remote_addr zone=ping:10m rate=10r/m;
|
||||
#
|
||||
# # Rate limiting por IP real (considerando proxy)
|
||||
# limit_req_zone $realip_remote_addr zone=real_ip:10m rate=10r/m;
|
||||
# }
|
||||
|
||||
# Configuración adicional para development/testing
|
||||
# Archivo separado: nginx-dev.conf
|
||||
#
|
||||
# server {
|
||||
# listen 80;
|
||||
# server_name localhost;
|
||||
#
|
||||
# location / {
|
||||
# proxy_pass http://127.0.0.1:3000;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# }
|
||||
# }
|
||||
@@ -3,17 +3,25 @@ import { NextResponse } from 'next/server';
|
||||
import { RateLimiter } from '../../../lib/rate-limiter';
|
||||
import { validateIP } from '../../../lib/validators';
|
||||
import { PING_CONFIG } from '../../../lib/config.js';
|
||||
import { getClientIP, getProxyInfo } from '../../../lib/proxy-utils.js';
|
||||
|
||||
// Crear instancia del rate limiter
|
||||
const rateLimiter = new RateLimiter();
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
// Obtener IP del cliente para rate limiting
|
||||
const forwarded = request.headers.get('x-forwarded-for');
|
||||
const clientIP = forwarded
|
||||
? forwarded.split(',')[0].trim()
|
||||
: request.headers.get('x-real-ip') || 'unknown';
|
||||
// Obtener IP del cliente considerando proxies (nginx, cloudflare, etc.)
|
||||
const clientIP = getClientIP(request);
|
||||
const proxyInfo = getProxyInfo(request);
|
||||
|
||||
// Log para debugging en desarrollo
|
||||
if (PING_CONFIG.DEBUG.LOG_REQUESTS) {
|
||||
console.log('Ping request from:', {
|
||||
clientIP,
|
||||
isProxied: proxyInfo.isProxied,
|
||||
proxyHeaders: proxyInfo.proxyHeaders
|
||||
});
|
||||
}
|
||||
|
||||
// Verificar rate limit
|
||||
const rateLimitResult = await rateLimiter.checkLimit(clientIP);
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { RateLimiter } from '../../../lib/rate-limiter';
|
||||
import { getClientIP, getProxyInfo } from '../../../lib/proxy-utils.js';
|
||||
|
||||
// Crear instancia del rate limiter
|
||||
const rateLimiter = new RateLimiter();
|
||||
|
||||
export async function GET(request) {
|
||||
try {
|
||||
// Obtener IP del cliente
|
||||
const forwarded = request.headers.get('x-forwarded-for');
|
||||
const clientIP = forwarded
|
||||
? forwarded.split(',')[0].trim()
|
||||
: request.headers.get('x-real-ip') || 'unknown';
|
||||
// Obtener IP del cliente considerando proxies
|
||||
const clientIP = getClientIP(request);
|
||||
const proxyInfo = getProxyInfo(request);
|
||||
|
||||
// Obtener estadísticas del rate limiter para este cliente
|
||||
const rateLimitStats = rateLimiter.getStats(clientIP);
|
||||
@@ -23,7 +22,12 @@ export async function GET(request) {
|
||||
uptime: process.uptime(),
|
||||
clientInfo: {
|
||||
ip: clientIP,
|
||||
rateLimit: rateLimitStats
|
||||
isProxied: proxyInfo.isProxied,
|
||||
userAgent: proxyInfo.userAgent,
|
||||
rateLimit: rateLimitStats,
|
||||
...(process.env.NODE_ENV === 'development' && {
|
||||
proxyHeaders: proxyInfo.proxyHeaders
|
||||
})
|
||||
},
|
||||
features: {
|
||||
maxPingsPerRequest: 10,
|
||||
|
||||
129
src/lib/proxy-utils.js
Archivo normal
129
src/lib/proxy-utils.js
Archivo normal
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Extrae la IP real del cliente considerando proxies como nginx
|
||||
* @param {Request} request - La request de Next.js
|
||||
* @returns {string} La IP del cliente
|
||||
*/
|
||||
export function getClientIP(request) {
|
||||
// Headers que pueden contener la IP real del cliente
|
||||
const forwardedFor = request.headers.get('x-forwarded-for');
|
||||
const realIP = request.headers.get('x-real-ip');
|
||||
const cfConnectingIP = request.headers.get('cf-connecting-ip'); // Cloudflare
|
||||
const trueClientIP = request.headers.get('true-client-ip'); // Cloudflare Enterprise
|
||||
const xClientIP = request.headers.get('x-client-ip');
|
||||
const xForwardedHost = request.headers.get('x-forwarded-host');
|
||||
|
||||
// Prioridad de headers (más confiables primero)
|
||||
let clientIP = null;
|
||||
|
||||
// 1. Cloudflare headers (muy confiables)
|
||||
if (trueClientIP) {
|
||||
clientIP = trueClientIP.trim();
|
||||
} else if (cfConnectingIP) {
|
||||
clientIP = cfConnectingIP.trim();
|
||||
}
|
||||
// 2. X-Real-IP (nginx real_ip_module)
|
||||
else if (realIP) {
|
||||
clientIP = realIP.trim();
|
||||
}
|
||||
// 3. X-Forwarded-For (puede contener múltiples IPs)
|
||||
else if (forwardedFor) {
|
||||
// X-Forwarded-For puede contener múltiples IPs separadas por comas
|
||||
// La primera es la IP original del cliente
|
||||
const ips = forwardedFor.split(',').map(ip => ip.trim());
|
||||
clientIP = ips[0];
|
||||
}
|
||||
// 4. Otros headers menos comunes
|
||||
else if (xClientIP) {
|
||||
clientIP = xClientIP.trim();
|
||||
}
|
||||
|
||||
// Validar que la IP obtenida sea válida
|
||||
if (clientIP && isValidIP(clientIP)) {
|
||||
return clientIP;
|
||||
}
|
||||
|
||||
// Fallback: usar una IP por defecto
|
||||
return '127.0.0.1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida si una cadena es una IP válida (IPv4 o IPv6)
|
||||
* @param {string} ip - La IP a validar
|
||||
* @returns {boolean} True si es válida
|
||||
*/
|
||||
function isValidIP(ip) {
|
||||
// Regex básico para IPv4
|
||||
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
// Regex básico para IPv6
|
||||
const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::1$|^::$/;
|
||||
|
||||
if (ipv4Regex.test(ip)) {
|
||||
// Validar rangos de IPv4 (0-255)
|
||||
const parts = ip.split('.');
|
||||
return parts.every(part => {
|
||||
const num = parseInt(part, 10);
|
||||
return num >= 0 && num <= 255;
|
||||
});
|
||||
}
|
||||
|
||||
if (ipv6Regex.test(ip) || ip === '::1' || ip.startsWith('::ffff:')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene información detallada del proxy y cliente
|
||||
* @param {Request} request - La request de Next.js
|
||||
* @returns {object} Información del cliente y proxy
|
||||
*/
|
||||
export function getProxyInfo(request) {
|
||||
const headers = {};
|
||||
|
||||
// Recopilar todos los headers relevantes para debugging
|
||||
const proxyHeaders = [
|
||||
'x-forwarded-for',
|
||||
'x-real-ip',
|
||||
'x-forwarded-proto',
|
||||
'x-forwarded-host',
|
||||
'x-forwarded-port',
|
||||
'cf-connecting-ip',
|
||||
'true-client-ip',
|
||||
'x-client-ip',
|
||||
'x-cluster-client-ip',
|
||||
'forwarded'
|
||||
];
|
||||
|
||||
proxyHeaders.forEach(header => {
|
||||
const value = request.headers.get(header);
|
||||
if (value) {
|
||||
headers[header] = value;
|
||||
}
|
||||
});
|
||||
|
||||
const clientIP = getClientIP(request);
|
||||
|
||||
return {
|
||||
clientIP,
|
||||
proxyHeaders: headers,
|
||||
isProxied: Object.keys(headers).length > 0,
|
||||
userAgent: request.headers.get('user-agent') || 'unknown'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware helper para configurar trust proxy en desarrollo
|
||||
* @param {Request} request
|
||||
* @returns {object} Configuración de trust
|
||||
*/
|
||||
export function getTrustProxyConfig(request) {
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const trustProxy = process.env.TRUST_PROXY === 'true';
|
||||
|
||||
return {
|
||||
trustProxy: isProduction || trustProxy,
|
||||
clientIP: getClientIP(request),
|
||||
environment: process.env.NODE_ENV || 'development'
|
||||
};
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getClientIP } from './lib/proxy-utils.js';
|
||||
|
||||
export function middleware(request) {
|
||||
// Crear la respuesta
|
||||
const response = NextResponse.next();
|
||||
|
||||
// Obtener IP real del cliente
|
||||
const clientIP = getClientIP(request);
|
||||
|
||||
// Agregar headers de seguridad
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
response.headers.set('X-Frame-Options', 'DENY');
|
||||
@@ -12,16 +16,36 @@ export function middleware(request) {
|
||||
|
||||
// Headers específicos para API
|
||||
if (request.nextUrl.pathname.startsWith('/api/')) {
|
||||
// CORS headers
|
||||
response.headers.set('Access-Control-Allow-Origin', '*');
|
||||
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||
response.headers.set('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
// Headers para debugging de proxy (solo en desarrollo)
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
response.headers.set('X-Debug-Client-IP', clientIP);
|
||||
response.headers.set('X-Debug-Original-IP', request.ip || 'unknown');
|
||||
}
|
||||
|
||||
// Manejar preflight requests
|
||||
if (request.method === 'OPTIONS') {
|
||||
return new Response(null, { status: 200, headers: response.headers });
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'Access-Control-Max-Age': '86400', // 24 horas
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Headers para contenido estático
|
||||
if (request.nextUrl.pathname.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg)$/)) {
|
||||
response.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
Referencia en una nueva incidencia
Block a user