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
| Severidad | Cantidad | Hallazgos |
|---|---|---|
| CRITICAL | 3 | Puerto 8000 expuesto sin TLS, Dokploy público, error 500 en /api/credentials/ |
| WARNING | 5 | Sin firewall host, Docker Swarm expuesto, cert SSL 45 días, Server header, rate limit fail-open |
| INFO | 15+ | 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
| Aspecto | Valor | Veredicto |
|---|---|---|
| Subject | CN=crearack.com | ✅ OK |
| Emisor | Let’s Encrypt R13 | ✅ OK |
| Válido desde | 18-02-2026 | ✅ OK |
| Expira | 19-05-2026 (45 días) | ⚠️ WARNING — verificar auto-renewal |
| TLS Version | TLSv1.3 | ✅ Excelente |
| Cipher | TLS_AES_128_GCM_SHA256 | ✅ OK |
| Key Exchange | X25519MLKEM768 (post-quantum hybrid) | ✅ Excelente |
| Chain Verify | return code: 0 (ok) | ✅ OK |
| HTTP/3 | Alt-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
| Header | Valor | Veredicto |
|---|---|---|
| Strict-Transport-Security | max-age=31536000; includeSubDomains; preload | ✅ Excelente — configuración máxima |
| Content-Security-Policy | Restrictiva, nonce-based script-src | ✅ Excelente |
| X-Frame-Options | DENY | ✅ Excelente |
| X-Content-Type-Options | nosniff | ✅ OK |
| Referrer-Policy | strict-origin-when-cross-origin | ✅ OK |
| Permissions-Policy | camera, microphone, geolocation, payment, USB, etc. desactivados | ✅ Excelente |
| Cross-Origin-Opener-Policy | same-origin | ✅ OK |
| Server | daphne | ⚠️ WARNING — revela servidor ASGI |
| X-RateLimit-* | Presente (300 req limit) | ✅ Rate limiting activo |
Recomendaciones
- Ocultar header
Server: daphnevia 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
| Puerto | Proceso | Bind | Propósito | Riesgo |
|---|---|---|---|---|
| 22 | sshd | 0.0.0.0 | SSH | ✅ OK (key-only) |
| 80 | docker-proxy | 0.0.0.0 | HTTP → Traefik | ✅ OK |
| 443 | docker-proxy | 0.0.0.0 | HTTPS → Traefik | ✅ OK |
| 3000 | docker-proxy | 0.0.0.0 | Dokploy panel | 🔴 CRITICAL |
| 8000 | docker-proxy | 0.0.0.0 | Django directo | 🔴 CRITICAL |
| 2377 | dockerd | * | Docker Swarm manager | ⚠️ WARNING |
| 7946 | dockerd | * | Docker Swarm gossip | ⚠️ WARNING |
Resultados — Accesibilidad Externa
| Servicio | Puerto | Accesible externamente | Estado |
|---|---|---|---|
| PostgreSQL | 5432 | ❌ Timeout | ✅ OK |
| Valkey/Redis | 6379 | ❌ Timeout | ✅ OK |
| pgbouncer | 6432 | ❌ Timeout | ✅ OK |
| VictoriaMetrics | 8428 | ❌ Timeout | ✅ OK |
| Dokploy | 3000 | ✅ Login page HTML | 🔴 CRITICAL |
| Django directo | 8000 | ✅ 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
| Aspecto | Estado | Veredicto |
|---|---|---|
| UFW | Inactivo | ⚠️ WARNING |
| iptables | INPUT ACCEPT, 0 reglas | ⚠️ WARNING |
| Hetzner Cloud Firewall | Externo (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ón | Valor | Veredicto |
|---|---|---|
| PermitRootLogin | no | ✅ OK |
| PasswordAuthentication | no (key-only) | ✅ OK |
| Port | 22 (default) | ℹ️ INFO |
| fail2ban | Activo, jail sshd | ✅ OK |
| Baneados actualmente | 0 | ✅ OK |
Veredicto: Configuración SSH sólida.
Recomendaciones opcionales
- Configurar
MaxAuthTries 3en 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
| Test | Resultado | Veredicto |
|---|---|---|
/admin/login/ desde IP autorizada | HTTP 200 | ✅ OK — IP en whitelist |
/admin/login/ desde IP no autorizada | HTTP 404 (oculta existencia) | ✅ Excelente |
| CSRF protection | Token csrfmiddlewaretoken presente | ✅ OK |
| POST sin CSRF token | HTTP 403 (Forbidden) | ✅ OK |
Rate limit /admin/login/ | 5 req/min | ✅ OK |
Rate limit /accounts/login/ | 5 req/min | ✅ OK |
Cookies
| Cookie | Flags | Veredicto |
|---|---|---|
csrftoken | Secure; SameSite=Lax | ✅ OK (HttpOnly no necesario por diseño Django) |
sessionid | HttpOnly; 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
| Endpoint | HTTP Code | Veredicto |
|---|---|---|
/ | 302 → /login | ✅ Requiere auth |
/api/docs | 404 | ✅ Protegido (AdminPathsMiddleware) |
/api/racks/ | 404 | ✅ Protegido |
/api/monitoring/targets/ | 404 | ✅ Protegido |
/api/network/devices/ | 404 | ✅ Protegido |
/api/signup (POST) | 404 | ✅ No desplegado aún |
/metrics | 403 | ✅ Bloqueado por MetricsIPRestrictionMiddleware |
/health | 200 | ✅ Público intencionalmente (load balancer) |
/.env | 404 | ✅ 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
| Test | Resultado | Veredicto |
|---|---|---|
Origin: https://evil.com | Sin headers Access-Control-* | ✅ Excelente |
| django-cors-headers | No instalado | ✅ Máximo aislamiento |
Rate Limiting
| Aspecto | Configuración | Veredicto |
|---|---|---|
| Global | 300 req/min por IP | ✅ Razonable para SPA |
| Login endpoints | 5 req/min | ✅ Bueno contra brute force |
| AI endpoints | 10 req/min | ✅ OK |
/api/monitoring/ | Sin rate limit | ⚠️ WARNING |
| Fail-open | Si Valkey cae, permite todo | ⚠️ WARNING — aceptable, documentar |
Plan de Remediación
Prioridad Inmediata (CRITICAL)
| # | Hallazgo | Acción | Esfuerzo |
|---|---|---|---|
| 1 | Puerto 8000 expuesto | Eliminar ports: "8000:8000" de compose.prod.yml | 5 min |
| 2 | Dokploy puerto 3000 público | Regla Hetzner Firewall: bloquear 3000 excepto IPs admin | 5 min |
| 3 | /api/credentials/ error 500 | Añadir 401: ErrorSchema al endpoint | 10 min |
Prioridad Media (WARNING)
| # | Hallazgo | Acción | Esfuerzo |
|---|---|---|---|
| 4 | Sin firewall host | Activar UFW (allow 22, 80, 443) | 10 min |
| 5 | Docker Swarm expuesto | docker swarm leave --force si no se usa multi-nodo | 2 min |
| 6 | Server header daphne | Ocultar via Traefik middleware | 5 min |
| 7 | Cert SSL 45 días | Verificar auto-renewal ACME | 5 min |
| 8 | Rate limit fail-open | Documentado — comportamiento aceptable (si Valkey cae, permite requests; recupera al reconectar) | ✅ Documentado |
Mejoras Opcionales (INFO)
| # | Hallazgo | Acción |
|---|---|---|
| 9 | SSH puerto default | Considerar puerto no estándar |
| 10 | MaxAuthTries | Configurar a 3 en sshd_config |
| 11 | CSP connect-src localhost | Condicionar 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
Referenciado desde
- Auditoría de Robustez Infraestructural — CreaRack Pro SaaS
- Auditoría de Seguridad y Licencias - CreaRack Pro
- Auditoría Exhaustiva Abril 2026 — CreaRack Pro v1.0.58
- Auditoría Punto Cero — CreaRack Pro v1.0.51
- Guía de Seguridad — CreaRack Pro
- Runbook · Endurecer firewall Hetzner: quitar whitelist de IPs personales y dejar admin solo por NetBird
- Secret Rotation Playbook — CreaRack Pro
- security-audit-11-04-2026