rss2/SECURITY_GUIDE.md
2026-01-13 13:39:51 +01:00

9.7 KiB

🔒 GUÍA DE SEGURIDAD Y MIGRACIÓN - RSS2 Application

⚠️ RESUMEN DE VULNERABILIDADES ENCONTRADAS

CRÍTICAS (Arreglar INMEDIATAMENTE)

  1. Credenciales débiles en .env

    • PostgreSQL password: x
    • Flask SECRET_KEY: secret
    • Grafana password: admin
  2. Servicios expuestos públicamente sin autenticación

    • Qdrant (puertos 6333, 6334) - Base de datos vectorial
    • Prometheus (puerto 9090) - Métricas del sistema
    • cAdvisor (puerto 8081) - Estadísticas de contenedores
  3. Redis sin autenticación

    • Accesible por todos los contenedores sin password

ALTO RIESGO

  1. Ausencia de segmentación de red

    • Todos los servicios en una única red Docker
  2. Ausencia de límites de recursos

    • Contenedores sin límites de CPU/memoria (riesgo de DoS)
  3. Volúmenes con permisos excesivos

    • Código fuente montado con permisos de escritura

🛠️ SOLUCIONES IMPLEMENTADAS

1. Archivo docker-compose.secure.yml

Mejoras de seguridad implementadas:

🔹 Redes Segmentadas

networks:
  frontend:     # Solo nginx y rss2_web
  backend:      # BD, workers, redis, qdrant (interna)
  monitoring:   # Prometheus, Grafana, cAdvisor (interna)

🔹 Puertos Internalizados

  • Eliminados puertos públicos de:

    • Qdrant (6333, 6334) → Solo acceso interno
    • Prometheus (9090) → Solo acceso interno
    • cAdvisor (8081) → Solo acceso interno
    • rss-web-go (8002) → Servicio comentado (duplicado)
  • Único puerto público:

    • Nginx (8001) → Proxy reverso con seguridad
  • Puerto localhost únicamente:

    • Grafana (127.0.0.1:3001) → Acceso solo local o via túnel SSH

🔹 Autenticación en Redis

redis:
  command: >
    redis-server
    --requirepass ${REDIS_PASSWORD}

🔹 Límites de Recursos

Todos los contenedores tienen límites de CPU y memoria:

deploy:
  resources:
    limits:
      cpus: '2'
      memory: 2G
    reservations:
      memory: 512M

🔹 Volúmenes Read-Only

Código fuente montado en modo lectura donde sea posible:

volumes:
  - ./app.py:/app/app.py:ro
  - ./routers:/app/routers:ro
  - ./templates:/app/templates:ro

📋 PASOS DE MIGRACIÓN

Opción A: Migración Gradual (RECOMENDADO para producción)

Paso 1: Generar Credenciales Seguras

# 1. Generar password para PostgreSQL
POSTGRES_PASSWORD=$(openssl rand -base64 32)
echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD"

# 2. Generar password para Redis
REDIS_PASSWORD=$(openssl rand -base64 32)
echo "REDIS_PASSWORD=$REDIS_PASSWORD"

# 3. Generar SECRET_KEY para Flask
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
echo "SECRET_KEY=$SECRET_KEY"

# 4. Generar password para Grafana
GRAFANA_PASSWORD=$(openssl rand -base64 24)
echo "GRAFANA_PASSWORD=$GRAFANA_PASSWORD"

# Guardar estos valores en un lugar seguro (gestor de passwords)

Paso 2: Copiar y Configurar .env Seguro

# Copiar el ejemplo seguro
cp .env.secure.example .env

# Editar .env y pegar las contraseñas generadas
nano .env  # o usa tu editor preferido

Paso 3: Backup de Datos

# Backup de PostgreSQL
docker exec rss2_db pg_dump -U rss rss > backup_$(date +%Y%m%d_%H%M%S).sql

# Backup de Qdrant
tar -czf qdrant_backup_$(date +%Y%m%d_%H%M%S).tar.gz qdrant_storage/

# Backup de Redis (opcional)
docker exec rss2_redis redis-cli --rdb /data/dump.rdb
cp redis-data/dump.rdb redis_backup_$(date +%Y%m%d_%H%M%S).rdb

Paso 4: Detener Servicios Actuales

docker-compose down

Paso 5: Migrar a Configuración Segura

# Renombrar archivo actual (backup)
mv docker-compose.yml docker-compose.yml.insecure.bak

# Usar la versión segura
cp docker-compose.secure.yml docker-compose.yml

# Verificar configuración
docker-compose config

Paso 6: Iniciar con Nueva Configuración

# Iniciar servicios
docker-compose up -d

# Verificar logs
docker-compose logs -f

# Verificar que todos los contenedores están corriendo
docker-compose ps

Paso 7: Verificar Conectividad

# Test web app
curl http://localhost:8001

# Test Redis (desde dentro de un contenedor)
docker exec rss2_web bash -c 'python3 -c "import redis; r = redis.Redis(host=\"redis\", port=6379, password=\"$REDIS_PASSWORD\"); print(r.ping())"'

# Verificar logs de workers
docker-compose logs rss2_tasks_py | tail -20

Opción B: Migración Directa (Para desarrollo/testing)

# 1. Backup de datos (como en Opción A, Paso 3)

# 2. Detener todo
docker-compose down -v  # CUIDADO: -v elimina volúmenes

# 3. Generar credenciales y configurar .env
cp .env.secure.example .env
# Editar .env con credenciales generadas

# 4. Restaurar datos si es necesario
# (Restaurar dump SQL, qdrant_storage, etc.)

# 5. Iniciar con configuración segura
cp docker-compose.secure.yml docker-compose.yml
docker-compose up -d

🔐 ACCESO A SERVICIOS PROTEGIDOS

Grafana (Monitoring)

Ahora solo accesible en localhost. Para acceso remoto:

# Opción 1: Túnel SSH (RECOMENDADO)
ssh -L 3001:localhost:3001 usuario@servidor

# Luego acceder en tu navegador local:
# http://localhost:3001
# Usuario: admin
# Password: El que configuraste en GRAFANA_PASSWORD

Qdrant (Base de Datos Vectorial)

Ya no es accesible públicamente. Para acceso de desarrollo:

# Opción 1: Temporalmente exponer puerto (SOLO para debug)
# Editar docker-compose.yml y descomentar:
#   ports:
#     - "127.0.0.1:6333:6333"

# Opción 2: Acceder desde dentro de la red Docker
docker exec -it rss2_qdrant_worker bash
curl http://qdrant:6333/collections

Prometheus (Métricas)

# Acceso via túnel SSH
ssh -L 9090:localhost:9090 usuario@servidor

# O exponer temporalmente en localhost:
# En docker-compose.yml, prometheus service:
# ports:
#   - "127.0.0.1:9090:9090"

🧪 TESTING DE SEGURIDAD

Verificar que puertos NO son accesibles públicamente:

# Desde FUERA del servidor (desde tu máquina local)

# Estos NO deberían responder:
curl http://servidor:6333  # Qdrant - debe fallar
curl http://servidor:9090  # Prometheus - debe fallar
curl http://servidor:8081  # cAdvisor - debe fallar

# Este SÍ debe responder:
curl http://servidor:8001  # Nginx - debe funcionar

Verificar segmentación de redes:

# Los contenedores NO deberían poder acceder a servicios fuera de su red

# Desde un worker backend, NO debe alcanzar nginx:
docker exec rss2_cluster_py curl http://nginx  # Debería fallar

# Desde monitoring, NO debe alcanzar db:
docker exec rss2_prometheus curl http://db:5432  # Debería fallar

📊 COMPARATIVA: ANTES vs DESPUÉS

Aspecto ANTES (Inseguro) DESPUÉS (Seguro)
Puertos expuestos 7 puertos públicos 1 puerto público + 1 localhost
Autenticación Redis Sin password Autenticado
PostgreSQL Password x (débil) 32+ caracteres aleatorios
Flask SECRET_KEY secret 64 caracteres hex
Segmentación de red Una red única 3 redes aisladas
Límites de recursos Sin límites CPU y RAM limitados
Volúmenes Read-Write Read-Only donde posible
Qdrant público ⚠️ Sí (puerto 6333) Solo interno
Prometheus público ⚠️ Sí (puerto 9090) Solo interno

🚨 CHECKLIST FINAL DE SEGURIDAD

Antes de poner en producción, verifica:

  • Todas las contraseñas generadas aleatoriamente (min 32 caracteres)
  • Archivo .env NO está en el repositorio (revisar .gitignore)
  • Solo puerto 8001 expuesto públicamente
  • Grafana accesible solo en localhost
  • Redis requiere autenticación
  • Todos los workers pueden conectarse a Redis
  • Todos los workers pueden conectarse a PostgreSQL
  • Web app funciona correctamente
  • Búsqueda semántica (Qdrant) funciona
  • Backups automáticos configurados
  • Monitoring (Grafana) accessible via SSH tunnel
  • Firewall del servidor configurado (solo permitir 8001, 22)

🔧 TROUBLESHOOTING

Error: Redis authentication failed

# Verificar que REDIS_PASSWORD está en .env
grep REDIS_PASSWORD .env

# Verificar que los workers tienen la variable
docker exec rss2_web env | grep REDIS

# Reiniciar servicios
docker-compose restart

Error: No puedo acceder a Grafana desde mi máquina

# Asegurarte de que el túnel SSH está activo
ssh -L 3001:localhost:3001 usuario@servidor

# Verificar que Grafana está corriendo
docker-compose ps | grep grafana

# Logs de Grafana
docker-compose logs grafana

Workers no pueden conectarse a Qdrant

# Verificar que Qdrant está en la red backend
docker network inspect rss2_backend | grep qdrant

# Verificar logs de Qdrant
docker-compose logs qdrant

# Test de conectividad desde un worker
docker exec rss2_qdrant_worker curl http://qdrant:6333/collections

📚 RECURSOS ADICIONALES


📝 NOTAS IMPORTANTES

  1. Backup Regular: Configura backups automáticos ANTES de migrar
  2. Testing: Prueba en un entorno de desarrollo primero
  3. Downtime: Planifica una ventana de mantenimiento
  4. Monitoring: Verifica que Grafana funciona después de migrar
  5. Documentation: Documenta las contraseñas en un gestor seguro

Última actualización: 2026-01-12 Autor: Auditoría de Seguridad Automatizada