Files
traefikNucleo/planV1.md
2025-10-05 04:01:15 -06:00

30 KiB

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
  2. Configuración Base de Traefik
  3. Traducción de Cada Proxy Host
  4. Middlewares Comunes
  5. Certificados SSL
  6. Docker Compose Completo
  7. 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

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)

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:

# 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:

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:

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):

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
  • /apiplanilla-api:4000
  • /mcpplanilla-mcp:5000
  • /eventsplanilla-api:4000
  • WebSocket en todos

Traefik Dynamic Config (/config/planilla.yml):

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):

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):

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):

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):

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)

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

# 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:

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:

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@nucleoriofrio.com
      storage: /acme.json
      httpChallenge:
        entryPoint: http

Para wildcard con DNS Challenge (Cloudflare):

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:

CF_DNS_API_TOKEN=your_cloudflare_token
CF_ZONE_API_TOKEN=your_zone_token

6. Docker Compose Completo

6.1 Stack Traefik Principal

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

# 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

#!/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

#!/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

#!/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

#!/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