CreaRack-SL

Auditoría de Seguridad — crearack.com

Auditoría de Seguridad — crearack.com

Fecha: 04-04-2026 Auditor: Claude (Anthropic) + Edu Objetivo: Auditoría completa de superficie externa, infraestructura, autenticación y configuración Servidor: Hetzner Cloud CCX — crearack.com Stack: Django 6 + Daphne + Traefik + PostgreSQL 18 + pgbouncer + Valkey + Docker


Resumen Ejecutivo

SeveridadCantidadHallazgos
CRITICAL3Puerto 8000 expuesto sin TLS, Dokploy público, error 500 en /api/credentials/
WARNING5Sin firewall host, Docker Swarm expuesto, cert SSL 45 días, Server header, rate limit fail-open
INFO15+Configuración sólida en general

Puntuación general: La configuración de seguridad es muy sólida en la capa de aplicación (headers, CSP, auth, rate limiting). Los problemas están en la capa de infraestructura (puertos expuestos, firewall ausente).


1. SSL/TLS

Plan de Pruebas

  • Verificar certificado, emisor, expiración
  • Comprobar versión TLS y cipher suite
  • Verificar soporte HTTP/2 y HTTP/3

Resultados

AspectoValorVeredicto
SubjectCN=crearack.com✅ OK
EmisorLet’s Encrypt R13✅ OK
Válido desde18-02-2026✅ OK
Expira19-05-2026 (45 días)⚠️ WARNING — verificar auto-renewal
TLS VersionTLSv1.3✅ Excelente
CipherTLS_AES_128_GCM_SHA256✅ OK
Key ExchangeX25519MLKEM768 (post-quantum hybrid)✅ Excelente
Chain Verifyreturn code: 0 (ok)✅ OK
HTTP/3Alt-Svc: h3=":443"ℹ️ Soporte QUIC disponible

Recomendaciones

  • Verificar que Traefik tiene ACME auto-renewal configurado
  • Si usa DNS challenge con Cloudflare, confirmar que el token API sigue activo

2. HTTP Security Headers

Plan de Pruebas

  • Verificar presencia y valores de todos los headers de seguridad estándar
  • Comprobar que no se filtra información del servidor

Resultados

HeaderValorVeredicto
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preload✅ Excelente — configuración máxima
Content-Security-PolicyRestrictiva, nonce-based script-src✅ Excelente
X-Frame-OptionsDENY✅ Excelente
X-Content-Type-Optionsnosniff✅ OK
Referrer-Policystrict-origin-when-cross-origin✅ OK
Permissions-Policycamera, microphone, geolocation, payment, USB, etc. desactivados✅ Excelente
Cross-Origin-Opener-Policysame-origin✅ OK
Serverdaphne⚠️ WARNING — revela servidor ASGI
X-RateLimit-*Presente (300 req limit)✅ Rate limiting activo

Recomendaciones

  • Ocultar header Server: daphne via Traefik middleware para dificultar fingerprinting

3. Superficie Externa — Puertos y Servicios

Plan de Pruebas

  • Verificar puertos abiertos desde el exterior (5432, 6379, 6432, 8428, 8000, 3000)
  • Comprobar qué puertos escuchan en 0.0.0.0
  • Verificar servicios internos no accesibles desde fuera

Resultados — Puertos Escuchando

PuertoProcesoBindPropósitoRiesgo
22sshd0.0.0.0SSH✅ OK (key-only)
80docker-proxy0.0.0.0HTTP → Traefik✅ OK
443docker-proxy0.0.0.0HTTPS → Traefik✅ OK
3000docker-proxy0.0.0.0Dokploy panel🔴 CRITICAL
8000docker-proxy0.0.0.0Django directo🔴 CRITICAL
2377dockerd*Docker Swarm manager⚠️ WARNING
7946dockerd*Docker Swarm gossip⚠️ WARNING

Resultados — Accesibilidad Externa

ServicioPuertoAccesible externamenteEstado
PostgreSQL5432❌ Timeout✅ OK
Valkey/Redis6379❌ Timeout✅ OK
pgbouncer6432❌ Timeout✅ OK
VictoriaMetrics8428❌ Timeout✅ OK
Dokploy3000✅ Login page HTML🔴 CRITICAL
Django directo8000✅ Conexión aceptada🔴 CRITICAL

Hallazgos Críticos

🔴 CRITICAL-1: Puerto 8000 (Django/Daphne) expuesto públicamente

Django/Daphne es accesible directamente en http://crearack.com:8000, sin TLS, sin rate limiting de Traefik, sin headers de seguridad de Traefik. Todo el tráfico debería pasar exclusivamente por Traefik (443).

Solución: Eliminar ports: "8000:8000" de compose.prod.yml o cambiar a 127.0.0.1:8000:8000. Traefik accede a web via red Docker interna, no necesita el port mapping.

🔴 CRITICAL-2: Puerto 3000 (Dokploy) accesible públicamente

El panel de administración de Dokploy está accesible desde cualquier IP. Un atacante podría intentar brute-force de credenciales para obtener control total del deployment.

Solución: Añadir regla en Hetzner Firewall para bloquear puerto 3000 excepto IPs de administradores, o configurar Dokploy para bind solo en 127.0.0.1.

Recomendaciones

  • Docker Swarm: Si no se usa multi-nodo, ejecutar docker swarm leave --force. Si se necesita, restringir via firewall y habilitar autolock.

4. Firewall

Plan de Pruebas

  • Verificar estado de UFW/iptables en el host
  • Comprobar reglas de Hetzner Cloud Firewall

Resultados

AspectoEstadoVeredicto
UFWInactivo⚠️ WARNING
iptablesINPUT ACCEPT, 0 reglas⚠️ WARNING
Hetzner Cloud FirewallExterno (bloquea 5432, 6379, etc.)✅ Funcional

⚠️ WARNING: Sin firewall a nivel de host

El servidor depende exclusivamente del Hetzner Cloud Firewall (externo). Si el firewall de Hetzner se desactiva o misconfigura, todos los puertos quedan expuestos.

Solución: Activar UFW como defensa en profundidad:

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp    # SSH
ufw allow 80/tcp    # HTTP
ufw allow 443/tcp   # HTTPS
ufw allow 3000/tcp from <IP_ADMIN>  # Dokploy solo desde IPs autorizadas
ufw enable

5. SSH

Plan de Pruebas

  • Verificar configuración sshd
  • Comprobar fail2ban

Resultados

ConfiguraciónValorVeredicto
PermitRootLoginno✅ OK
PasswordAuthenticationno (key-only)✅ OK
Port22 (default)ℹ️ INFO
fail2banActivo, jail sshd✅ OK
Baneados actualmente0✅ OK

Veredicto: Configuración SSH sólida.

Recomendaciones opcionales

  • Configurar MaxAuthTries 3 en sshd_config
  • Considerar puerto no estándar para reducir ruido en logs

6. Django Admin y Autenticación

Plan de Pruebas

  • Verificar accesibilidad del admin desde IPs no autorizadas
  • Comprobar CSRF protection
  • Verificar rate limiting en login
  • Comprobar cookies de sesión

Resultados

TestResultadoVeredicto
/admin/login/ desde IP autorizadaHTTP 200✅ OK — IP en whitelist
/admin/login/ desde IP no autorizadaHTTP 404 (oculta existencia)✅ Excelente
CSRF protectionToken csrfmiddlewaretoken presente✅ OK
POST sin CSRF tokenHTTP 403 (Forbidden)✅ OK
Rate limit /admin/login/5 req/min✅ OK
Rate limit /accounts/login/5 req/min✅ OK

Cookies

CookieFlagsVeredicto
csrftokenSecure; SameSite=Lax✅ OK (HttpOnly no necesario por diseño Django)
sessionidHttpOnly; Secure; SameSite=Strict✅ Excelente

Veredicto: Autenticación bien protegida.


7. API Exposure

Plan de Pruebas

  • Verificar endpoints sensibles sin autenticación
  • Comprobar CORS
  • Verificar que Swagger/API docs no son públicos

Resultados — Endpoints Sin Auth

EndpointHTTP CodeVeredicto
/302 → /login✅ Requiere auth
/api/docs404✅ Protegido (AdminPathsMiddleware)
/api/racks/404✅ Protegido
/api/monitoring/targets/404✅ Protegido
/api/network/devices/404✅ Protegido
/api/signup (POST)404✅ No desplegado aún
/metrics403✅ Bloqueado por MetricsIPRestrictionMiddleware
/health200✅ Público intencionalmente (load balancer)
/.env404✅ No expuesto
/static/404✅ Sin directory listing
/media/404✅ Sin directory listing
/api/credentials/500🔴 CRITICAL

🔴 CRITICAL-3: /api/credentials/ devuelve HTTP 500

Respuesta: {"error": "Schema for status 401 is not set in response dict_keys([200])", "type": "ConfigError"}

El endpoint debería devolver 401, pero el schema de Django Ninja solo define respuesta para 200. Aunque no hay fuga de datos, el error:

  • Revela información interna (framework, tipo de error)
  • Indica a un atacante que el endpoint existe y es funcional
  • Debería devolver 401 limpio

Solución: Añadir 401: ErrorSchema al schema de respuesta del endpoint.

CORS

TestResultadoVeredicto
Origin: https://evil.comSin headers Access-Control-*✅ Excelente
django-cors-headersNo instalado✅ Máximo aislamiento

Rate Limiting

AspectoConfiguraciónVeredicto
Global300 req/min por IP✅ Razonable para SPA
Login endpoints5 req/min✅ Bueno contra brute force
AI endpoints10 req/min✅ OK
/api/monitoring/Sin rate limit⚠️ WARNING
Fail-openSi Valkey cae, permite todo⚠️ WARNING — aceptable, documentar

Plan de Remediación

Prioridad Inmediata (CRITICAL)

#HallazgoAcciónEsfuerzo
1Puerto 8000 expuestoEliminar ports: "8000:8000" de compose.prod.yml5 min
2Dokploy puerto 3000 públicoRegla Hetzner Firewall: bloquear 3000 excepto IPs admin5 min
3/api/credentials/ error 500Añadir 401: ErrorSchema al endpoint10 min

Prioridad Media (WARNING)

#HallazgoAcciónEsfuerzo
4Sin firewall hostActivar UFW (allow 22, 80, 443)10 min
5Docker Swarm expuestodocker swarm leave --force si no se usa multi-nodo2 min
6Server header daphneOcultar via Traefik middleware5 min
7Cert SSL 45 díasVerificar auto-renewal ACME5 min
8Rate limit fail-openDocumentado — comportamiento aceptable (si Valkey cae, permite requests; recupera al reconectar)✅ Documentado

Mejoras Opcionales (INFO)

#HallazgoAcción
9SSH puerto defaultConsiderar puerto no estándar
10MaxAuthTriesConfigurar a 3 en sshd_config
11CSP connect-src localhostCondicionar solo en dev settings

Conclusión

La capa de aplicación de CreaRack Pro está excelentemente protegida: TLS 1.3 con intercambio post-cuántico, HSTS con preload, CSP restrictiva con nonces, headers de seguridad completos, rate limiting, admin con restricción por IP, CSRF activo, cookies seguras, sin CORS, sin directory listing.

Los 3 hallazgos críticos identificados fueron remediados el mismo día:

Remediaciones aplicadas (04-04-2026):

  • ✅ Puerto 8000 bloqueado externamente via iptables DOCKER-USER (solo localhost + Docker interno)
  • ✅ Puerto 3000 (Dokploy) restringido a IP admin 93.176.0.0/16 via iptables DOCKER-USER
  • ✅ /api/credentials/ corregido: schema incluye 401/403, ya no devuelve 500
  • ✅ Reglas iptables persistidas con netfilter-persistent

Postura de seguridad actual: Excelente.


Generado por: Claude (Anthropic) para CreaRack Pro Fecha: 04-04-2026

Véase también

  • [[crearack-tech—reports—security-audit]] — auditoría de seguridad y licencias
  • [[crearack-tech—guides—security-guide]] — guía de seguridad
  • [[crearack-tech—guides—secret-rotation-playbook]] — playbook de rotación de secretos
  • [[crearack-tech—reports—infrastructure-robustness-audit-04-04-2026]] — auditoría de robustez infra
  • [[workspace-tech—tecnico—security-audit-11-04-2026]] — auditoría seguridad workspace
  • [[crearack-tech—backend—passkeys-authentication]] — autenticación con passkeys