commit fa418dae4f8d87365e69fb9a811d675d902eb73c Author: josedario87 Date: Sat Oct 4 18:27:52 2025 -0600 Initial commit diff --git a/planV1.md b/planV1.md new file mode 100644 index 0000000..941bd5b --- /dev/null +++ b/planV1.md @@ -0,0 +1,1274 @@ +# 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 +