diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100755 index 0000000..e8877cf --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "mcp__context7__get-library-docs" + ], + "deny": [], + "ask": [] + } +} diff --git a/.env.example b/.env.example new file mode 100755 index 0000000..b9e4bf8 --- /dev/null +++ b/.env.example @@ -0,0 +1,22 @@ +# Variables de entorno para Traefik +# Copia este archivo a .env y personaliza los valores + +# Configuración general +TZ=America/Argentina/Buenos_Aires + +# Email para Let's Encrypt (OBLIGATORIO) +ACME_EMAIL=admin@nucleoriofrio.com + +# Dominio para el dashboard +DASHBOARD_DOMAIN=traefik.nucleoriofrio.com + +# Contraseña para el dashboard +# Genera con: htpasswd -nb admin tu-password +# Luego copia el resultado completo aquí +DASHBOARD_AUTH=admin:$$apr1$$8evjzEMf$$FfHNgRWn5E9y3W9qZ9Y5z/ + +# Red de Traefik +TRAEFIK_NETWORK=traefik-network + +# Logging +LOG_LEVEL=INFO # DEBUG, INFO, WARN, ERROR diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..e477797 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Environment variables +.env + +# Logs +*.log +logs/ + +# Certificados locales (desarrollo) +certs/ +*.pem +*.key +*.crt + +# Backups +*.tar.gz +*.zip +backups/ + +# OS +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ diff --git a/README.md b/README.md new file mode 100755 index 0000000..8aa8385 --- /dev/null +++ b/README.md @@ -0,0 +1,353 @@ +# Traefik Nucleo - Reverse Proxy Configuration + +Configuración de Traefik v3.5 para el servidor Nucleo002. + +## 📁 Estructura del Proyecto + +``` +traefikNucleo/ +├── docker-compose.yml # Configuración principal de Traefik +├── traefik/ +│ └── traefik.yml # Configuración estática +├── dynamic/ +│ └── middlewares.yml # Middlewares (security, cors, etc.) +├── examples/ +│ ├── whoami-service.yml # Ejemplo de servicio nuevo +│ └── existing-service.yml # Ejemplo para servicios existentes +└── README.md # Este archivo +``` + +## 🚀 Inicio Rápido + +### 1. Configuración inicial + +Antes de iniciar, **DEBES CAMBIAR**: + +1. **Email en `traefik/traefik.yml`** (línea 43): + ```yaml + email: TU-EMAIL@AQUI.com # Para Let's Encrypt + ``` + +2. **Dominio del dashboard en `docker-compose.yml`** (línea 47): + ```yaml + - "traefik.http.routers.dashboard.rule=Host(`traefik.TU-DOMINIO.com`)" + ``` + +3. **Contraseña del dashboard** (línea 54): + ```bash + # Generar hash de contraseña: + htpasswd -nb admin tu-password-seguro + + # Reemplazar en docker-compose.yml: + - "traefik.http.middlewares.dashboard-auth.basicauth.users=HASH-AQUI" + ``` + +### 2. Iniciar Traefik + +```bash +# Desde el directorio traefikNucleo/ +docker compose up -d + +# Ver logs +docker compose logs -f traefik + +# Verificar que está corriendo +docker ps | grep traefik +``` + +### 3. Acceder al Dashboard + +Después de configurar el DNS de tu dominio: + +``` +https://traefik.TU-DOMINIO.com +``` + +Usuario: `admin` +Password: la que configuraste + +## 📋 Conectar Servicios Existentes a Traefik + +### Opción A: Via labels en docker-compose.yml + +Edita el `docker-compose.yml` de tu servicio y agrega: + +```yaml +services: + tu-servicio: + # ... configuración existente ... + + networks: + - tu-red-original + - traefik-network # ← AGREGAR ESTO + + labels: + - "traefik.enable=true" + - "traefik.http.routers.NOMBRE.rule=Host(`DOMINIO.com`)" + - "traefik.http.routers.NOMBRE.entrypoints=websecure" + - "traefik.http.routers.NOMBRE.tls.certresolver=letsencrypt" + - "traefik.http.services.NOMBRE.loadbalancer.server.port=PUERTO" + +networks: + traefik-network: + external: true +``` + +Luego: +```bash +docker compose up -d +``` + +### Opción B: Conectar contenedor corriendo (sin reiniciar) + +```bash +# 1. Conectar a la red de Traefik +docker network connect traefik-network nombre-del-contenedor + +# 2. Agregar labels dinámicamente +docker update \ + --label traefik.enable=true \ + --label traefik.http.routers.NOMBRE.rule='Host(`DOMINIO.com`)' \ + --label traefik.http.routers.NOMBRE.entrypoints=websecure \ + --label traefik.http.routers.NOMBRE.tls.certresolver=letsencrypt \ + --label traefik.http.services.NOMBRE.loadbalancer.server.port=PUERTO \ + nombre-del-contenedor + +# 3. Reiniciar el contenedor para aplicar labels +docker restart nombre-del-contenedor +``` + +## 🔧 Comandos Útiles + +### Gestión de Traefik + +```bash +# Ver logs en tiempo real +docker compose logs -f traefik + +# Reiniciar Traefik (recargar configuración) +docker compose restart traefik + +# Detener Traefik +docker compose down + +# Detener y eliminar volúmenes (CUIDADO: borra certificados SSL) +docker compose down -v +``` + +### Ver configuración activa + +```bash +# Ver todos los routers activos +docker exec traefik traefik healthcheck + +# Inspeccionar contenedor de Traefik +docker inspect traefik +``` + +### Gestión de certificados + +```bash +# Ver certificados almacenados +docker volume inspect traefik-letsencrypt + +# Backup de certificados +docker run --rm -v traefik-letsencrypt:/data -v $(pwd):/backup \ + alpine tar czf /backup/letsencrypt-backup.tar.gz /data +``` + +## 🌐 Ejemplos de Servicios + +### Servicio de prueba (whoami) + +```bash +cd examples/ +docker compose -f whoami-service.yml up -d +``` + +Accede a: `https://whoami.TU-DOMINIO.com` + +### Tus servicios existentes + +Ver archivo `examples/existing-service.yml` para plantilla. + +Servicios actuales que puedes migrar: +- ✅ gitea (puerto 3000) +- ✅ portainer (puerto 9443) +- ✅ authentik (puertos 9100, 9444) +- ✅ openwebui (puerto 3020) +- ✅ snatchgame-client (puerto 3010) +- ✅ planilla-ui (puerto 3008) +- Y más... + +## 🔐 SSL/TLS Automático + +Traefik solicita automáticamente certificados SSL de Let's Encrypt cuando: + +1. El dominio apunta a tu IP pública +2. Los puertos 80 y 443 están accesibles desde internet +3. El contenedor tiene `tls.certresolver=letsencrypt` en sus labels + +### Troubleshooting SSL + +Si los certificados no se generan: + +```bash +# Ver logs de ACME (Let's Encrypt) +docker compose logs traefik | grep acme + +# Verificar que el dominio apunta a tu IP +dig TU-DOMINIO.com + +# Verificar conectividad externa +curl -I http://TU-DOMINIO.com +``` + +## 📊 Middlewares Disponibles + +Los middlewares procesan las peticiones antes de llegar al servicio. + +Usa los middlewares agregándolos a tus labels: + +```yaml +labels: + - "traefik.http.routers.NOMBRE.middlewares=compress,security-headers,rate-limit" +``` + +### Middlewares configurados: + +- `compress`: Compresión gzip +- `security-headers`: Headers de seguridad HTTP +- `cors`: Configuración CORS +- `rate-limit`: Límite de peticiones (100/s) +- `redirect-https`: Redirección forzada a HTTPS +- `basic-auth-example`: Autenticación básica +- `strip-prefix-api`: Remover `/api` del path + +Ver `dynamic/middlewares.yml` para detalles. + +## 🛠️ Personalización Avanzada + +### Agregar más entry points + +Edita `traefik/traefik.yml`: + +```yaml +entryPoints: + custom-port: + address: ":9000" +``` + +Expón el puerto en `docker-compose.yml`: + +```yaml +ports: + - "9000:9000" +``` + +### Configuración TCP/UDP + +Para servicios que no son HTTP (como bases de datos, SSH, etc.): + +Edita `traefik/traefik.yml` y agrega entry points TCP: + +```yaml +entryPoints: + postgres: + address: ":5432" +``` + +Y configura routers TCP en `dynamic/tcp.yml` (crear archivo nuevo). + +## 📈 Monitoreo + +### Métricas Prometheus + +Traefik expone métricas en: + +``` +http://IP-SERVIDOR:8082/metrics +``` + +### Dashboard + +Acceso web completo a: +- Routers activos +- Services +- Middlewares +- Certificados TLS +- Estadísticas en tiempo real + +## ⚠️ Notas Importantes + +1. **Puertos 80 y 443 deben estar libres** antes de iniciar Traefik +2. **Firewall**: Asegúrate de que los puertos están abiertos en tu firewall +3. **DNS**: Los dominios deben apuntar a la IP de tu servidor +4. **Backup**: Haz backup regular del volumen `traefik-letsencrypt` +5. **Let's Encrypt rate limits**: Máximo 5 certificados duplicados por semana + +## 🆘 Solución de Problemas + +### Traefik no inicia + +```bash +# Ver logs detallados +docker compose logs traefik + +# Verificar puertos ocupados +ss -tuln | grep ':80\|:443' + +# Verificar permisos del socket de Docker +ls -la /var/run/docker.sock +``` + +### Servicio no aparece en el dashboard + +```bash +# Verificar que el contenedor está en la red correcta +docker inspect CONTENEDOR | grep -A 10 Networks + +# Verificar labels +docker inspect CONTENEDOR | grep -A 20 Labels + +# Ver logs de Traefik para errores +docker compose logs traefik | grep error +``` + +### Certificado SSL no se genera + +1. Verifica que el dominio resuelve: `dig TU-DOMINIO.com` +2. Verifica conectividad: `curl http://TU-DOMINIO.com` +3. Revisa logs de ACME: `docker compose logs traefik | grep acme` +4. Espera unos minutos (Let's Encrypt puede tardar) + +## 📚 Recursos + +- [Documentación oficial de Traefik](https://doc.traefik.io/traefik/) +- [Traefik en GitHub](https://github.com/traefik/traefik) +- [Let's Encrypt](https://letsencrypt.org/) + +## 🔄 Migración desde Nginx Proxy Manager + +Si vienes de NPM: + +1. Exporta/documenta tus configuraciones de NPM +2. Detén NPM: `docker stop npm npm-db` +3. Inicia Traefik: `docker compose up -d` +4. Migra servicios uno por uno usando los ejemplos +5. Cuando todo funcione, elimina NPM si quieres + +## 🎯 Próximos Pasos + +1. [ ] Cambiar email, dominio y password en la configuración +2. [ ] Iniciar Traefik +3. [ ] Probar con servicio whoami +4. [ ] Migrar un servicio existente +5. [ ] Configurar backups de certificados SSL +6. [ ] Conectar métricas a Prometheus/Grafana (opcional) + +--- + +**Creado para**: Servidor Nucleo002 +**Fecha**: Octubre 2025 +**Versión de Traefik**: v3.5 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 0000000..11be60c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,76 @@ +version: '3.8' + +services: + traefik: + image: traefik:v3.5 + container_name: traefik + restart: unless-stopped + + # Puertos expuestos + ports: + - "80:80" # HTTP + - "443:443" # HTTPS + - "8080:8080" # Dashboard (solo si api.insecure=true) + - "8082:8082" # Métricas Prometheus + + # Variables de entorno + environment: + - TZ=America/Argentina/Buenos_Aires + - CLOUDFLARE_DNS_API_TOKEN=${CF_API_TOKEN} + - CLOUDFLARE_ZONE_API_TOKEN=${CF_API_TOKEN} + + # Volúmenes + volumes: + # Configuración estática + - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro + + # Configuración dinámica + - ./dynamic:/etc/traefik/dynamic:ro + + # Socket de Docker (para auto-descubrimiento) + - /var/run/docker.sock:/var/run/docker.sock:ro + + # Almacenamiento de certificados SSL + - traefik-letsencrypt:/letsencrypt + + # Logs + - traefik-logs:/var/log/traefik + + # Redes + networks: + - traefik-network + - principal + + # Labels para el dashboard de Traefik + labels: + - "traefik.enable=true" + + # Dashboard en traefik.nucleoriofrio.com + - "traefik.http.routers.dashboard.rule=Host(`traefik.nucleoriofrio.com`)" + - "traefik.http.routers.dashboard.entrypoints=websecure" + - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt" + - "traefik.http.routers.dashboard.tls.domains[0].main=nucleoriofrio.com" + - "traefik.http.routers.dashboard.tls.domains[0].sans=*.nucleoriofrio.com" + - "traefik.http.routers.dashboard.service=api@internal" + + # Middleware de autenticación básica para el dashboard + - "traefik.http.routers.dashboard.middlewares=dashboard-auth" + - "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$cJ7gew7R$$OtpXnfijB8Nj/XXRp4rHq1" + # Usuario: admin, Password: admin (CAMBIAR ESTO!) + # Genera tu propio hash con: htpasswd -nb admin tu-password + # o en línea: https://hostingcanada.org/htpasswd-generator/ + +# Redes +networks: + traefik-network: + name: traefik-network + driver: bridge + principal: + external: true + +# Volúmenes +volumes: + traefik-letsencrypt: + name: traefik-letsencrypt + traefik-logs: + name: traefik-logs diff --git a/dynamic/amigos-nucleoriofrio.yml b/dynamic/amigos-nucleoriofrio.yml new file mode 100644 index 0000000..31bc984 --- /dev/null +++ b/dynamic/amigos-nucleoriofrio.yml @@ -0,0 +1,41 @@ +# Configuración dinámica para amigos.nucleoriofrio.com +# Proxy a amigos-app:3001 con autenticación Authentik + +http: + routers: + amigos-nucleoriofrio: + rule: "Host(`amigos.nucleoriofrio.com`)" + entryPoints: + - websecure + service: amigos-nucleoriofrio-service + tls: + certResolver: letsencrypt + middlewares: + - authentik-forward-auth + - amigos-headers + + services: + amigos-nucleoriofrio-service: + loadBalancer: + servers: + - url: "http://amigos-app:3001" + passHostHeader: true + + middlewares: + # Forward Auth con Authentik + authentik-forward-auth: + forwardAuth: + address: "http://authentiknucleo-server-1:9000/outpost.goauthentik.io/auth/traefik" + trustForwardHeader: true + authResponseHeaders: + - X-authentik-username + - X-authentik-email + - X-authentik-uid + - Set-Cookie + + # Headers personalizados para amigos + amigos-headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + X-Forwarded-Scheme: "https" diff --git a/dynamic/authentik-nucleoriofrio.yml b/dynamic/authentik-nucleoriofrio.yml new file mode 100644 index 0000000..9f618c0 --- /dev/null +++ b/dynamic/authentik-nucleoriofrio.yml @@ -0,0 +1,30 @@ +# Configuración dinámica para authentik.nucleoriofrio.com +# Proxy a authentiknucleo-server-1:9000 + +http: + routers: + authentik-nucleoriofrio: + rule: "Host(`authentik.nucleoriofrio.com`)" + entryPoints: + - websecure + service: authentik-nucleoriofrio-service + tls: + certResolver: letsencrypt + middlewares: + - authentik-headers + + services: + authentik-nucleoriofrio-service: + loadBalancer: + servers: + - url: "http://authentiknucleo-server-1:9000" + passHostHeader: true + + middlewares: + authentik-headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + X-Forwarded-Scheme: "https" + customResponseHeaders: + X-Robots-Tag: "noindex, nofollow" diff --git a/dynamic/gitea.yml b/dynamic/gitea.yml new file mode 100644 index 0000000..4e96e42 --- /dev/null +++ b/dynamic/gitea.yml @@ -0,0 +1,17 @@ +# Configuración de Gitea + +http: + routers: + gitea: + rule: "Host(`gitea.nucleoriofrio.com`)" + entryPoints: + - websecure + service: gitea-service + tls: + certResolver: letsencrypt + + services: + gitea-service: + loadBalancer: + servers: + - url: "http://gitea:3000" diff --git a/dynamic/middlewares.yml b/dynamic/middlewares.yml new file mode 100755 index 0000000..265818c --- /dev/null +++ b/dynamic/middlewares.yml @@ -0,0 +1,71 @@ +# Configuración dinámica - Middlewares +# Los middlewares procesan las peticiones antes de llegar al servicio + +http: + middlewares: + # Middleware para comprimir respuestas + compress: + compress: {} + + # Middleware para rate limiting (limitar peticiones) + rate-limit: + rateLimit: + average: 100 # 100 peticiones + period: 1s # por segundo + burst: 50 # permite picos de hasta 50 + + # Middleware para headers de seguridad + security-headers: + headers: + frameDeny: true + browserXssFilter: true + contentTypeNosniff: true + forceSTSHeader: true + stsIncludeSubdomains: true + stsPreload: true + stsSeconds: 31536000 + customResponseHeaders: + X-Powered-By: "Nucleo Rio Frio" + Server: "" + + # Middleware para CORS + cors: + headers: + accessControlAllowMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + accessControlAllowOriginList: + - "*" # CAMBIAR por tus dominios específicos en producción + accessControlAllowHeaders: + - "*" + accessControlMaxAge: 100 + addVaryHeader: true + + # Middleware para redirección HTTPS + redirect-https: + redirectScheme: + scheme: https + permanent: true + + # Middleware de autenticación básica de ejemplo + # Genera usuarios con: htpasswd -nb usuario password + basic-auth-example: + basicAuth: + users: + - "user:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" # user:password + realm: "Área Protegida" + + # Middleware para agregar prefijo a las rutas + strip-prefix-api: + stripPrefix: + prefixes: + - "/api" + + # Middleware para timeout + timeout: + forwardAuth: + address: "http://localhost" + trustForwardHeader: true diff --git a/dynamic/musica-nucleoriofrio.yml b/dynamic/musica-nucleoriofrio.yml new file mode 100644 index 0000000..82a8416 --- /dev/null +++ b/dynamic/musica-nucleoriofrio.yml @@ -0,0 +1,43 @@ +# Configuración dinámica para musica.nucleoriofrio.com +# Proxy a repodructor:3000 (Reproductor de música con Nuxt) + +http: + routers: + musica-nucleoriofrio: + rule: "Host(`musica.nucleoriofrio.com`)" + entryPoints: + - websecure + service: musica-nucleoriofrio-service + tls: + certResolver: letsencrypt + middlewares: + - musica-headers + - musica-body-size + + services: + musica-nucleoriofrio-service: + loadBalancer: + servers: + - url: "http://repodructor:3000" + passHostHeader: true + # Optimizaciones para streaming + responseForwarding: + flushInterval: "100ms" + + middlewares: + # Headers personalizados para el reproductor + musica-headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + X-Forwarded-Scheme: "https" + customResponseHeaders: + X-Frame-Options: "SAMEORIGIN" + X-Content-Type-Options: "nosniff" + X-XSS-Protection: "1; mode=block" + Cache-Control: "public, max-age=3600" + + # Tamaño máximo de body para subir archivos + musica-body-size: + buffering: + maxRequestBodyBytes: 104857600 # 100MB diff --git a/dynamic/portainer.yml b/dynamic/portainer.yml new file mode 100644 index 0000000..8a69088 --- /dev/null +++ b/dynamic/portainer.yml @@ -0,0 +1,23 @@ +# Configuración de Portainer + +http: + routers: + portainer: + rule: "Host(`portainer.nucleoriofrio.com`)" + entryPoints: + - websecure + service: portainer-service + tls: + certResolver: letsencrypt + + services: + portainer-service: + loadBalancer: + servers: + - url: "https://portainer:9443" + serversTransport: portainer-transport + + # Transport para aceptar certificado autofirmado de Portainer + serversTransports: + portainer-transport: + insecureSkipVerify: true diff --git a/dynamic/wifi-nucleoriofrio.yml b/dynamic/wifi-nucleoriofrio.yml new file mode 100644 index 0000000..a37ed76 --- /dev/null +++ b/dynamic/wifi-nucleoriofrio.yml @@ -0,0 +1,45 @@ +# Configuración dinámica para wifi.nucleoriofrio.com +# Proxy a radiusnucleo-node-1:3000 con soporte SSE y WebSocket + +http: + routers: + wifi-nucleoriofrio: + rule: "Host(`wifi.nucleoriofrio.com`)" + entryPoints: + - websecure + service: wifi-nucleoriofrio-service + tls: + certResolver: letsencrypt + middlewares: + - wifi-headers + + services: + wifi-nucleoriofrio-service: + loadBalancer: + servers: + - url: "http://radiusnucleo-node-1:3000" + # Configuración para Server-Sent Events (SSE) + passHostHeader: true + responseForwarding: + flushInterval: 1ms # Para SSE - envía datos inmediatamente + serversTransport: wifi-transport + + # Middleware para headers específicos + middlewares: + wifi-headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + X-Forwarded-Scheme: "https" + # No agregar security headers que puedan interferir con SSE + + # Transport específico para SSE y WebSocket + serversTransports: + wifi-transport: + serverName: radiusnucleo-node-1 + insecureSkipVerify: false + # Timeouts largos para SSE + forwardingTimeouts: + dialTimeout: 30s + responseHeaderTimeout: 0s # Sin timeout para headers de respuesta (SSE) + idleConnTimeout: 90s diff --git a/examples/existing-service.yml b/examples/existing-service.yml new file mode 100755 index 0000000..352bfa4 --- /dev/null +++ b/examples/existing-service.yml @@ -0,0 +1,52 @@ +# Ejemplo: Conectar un servicio EXISTENTE a Traefik +# +# IMPORTANTE: Para conectar servicios que ya están corriendo: +# 1. Agregar el servicio a la red traefik-network +# 2. Agregar los labels de Traefik + +# Opción 1: Conectar la red manualmente +# docker network connect traefik-network nombre-del-contenedor + +# Opción 2: Modificar el docker-compose.yml del servicio existente +# Ejemplo con tu servicio 'gitea': + +version: '3.8' + +services: + gitea: + image: docker.gitea.com/gitea:1.23.7 + container_name: gitea + restart: unless-stopped + + # Agregar la red de Traefik + networks: + - gitea # Red original del servicio + - traefik-network # Red de Traefik + + # Labels para Traefik + labels: + - "traefik.enable=true" + + # Router para el servicio web + - "traefik.http.routers.gitea-web.rule=Host(`gitea.nucleoriofrio.com`)" + - "traefik.http.routers.gitea-web.entrypoints=websecure" + - "traefik.http.routers.gitea-web.tls.certresolver=letsencrypt" + - "traefik.http.services.gitea-web.loadbalancer.server.port=3000" + + # Middlewares opcionales + - "traefik.http.routers.gitea-web.middlewares=compress,security-headers" + + # NOTA: Los puertos ya no necesitan exponerse públicamente + # Traefik se encarga de enrutar el tráfico + # ports: + # - "3000:3000" # Ya no es necesario si usas Traefik + +networks: + gitea: + external: true # Si ya existe + traefik-network: + external: true + +# IMPORTANTE para servicios con múltiples puertos (como Gitea con SSH): +# Si necesitas exponer SSH (puerto 2222), ese puerto SÍ debe estar en ports: +# porque Traefik solo maneja HTTP/HTTPS, no TCP arbitrario (a menos que lo configures) diff --git a/examples/whoami-service.yml b/examples/whoami-service.yml new file mode 100755 index 0000000..caeb541 --- /dev/null +++ b/examples/whoami-service.yml @@ -0,0 +1,35 @@ +# Ejemplo: Servicio whoami con Traefik +# Este es un servicio de prueba para verificar que Traefik funciona correctamente + +version: '3.8' + +services: + whoami: + image: traefik/whoami + container_name: whoami-example + restart: unless-stopped + + # IMPORTANTE: Conectar a la red de Traefik + networks: + - traefik-network + + # Labels para Traefik + labels: + # Habilitar Traefik para este contenedor + - "traefik.enable=true" + + # Definir el router HTTP + - "traefik.http.routers.whoami.rule=Host(`whoami.nucleoriofrio.com`)" + - "traefik.http.routers.whoami.entrypoints=websecure" + - "traefik.http.routers.whoami.tls.certresolver=letsencrypt" + + # Especificar el puerto del contenedor (si no es obvio) + - "traefik.http.services.whoami.loadbalancer.server.port=80" + + # Aplicar middlewares (opcional) + - "traefik.http.routers.whoami.middlewares=compress,security-headers" + +# Usar la red externa de Traefik +networks: + traefik-network: + external: true diff --git a/planV1.md b/planV1.md deleted file mode 100644 index 941bd5b..0000000 --- a/planV1.md +++ /dev/null @@ -1,1274 +0,0 @@ -# Guía de Migración: Nginx Proxy Manager → Traefik - -**Fecha**: 5 de Octubre, 2025 -**Servidor**: nucleo002 -**Configuraciones Extraídas**: 18 Proxy Hosts - ---- - -## 📋 Índice - -1. [Resumen de Configuraciones NPM](#resumen-de-configuraciones-npm) -2. [Configuración Base de Traefik](#configuración-base-de-traefik) -3. [Traducción de Cada Proxy Host](#traducción-de-cada-proxy-host) -4. [Middlewares Comunes](#middlewares-comunes) -5. [Certificados SSL](#certificados-ssl) -6. [Docker Compose Completo](#docker-compose-completo) -7. [Scripts de Migración](#scripts-de-migración) - ---- - -## 1. Resumen de Configuraciones NPM - -### Proxy Hosts Detectados - -| ID | Dominio | Backend | Puerto | SSL | Características Especiales | -|----|---------|---------|--------|-----|----------------------------| -| 2 | portainer.interno.com | portainer | 9443 | Custom | - | -| 3 | gitea.interno.com | gitea | 3000 | Custom | - | -| 4 | geoids.interno.com | geo-ids | 443 | Custom | **Authentik SSO** | -| 5 | amigos.nucleoriofrio.com | amigos-app | 3001 | Let's Encrypt | **Authentik SSO** (parcial) | -| 7 | whatsappbot.interno.com | whatsapp-router | 3001 | Custom | - | -| 8 | conversation-layer.interno.com | - | - | Custom | - | -| 9 | Nginx.interno.com | npm | 81 | Custom | Admin Panel | -| 10 | planilla.interno.com | planilla-ui | 80 | Custom | **Multi-location** (/, /api, /mcp, /events) | -| 11 | nucleobot.interno.com | whatsapp-router | 3001 | Custom | - | -| 16 | nucleoriofrio.com | z590.interno.com | 3000 | Let's Encrypt | **Custom locations** (/api/chat/stream, /api/chat/) | -| 17 | musica.interno.com | z590.interno.com | 3000 | Custom | **HTTP/2**, **HMR**, Multiple locations | -| 18 | musica.nucleoriofrio.com | repodructor | 3000 | Let's Encrypt | Multiple locations, Cache headers | -| 19 | snatchgame.interno.com | z590.interno.com | 3004 | Custom | **7 locations** (/ws, /colyseus, /api/*) | -| 20 | snatchgame.nucleoriofrio.com | snatchgame | 2567 | Let's Encrypt | **6 locations**, Asset caching | -| 21 | duplicati.interno.com | duplicati | - | Custom | - | -| 22 | internet.nucleoriofrio.com | z590.interno.com | 80 | Let's Encrypt | - | -| 23 | wifi.nucleoriofrio.com | radiusnucleo-node-1 | - | Let's Encrypt | - | - -### Características Globales - -- **HSTS**: Habilitado en todos (`max-age=63072000; preload`) -- **Force SSL**: Mayoría de hosts -- **Block Exploits**: Todos -- **WebSocket Support**: Habilitado donde se necesita -- **HTTP/2**: Solo musica.interno.com - ---- - -## 2. Configuración Base de Traefik - -### 2.1 Docker Compose - Traefik - -```yaml -version: '3.8' - -services: - traefik: - image: traefik:v3.1 - container_name: traefik - restart: unless-stopped - security_opt: - - no-new-privileges:true - networks: - - principal - ports: - - "80:80" - - "443:443" - - "8080:8080" # Dashboard - environment: - - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} # Si usas Cloudflare - - CF_ZONE_API_TOKEN=${CF_ZONE_API_TOKEN} - volumes: - - /etc/localtime:/etc/localtime:ro - - /var/run/docker.sock:/var/run/docker.sock:ro - - ./traefik.yml:/traefik.yml:ro - - ./config/:/config/:ro - - ./acme.json:/acme.json - - ./certificates/:/certificates/:ro - labels: - - "traefik.enable=true" - - # Dashboard - - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.interno.com`)" - - "traefik.http.routers.traefik-dashboard.entrypoints=https" - - "traefik.http.routers.traefik-dashboard.tls=true" - - "traefik.http.routers.traefik-dashboard.service=api@internal" - - "traefik.http.routers.traefik-dashboard.middlewares=auth@file" - -networks: - principal: - external: true -``` - -### 2.2 traefik.yml (Static Configuration) - -```yaml -api: - dashboard: true - debug: false - -entryPoints: - http: - address: ":80" - http: - redirections: - entryPoint: - to: https - scheme: https - permanent: true - - https: - address: ":443" - http: - tls: - options: default - certResolver: letsencrypt - domains: - - main: "*.interno.com" - - main: "*.nucleoriofrio.com" - http2: - maxConcurrentStreams: 250 - - metrics: - address: ":8082" - -providers: - docker: - endpoint: "unix:///var/run/docker.sock" - exposedByDefault: false - network: principal - watch: true - - file: - directory: /config - watch: true - -certificatesResolvers: - letsencrypt: - acme: - email: admin@nucleoriofrio.com - storage: /acme.json - httpChallenge: - entryPoint: http - # O para wildcard con DNS challenge: - # dnsChallenge: - # provider: cloudflare - # delayBeforeCheck: 0 - - custom: - acme: - tlsChallenge: {} - storage: /acme.json - -# Global HTTP settings -serversTransport: - insecureSkipVerify: true # Solo si usas certificados autofirmados en backend - -# Logging -log: - level: INFO - filePath: /logs/traefik.log - format: json - -accessLog: - filePath: /logs/access.log - format: json - bufferingSize: 100 - -# Metrics (opcional) -metrics: - prometheus: - addEntryPointsLabels: true - addServicesLabels: true - entryPoint: metrics -``` - ---- - -## 3. Traducción de Cada Proxy Host - -### 3.1 Portainer (portainer.interno.com) - -**NPM Original**: -- Backend: `portainer:9443` (HTTPS) -- SSL: Custom certificate -- Force SSL - -**Traefik Labels**: - -```yaml -# En docker-compose del stack portainer -services: - portainer: - # ... resto de config - labels: - - "traefik.enable=true" - - "traefik.http.routers.portainer.rule=Host(`portainer.interno.com`)" - - "traefik.http.routers.portainer.entrypoints=https" - - "traefik.http.routers.portainer.tls=true" - - "traefik.http.routers.portainer.tls.certresolver=custom" - - - "traefik.http.services.portainer.loadbalancer.server.port=9443" - - "traefik.http.services.portainer.loadbalancer.server.scheme=https" - - # Middlewares - - "traefik.http.routers.portainer.middlewares=hsts-headers,block-exploits" - networks: - - principal -``` - ---- - -### 3.2 Gitea (gitea.interno.com) - -**NPM Original**: -- Backend: `gitea:3000` (HTTP) -- SSL: Custom -- No Force SSL - -**Traefik Labels**: - -```yaml -services: - gitea: - labels: - - "traefik.enable=true" - - "traefik.http.routers.gitea.rule=Host(`gitea.interno.com`)" - - "traefik.http.routers.gitea.entrypoints=https" - - "traefik.http.routers.gitea.tls=true" - - "traefik.http.routers.gitea.tls.certresolver=custom" - - - "traefik.http.services.gitea.loadbalancer.server.port=3000" - - - "traefik.http.routers.gitea.middlewares=hsts-headers,block-exploits" - networks: - - principal -``` - ---- - -### 3.3 GeoIDs con Authentik SSO (geoids.interno.com) - -**NPM Original**: -- Backend: `geo-ids:443` (HTTPS) -- Auth: Authentik via `auth_request` -- WebSocket support -- Custom buffers - -**Traefik Labels + Middleware File**: - -```yaml -services: - geo-ids: - labels: - - "traefik.enable=true" - - "traefik.http.routers.geoids.rule=Host(`geoids.interno.com`)" - - "traefik.http.routers.geoids.entrypoints=https" - - "traefik.http.routers.geoids.tls=true" - - "traefik.http.routers.geoids.tls.certresolver=custom" - - - "traefik.http.services.geoids.loadbalancer.server.port=443" - - "traefik.http.services.geoids.loadbalancer.server.scheme=https" - - # Middlewares (orden importa!) - - "traefik.http.routers.geoids.middlewares=authentik-geoids@file,hsts-headers,websocket-headers" - networks: - - principal -``` - -**Middleware File** (`/config/middlewares-authentik.yml`): - -```yaml -http: - middlewares: - authentik-geoids: - forwardAuth: - address: "http://authentik-server:9000/outpost.goauthentik.io/auth/nginx" - trustForwardHeader: true - authResponseHeaders: - - "X-authentik-username" - - "X-authentik-groups" - - "X-authentik-email" - - "X-authentik-name" - - "X-authentik-uid" - - "Set-Cookie" -``` - ---- - -### 3.4 Planilla Multi-Location (planilla.interno.com) - -**NPM Original**: -- `/` → `planilla-ui:80` -- `/api` → `planilla-api:4000` -- `/mcp` → `planilla-mcp:5000` -- `/events` → `planilla-api:4000` -- WebSocket en todos - -**Traefik Dynamic Config** (`/config/planilla.yml`): - -```yaml -http: - routers: - planilla-ui: - rule: "Host(`planilla.interno.com`)" - entryPoints: - - https - service: planilla-ui-svc - middlewares: - - hsts-headers - - websocket-headers - tls: - certResolver: custom - priority: 1 - - planilla-api: - rule: "Host(`planilla.interno.com`) && PathPrefix(`/api`)" - entryPoints: - - https - service: planilla-api-svc - middlewares: - - hsts-headers - - websocket-headers - tls: - certResolver: custom - priority: 10 - - planilla-mcp: - rule: "Host(`planilla.interno.com`) && PathPrefix(`/mcp`)" - entryPoints: - - https - service: planilla-mcp-svc - middlewares: - - hsts-headers - - websocket-headers - tls: - certResolver: custom - priority: 10 - - planilla-events: - rule: "Host(`planilla.interno.com`) && PathPrefix(`/events`)" - entryPoints: - - https - service: planilla-api-svc # Mismo que /api - middlewares: - - hsts-headers - - websocket-headers - tls: - certResolver: custom - priority: 10 - - services: - planilla-ui-svc: - loadBalancer: - servers: - - url: "http://planilla-ui:80" - - planilla-api-svc: - loadBalancer: - servers: - - url: "http://planilla-api:4000" - - planilla-mcp-svc: - loadBalancer: - servers: - - url: "http://planilla-mcp:5000" -``` - ---- - -### 3.5 Música con HMR (musica.interno.com) - -**NPM Original**: -- HTTP/2: ON -- Multiple locations: `/_nuxt/`, `/__nuxt_devtools__/`, `/api/` -- WebSocket para HMR -- Proxy buffering OFF -- Large client body - -**Traefik Config** (`/config/musica.yml`): - -```yaml -http: - routers: - musica-nuxt: - rule: "Host(`musica.interno.com`) && PathPrefix(`/_nuxt/`)" - entryPoints: - - https - service: musica-z590-svc - middlewares: - - websocket-headers - - no-buffering - tls: - certResolver: custom - priority: 20 - - musica-devtools: - rule: "Host(`musica.interno.com`) && PathPrefix(`/__nuxt_devtools__/`)" - entryPoints: - - https - service: musica-z590-svc - middlewares: - - websocket-headers - - no-buffering - tls: - certResolver: custom - priority: 20 - - musica-api: - rule: "Host(`musica.interno.com`) && PathPrefix(`/api/`)" - entryPoints: - - https - service: musica-z590-svc - middlewares: - - no-buffering - - large-timeout - tls: - certResolver: custom - priority: 15 - - musica-root: - rule: "Host(`musica.interno.com`)" - entryPoints: - - https - service: musica-z590-svc - middlewares: - - hsts-headers - tls: - certResolver: custom - priority: 1 - - services: - musica-z590-svc: - loadBalancer: - servers: - - url: "http://192.168.87.135:3000" # z590.interno.com - passHostHeader: true - - middlewares: - no-buffering: - buffering: - maxRequestBodyBytes: 104857600 # 100MB - memRequestBodyBytes: 104857600 - maxResponseBodyBytes: 0 # Sin límite - memResponseBodyBytes: 0 - retryExpression: "IsNetworkError() && Attempts() < 2" - - large-timeout: - buffering: - maxRequestBodyBytes: 104857600 - # Los timeouts se configuran en service o entrypoint -``` - ---- - -### 3.6 SnatchGame Complejo (snatchgame.interno.com) - -**NPM Original**: -- 7 locations diferentes -- WebSocket en todos -- Asset caching - -**Traefik Config** (`/config/snatchgame.yml`): - -```yaml -http: - routers: - snatch-ws: - rule: "Host(`snatchgame.interno.com`) && PathPrefix(`/ws`)" - entryPoints: - - https - service: snatch-z590-svc - middlewares: - - websocket-headers - - asset-cache - tls: - certResolver: custom - priority: 30 - - snatch-colyseus: - rule: "Host(`snatchgame.interno.com`) && PathPrefix(`/colyseus`)" - entryPoints: - - https - service: snatch-z590-svc - middlewares: - - websocket-headers - - asset-cache - tls: - certResolver: custom - priority: 25 - - snatch-api-stream: - rule: "Host(`snatchgame.interno.com`) && Path(`/api/dashboard-stream`)" - entryPoints: - - https - service: snatch-z590-svc - middlewares: - - websocket-headers - tls: - certResolver: custom - priority: 40 - - snatch-api-uuids-stream: - rule: "Host(`snatchgame.interno.com`) && Path(`/api/uuids-stream`)" - entryPoints: - - https - service: snatch-z590-svc - middlewares: - - websocket-headers - tls: - certResolver: custom - priority: 40 - - snatch-api-admin: - rule: "Host(`snatchgame.interno.com`) && PathPrefix(`/api/admin/`)" - entryPoints: - - https - service: snatch-z590-svc - middlewares: - - asset-cache - tls: - certResolver: custom - priority: 35 - - snatch-api-wildcard: - rule: "Host(`snatchgame.interno.com`) && PathRegexp(`^/api/.*`)" - entryPoints: - - https - service: snatch-z590-svc - middlewares: - - asset-cache - tls: - certResolver: custom - priority: 20 - - snatch-root: - rule: "Host(`snatchgame.interno.com`)" - entryPoints: - - https - service: snatch-z590-svc - middlewares: - - hsts-headers - - asset-cache - tls: - certResolver: custom - priority: 1 - - services: - snatch-z590-svc: - loadBalancer: - servers: - - url: "http://z590.interno.com:3004" -``` - ---- - -### 3.7 Nucleoriofrio.com con API Chat - -**NPM Original**: -- `/api/chat/stream` → Streaming con buffering OFF -- `/api/chat/` → API normal -- CORS headers -- Large timeouts - -**Traefik Config** (`/config/nucleoriofrio.yml`): - -```yaml -http: - routers: - nucleo-chat-stream: - rule: "Host(`nucleoriofrio.com`) && Path(`/api/chat/stream`)" - entryPoints: - - https - service: nucleo-z590-alt-svc - middlewares: - - no-buffering - - cors-permissive - - large-timeout - tls: - certResolver: letsencrypt - priority: 50 - - nucleo-chat-api: - rule: "Host(`nucleoriofrio.com`) && PathPrefix(`/api/chat/`)" - entryPoints: - - https - service: nucleo-z590-alt-svc - middlewares: - - cors-permissive - tls: - certResolver: letsencrypt - priority: 40 - - nucleo-root: - rule: "Host(`nucleoriofrio.com`)" - entryPoints: - - https - service: nucleo-z590-svc - middlewares: - - hsts-headers - tls: - certResolver: letsencrypt - priority: 1 - - services: - nucleo-z590-svc: - loadBalancer: - servers: - - url: "https://z590.interno.com:3000" - - nucleo-z590-alt-svc: - loadBalancer: - servers: - - url: "https://192.168.87.135:3000" - - middlewares: - cors-permissive: - headers: - accessControlAllowOriginList: - - "*" - accessControlAllowMethods: - - "GET" - - "POST" - - "OPTIONS" - accessControlAllowHeaders: - - "Accept" - - "Authorization" - - "Cache-Control" - - "Content-Type" - - "DNT" - - "If-Modified-Since" - - "Keep-Alive" - - "Origin" - - "User-Agent" - - "X-Requested-With" - accessControlMaxAge: 100 - addVaryHeader: true -``` - ---- - -### 3.8 Música Pública (musica.nucleoriofrio.com) - -**NPM Original**: -- Multiple locations con diferentes propósitos -- Caching de assets -- Security headers -- Streaming optimizations - -**Traefik Config** (`/config/musica-public.yml`): - -```yaml -http: - routers: - musica-pub-nuxt: - rule: "Host(`musica.nucleoriofrio.com`) && PathPrefix(`/_nuxt/`)" - entryPoints: - - https - service: repodructor-svc - middlewares: - - asset-cache-long - tls: - certResolver: letsencrypt - priority: 30 - - musica-pub-music: - rule: "Host(`musica.nucleoriofrio.com`) && PathPrefix(`/music/`)" - entryPoints: - - https - service: repodructor-svc - middlewares: - - no-buffering - - music-streaming - - music-cache - tls: - certResolver: letsencrypt - priority: 25 - - musica-pub-api: - rule: "Host(`musica.nucleoriofrio.com`) && PathPrefix(`/api/`)" - entryPoints: - - https - service: repodructor-svc - middlewares: - - api-timeout - tls: - certResolver: letsencrypt - priority: 20 - - musica-pub-root: - rule: "Host(`musica.nucleoriofrio.com`)" - entryPoints: - - https - service: repodructor-svc - middlewares: - - hsts-headers - - security-headers - tls: - certResolver: letsencrypt - priority: 1 - - services: - repodructor-svc: - loadBalancer: - servers: - - url: "http://repodructor:3000" - - middlewares: - asset-cache-long: - headers: - customResponseHeaders: - Cache-Control: "public, max-age=31536000, immutable" - - music-cache: - headers: - customResponseHeaders: - Cache-Control: "public, max-age=3600" - - music-streaming: - buffering: - maxRequestBodyBytes: 0 # Sin límite - memRequestBodyBytes: 0 - maxResponseBodyBytes: 0 - memResponseBodyBytes: 0 - - api-timeout: - buffering: - maxRequestBodyBytes: 10485760 # 10MB - - security-headers: - headers: - customResponseHeaders: - X-Frame-Options: "SAMEORIGIN" - X-Content-Type-Options: "nosniff" - X-XSS-Protection: "1; mode=block" -``` - ---- - -## 4. Middlewares Comunes - -### 4.1 Archivo de Middlewares Globales (`/config/middlewares.yml`) - -```yaml -http: - middlewares: - # HSTS Headers - hsts-headers: - headers: - stsSeconds: 63072000 - stsIncludeSubdomains: true - stsPreload: true - forceSTSHeader: true - - # WebSocket Support - websocket-headers: - headers: - customRequestHeaders: - X-Forwarded-Proto: "https" - customResponseHeaders: - Upgrade: "$http_upgrade" - Connection: "$http_connection" - - # Block Common Exploits - block-exploits: - headers: - browserXssFilter: true - contentTypeNosniff: true - frameDeny: true - customFrameOptionsValue: "SAMEORIGIN" - ipWhiteList: - sourceRange: - - "127.0.0.1/32" - - "192.168.0.0/16" - - "172.16.0.0/12" - - "10.0.0.0/8" - - # Asset Caching - asset-cache: - headers: - customResponseHeaders: - Cache-Control: "public, max-age=86400" - - # Security Headers Strict - security-strict: - headers: - sslRedirect: true - stsSeconds: 31536000 - stsIncludeSubdomains: true - stsPreload: true - forceSTSHeader: true - frameDeny: true - contentTypeNosniff: true - browserXssFilter: true - referrerPolicy: "strict-origin-when-cross-origin" - permissionsPolicy: "camera=(), microphone=(), geolocation=()" - customResponseHeaders: - X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex" - - # Rate Limiting - rate-limit: - rateLimit: - average: 100 - burst: 200 - period: 1m - - # Basic Auth (ejemplo) - auth-basic: - basicAuth: - users: - - "admin:$apr1$..." # htpasswd hash - removeHeader: true - - # IP Whitelist (ejemplo para admin panels) - admin-whitelist: - ipWhiteList: - sourceRange: - - "192.168.87.0/24" - - "172.19.0.0/16" - - # Compress - compress: - compress: {} - - # Strip Prefix (útil para algunos backends) - strip-api-prefix: - stripPrefix: - prefixes: - - "/api" - forceSlash: false - - # Redirect Scheme (HTTP → HTTPS) - redirect-https: - redirectScheme: - scheme: https - permanent: true - - # Custom Headers for Backend - backend-headers: - headers: - customRequestHeaders: - X-Forwarded-Proto: "https" - X-Forwarded-Scheme: "https" - X-Real-IP: "" # Traefik lo rellena automáticamente - X-Forwarded-For: "" -``` - ---- - -## 5. Certificados SSL - -### 5.1 Certificado Custom (*.interno.com) - -**Opción A: Usar el certificado autofirmado existente** - -```yaml -# En /config/tls.yml -tls: - stores: - default: - defaultCertificate: - certFile: /certificates/interno.com/fullchain.pem - keyFile: /certificates/interno.com/privkey.pem - - certificates: - - certFile: /certificates/interno.com/fullchain.pem - keyFile: /certificates/interno.com/privkey.pem - stores: - - default -``` - -Copiar certificados: -```bash -mkdir -p /srv/traefik/certificates/interno.com -cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/fullchain.pem \ - /srv/traefik/certificates/interno.com/ -cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/privkey.pem \ - /srv/traefik/certificates/interno.com/ -``` - -### 5.2 Let's Encrypt (*.nucleoriofrio.com) - -Ya configurado en `traefik.yml`: - -```yaml -certificatesResolvers: - letsencrypt: - acme: - email: admin@nucleoriofrio.com - storage: /acme.json - httpChallenge: - entryPoint: http -``` - -Para wildcard con DNS Challenge (Cloudflare): - -```yaml -certificatesResolvers: - letsencrypt: - acme: - email: admin@nucleoriofrio.com - storage: /acme.json - dnsChallenge: - provider: cloudflare - delayBeforeCheck: 10 - resolvers: - - "1.1.1.1:53" - - "1.0.0.1:53" -``` - -Variables de entorno necesarias: -```bash -CF_DNS_API_TOKEN=your_cloudflare_token -CF_ZONE_API_TOKEN=your_zone_token -``` - ---- - -## 6. Docker Compose Completo - -### 6.1 Stack Traefik Principal - -```yaml -version: '3.8' - -services: - traefik: - image: traefik:v3.1 - container_name: traefik - restart: unless-stopped - security_opt: - - no-new-privileges:true - networks: - - principal - ports: - - "80:80" - - "443:443" - - "8080:8080" # Dashboard (cambiar puerto si hay conflicto) - environment: - - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} - - CF_ZONE_API_TOKEN=${CF_ZONE_API_TOKEN} - - TZ=America/Argentina/Buenos_Aires - volumes: - - /etc/localtime:/etc/localtime:ro - - /var/run/docker.sock:/var/run/docker.sock:ro - - ./traefik.yml:/traefik.yml:ro - - ./config/:/config/:ro - - ./acme.json:/acme.json - - ./certificates/:/certificates/:ro - - ./logs/:/logs/ - labels: - - "traefik.enable=true" - - # Dashboard - - "traefik.http.routers.dashboard.rule=Host(`traefik.interno.com`)" - - "traefik.http.routers.dashboard.entrypoints=https" - - "traefik.http.routers.dashboard.tls=true" - - "traefik.http.routers.dashboard.tls.certresolver=custom" - - "traefik.http.routers.dashboard.service=api@internal" - - "traefik.http.routers.dashboard.middlewares=admin-whitelist" - -networks: - principal: - external: true -``` - -### 6.2 Estructura de Directorios - -``` -/srv/traefik/ -├── traefik.yml # Config estática -├── acme.json # Certificados LE (chmod 600) -├── docker-compose.yml -├── .env -├── config/ -│ ├── middlewares.yml # Middlewares globales -│ ├── middlewares-authentik.yml -│ ├── tls.yml # Certificados custom -│ ├── planilla.yml # Config dinámica planilla -│ ├── musica.yml -│ ├── snatchgame.yml -│ ├── nucleoriofrio.yml -│ └── musica-public.yml -├── certificates/ -│ └── interno.com/ -│ ├── fullchain.pem -│ └── privkey.pem -└── logs/ - ├── traefik.log - └── access.log -``` - -### 6.3 Preparación - -```bash -# Crear estructura -mkdir -p /srv/traefik/{config,certificates/interno.com,logs} - -# Crear acme.json -touch /srv/traefik/acme.json -chmod 600 /srv/traefik/acme.json - -# Copiar certificados -cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/fullchain.pem \ - /srv/traefik/certificates/interno.com/ -cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/privkey.pem \ - /srv/traefik/certificates/interno.com/ - -# Crear archivos de config -touch /srv/traefik/traefik.yml -touch /srv/traefik/config/{middlewares,tls,planilla,musica,snatchgame}.yml -``` - ---- - -## 7. Scripts de Migración - -### 7.1 Script de Pre-Migración - -```bash -#!/bin/bash -# pre-migration.sh - -set -e - -echo "🔍 Pre-Migration Checklist para Traefik" - -# Backup de NPM -echo "📦 Backing up NPM configs..." -mkdir -p /srv/backups/npm-$(date +%Y%m%d) -cp -r /srv/nginx-proxy-manager/data/nginx /srv/backups/npm-$(date +%Y%m%d)/ -cp -r /srv/nginx-proxy-manager/data/custom_ssl /srv/backups/npm-$(date +%Y%m%d)/ - -# Verificar red principal -echo "🌐 Verificando red principal..." -docker network inspect principal > /dev/null 2>&1 || { - echo "❌ Red 'principal' no existe" - exit 1 -} - -# Verificar contenedores backend -echo "🔍 Verificando backends..." -backends=( - "portainer" - "gitea" - "geo-ids" - "planilla-ui" - "planilla-api" - "planilla-mcp" - "whatsapp-router" - "repodructor" - "amigos-app" - "duplicati" - "radiusnucleo-node-1" -) - -for backend in "${backends[@]}"; do - docker ps --filter "name=$backend" --format "{{.Names}}" | grep -q "$backend" || { - echo "⚠️ Backend '$backend' no encontrado o detenido" - } -done - -# Crear estructura Traefik -echo "📁 Creando estructura de directorios..." -mkdir -p /srv/traefik/{config,certificates/interno.com,logs} -touch /srv/traefik/acme.json -chmod 600 /srv/traefik/acme.json - -# Copiar certificados -echo "🔐 Copiando certificados SSL..." -cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/fullchain.pem \ - /srv/traefik/certificates/interno.com/ -cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/privkey.pem \ - /srv/traefik/certificates/interno.com/ - -echo "✅ Pre-migration completa" -echo "" -echo "Próximos pasos:" -echo "1. Crear archivos de configuración de Traefik" -echo "2. Actualizar labels en docker-compose de cada stack" -echo "3. Ejecutar migration.sh" -``` - -### 7.2 Script de Migración - -```bash -#!/bin/bash -# migration.sh - -set -e - -echo "🚀 Iniciando migración NPM → Traefik" - -# Detener NPM -echo "🛑 Deteniendo NPM..." -cd /srv/nginx-proxy-manager -docker-compose down - -# Levantar Traefik -echo "🚀 Levantando Traefik..." -cd /srv/traefik -docker-compose up -d - -# Esperar a que Traefik esté listo -echo "⏳ Esperando a que Traefik esté listo..." -sleep 10 - -# Verificar Traefik -echo "✅ Verificando Traefik..." -docker logs traefik --tail 50 - -# Verificar dashboard -echo "🌐 Dashboard disponible en: https://traefik.interno.com" - -# Restart de cada stack para aplicar labels -echo "🔄 Reiniciando stacks con nuevas labels..." -stacks=( - "/srv/gitea" - "/srv/planilla" - "/srv/conversation-layer" - # ... más stacks -) - -for stack in "${stacks[@]}"; do - if [ -d "$stack" ]; then - echo " Reiniciando $stack..." - cd "$stack" - docker-compose up -d --force-recreate - fi -done - -echo "✅ Migración completa!" -echo "" -echo "Verificar:" -echo "1. Dashboard Traefik: https://traefik.interno.com" -echo "2. Logs: docker logs traefik -f" -echo "3. Probar cada dominio manualmente" -``` - -### 7.3 Script de Rollback - -```bash -#!/bin/bash -# rollback.sh - -set -e - -echo "⏪ Rollback a NPM" - -# Detener Traefik -echo "🛑 Deteniendo Traefik..." -cd /srv/traefik -docker-compose down - -# Levantar NPM -echo "🚀 Levantando NPM..." -cd /srv/nginx-proxy-manager -docker-compose up -d - -echo "✅ Rollback completo" -echo "NPM disponible en: http://Nginx.interno.com:81" -``` - -### 7.4 Script de Verificación - -```bash -#!/bin/bash -# verify-migration.sh - -domains=( - "portainer.interno.com" - "gitea.interno.com" - "geoids.interno.com" - "planilla.interno.com" - "musica.interno.com" - "nucleoriofrio.com" - "snatchgame.interno.com" - "amigos.nucleoriofrio.com" -) - -echo "🔍 Verificando dominios..." - -for domain in "${domains[@]}"; do - echo -n "Testing $domain... " - if curl -skI "https://$domain" | head -1 | grep -q "200\|301\|302"; then - echo "✅ OK" - else - echo "❌ FAIL" - fi -done - -echo "" -echo "Verificar manualmente:" -echo "1. Autenticación Authentik en geoids.interno.com" -echo "2. WebSocket en musica.interno.com" -echo "3. Multi-location en planilla.interno.com" -echo "4. API streaming en nucleoriofrio.com/api/chat/stream" -``` - ---- - -## 8. Tabla de Mapeo Completa - -### NPM → Traefik Equivalencias - -| Característica NPM | Equivalente Traefik | -|-------------------|---------------------| -| `proxy_pass` | `service.loadBalancer.server.url` | -| `server_name` | `router.rule=Host(...)` | -| `location /path` | `router.rule=PathPrefix(...)` + `priority` | -| `proxy_set_header` | `middleware.headers.customRequestHeaders` | -| `add_header` | `middleware.headers.customResponseHeaders` | -| `auth_request` | `middleware.forwardAuth` | -| `ssl_certificate` | `tls.certificates` o `tls.certResolver` | -| `include force-ssl.conf` | `entrypoint.http.redirections` | -| `proxy_buffering off` | `middleware.buffering` | -| `client_max_body_size` | `middleware.buffering.maxRequestBodyBytes` | -| `proxy_read_timeout` | `service.loadBalancer.responseForwarding.timeout` | -| `upstream` con múltiples IPs | `service.loadBalancer.servers` (array) | -| `map` para variables | Middlewares condicionales o dynamic config | -| `if` statements | No soportado directamente, usar priorities | -| `limit_req` | `middleware.rateLimit` | -| `access_log` | `accessLog` en traefik.yml | -| `error_log` | `log` en traefik.yml | - ---- - -## 9. Checklist de Migración - -### Antes de Migrar - -- [ ] Backup completo de NPM configs -- [ ] Backup de base de datos de NPM -- [ ] Copiar certificados SSL -- [ ] Verificar que todos los backends están en red `principal` -- [ ] Documentar configuraciones custom - -### Durante la Migración - -- [ ] Crear estructura de directorios de Traefik -- [ ] Configurar `traefik.yml` -- [ ] Crear middlewares comunes -- [ ] Crear configs dinámicas por servicio -- [ ] Actualizar docker-compose de cada stack con labels -- [ ] Detener NPM -- [ ] Levantar Traefik -- [ ] Verificar dashboard de Traefik - -### Después de Migrar - -- [ ] Probar cada dominio manualmente -- [ ] Verificar certificados SSL -- [ ] Verificar autenticación Authentik -- [ ] Verificar WebSockets -- [ ] Verificar multi-location routing -- [ ] Verificar streaming endpoints -- [ ] Monitorear logs de Traefik -- [ ] Configurar alertas (opcional) -- [ ] Actualizar documentación - -### En Caso de Problemas - -- [ ] Rollback a NPM -- [ ] Revisar logs de Traefik -- [ ] Verificar conectividad de red -- [ ] Verificar resolución DNS interna -- [ ] Verificar permisos de certificados - diff --git a/server-info.sh b/server-info.sh new file mode 100755 index 0000000..b0cb708 --- /dev/null +++ b/server-info.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +echo "======================================" +echo " SERVER INFORMATION FOR TRAEFIK SETUP" +echo "======================================" +echo "" + +# Sistema Operativo +echo "## SISTEMA OPERATIVO" +echo "---" +uname -a +echo "" +if [ -f /etc/os-release ]; then + cat /etc/os-release +fi +echo "" + +# Docker +echo "## DOCKER" +echo "---" +if command -v docker &> /dev/null; then + echo "Docker instalado: SÍ" + docker --version + echo "" + echo "Docker daemon status:" + systemctl is-active docker 2>/dev/null || echo "No se puede verificar status (puede requerir sudo)" + echo "" + echo "Contenedores corriendo:" + docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}" 2>/dev/null || echo "No se puede listar (puede requerir sudo)" +else + echo "Docker instalado: NO" +fi +echo "" + +# Docker Compose +echo "## DOCKER COMPOSE" +echo "---" +if command -v docker-compose &> /dev/null; then + echo "Docker Compose instalado: SÍ" + docker-compose --version +elif docker compose version &> /dev/null; then + echo "Docker Compose (plugin) instalado: SÍ" + docker compose version +else + echo "Docker Compose instalado: NO" +fi +echo "" + +# Puertos ocupados +echo "## PUERTOS CRÍTICOS" +echo "---" +echo "Verificando puertos 80, 443, 8080..." +for port in 80 443 8080; do + if command -v ss &> /dev/null; then + result=$(ss -tuln | grep ":$port " 2>/dev/null) + else + result=$(netstat -tuln 2>/dev/null | grep ":$port ") + fi + + if [ -n "$result" ]; then + echo "Puerto $port: OCUPADO" + echo "$result" + else + echo "Puerto $port: LIBRE" + fi +done +echo "" + +# Servicios web corriendo +echo "## SERVICIOS WEB DETECTADOS" +echo "---" +if command -v systemctl &> /dev/null; then + for service in nginx apache2 httpd caddy; do + if systemctl list-unit-files | grep -q "^$service.service"; then + status=$(systemctl is-active $service 2>/dev/null) + echo "$service: $status" + fi + done +else + echo "systemctl no disponible" +fi +echo "" + +# Recursos del servidor +echo "## RECURSOS DEL SERVIDOR" +echo "---" +echo "CPU:" +grep -m1 "model name" /proc/cpuinfo 2>/dev/null || echo "No disponible" +echo "Cores: $(nproc 2>/dev/null || echo 'N/A')" +echo "" +echo "Memoria:" +free -h 2>/dev/null || echo "No disponible" +echo "" +echo "Disco:" +df -h / 2>/dev/null || echo "No disponible" +echo "" + +# Networking +echo "## NETWORKING" +echo "---" +echo "IPs del servidor:" +if command -v ip &> /dev/null; then + ip -4 addr show | grep inet | awk '{print $2}' | grep -v "127.0.0.1" +else + ifconfig 2>/dev/null | grep "inet " | awk '{print $2}' | grep -v "127.0.0.1" +fi +echo "" +echo "Hostname:" +hostname +echo "" + +# Dominios/DNS +echo "## DOMINIOS CONFIGURADOS (si existen)" +echo "---" +if [ -f /etc/hosts ]; then + echo "Entradas relevantes en /etc/hosts:" + grep -v "^#" /etc/hosts | grep -v "127.0.0.1.*localhost" | grep -v "::1" | grep -v "^$" +fi +echo "" + +# Docker networks +echo "## DOCKER NETWORKS" +echo "---" +if command -v docker &> /dev/null; then + docker network ls 2>/dev/null || echo "No se puede listar (puede requerir sudo)" +else + echo "Docker no disponible" +fi +echo "" + +# Directorio actual y permisos +echo "## INFORMACIÓN DEL DIRECTORIO" +echo "---" +echo "Directorio actual: $(pwd)" +echo "Usuario actual: $(whoami)" +echo "Permisos:" +ls -la $(pwd) 2>/dev/null | head -10 +echo "" + +# Variables de entorno relevantes +echo "## VARIABLES DE ENTORNO RELEVANTES" +echo "---" +echo "HOME: $HOME" +echo "USER: $USER" +echo "PATH: $PATH" +echo "" + +echo "======================================" +echo " FIN DEL REPORTE" +echo "======================================" diff --git a/traefik/traefik.yml b/traefik/traefik.yml new file mode 100755 index 0000000..3756bc4 --- /dev/null +++ b/traefik/traefik.yml @@ -0,0 +1,81 @@ +# Configuración estática de Traefik +# Este archivo se carga al iniciar Traefik y define la configuración base + +# API y Dashboard +api: + dashboard: true # Habilita el dashboard web + insecure: false # Requiere autenticación (configurada en dynamic/) + +# Entry Points - Puntos de entrada para el tráfico +entryPoints: + web: + address: ":80" + # Redirección automática HTTP -> HTTPS + http: + redirections: + entryPoint: + to: websecure + scheme: https + permanent: true + + websecure: + address: ":443" + http: + tls: + certResolver: letsencrypt # Usa Let's Encrypt para SSL + + # Entry point para métricas (opcional) + metrics: + address: ":8082" + +# Providers - De dónde Traefik obtiene su configuración +providers: + # Docker provider - Auto-descubre contenedores + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false # Solo expone contenedores con labels específicas + network: traefik-network # Red Docker para Traefik + watch: true # Detecta cambios en tiempo real + + # File provider - Configuraciones dinámicas desde archivos + file: + directory: /etc/traefik/dynamic + watch: true # Recarga automáticamente si hay cambios + +# Certificados SSL automáticos con Let's Encrypt +certificatesResolvers: + letsencrypt: + acme: + email: jodarioel87@gmail.com # Email para notificaciones de Let's Encrypt + storage: /letsencrypt/acme.json + # DNS Challenge para certificados wildcard + dnsChallenge: + provider: cloudflare + resolvers: + - "1.1.1.1:53" + - "1.0.0.1:53" + delayBeforeCheck: 10s + +# Logging +log: + level: INFO # DEBUG, INFO, WARN, ERROR + filePath: /var/log/traefik/traefik.log + format: json + +# Access logs +accessLog: + filePath: /var/log/traefik/access.log + format: json + bufferingSize: 100 + +# Métricas (opcional - para Prometheus) +metrics: + prometheus: + addEntryPointsLabels: true + addServicesLabels: true + entryPoint: metrics + +# Configuración global +global: + checkNewVersion: true + sendAnonymousUsage: false