Files
traefikNucleo/planV1.md
2025-10-04 18:27:52 -06:00

1275 lines
30 KiB
Markdown

# 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