diff --git a/Traefik.md b/Traefik.md index d9d1201..66ae546 100644 --- a/Traefik.md +++ b/Traefik.md @@ -60,6 +60,7 @@ services: # Service backend - "traefik.http.services.portainer.loadbalancer.server.port=9443" - "traefik.http.services.portainer.loadbalancer.server.scheme=https" + - "traefik.http.services.portainer.loadbalancer.serversTransport=insecure@file" # Middlewares de seguridad (opcional) - "traefik.http.routers.portainer.middlewares=security-headers@file" @@ -180,9 +181,19 @@ El middleware `ip-allowlist@file` ya incluye rangos privados (10.0.0.0/8, 172.16 email: tu-email@tudominio.com # Debe estar configurado ``` -### Certificado Autofirmado en Logs +### Certificado Autofirmado (Error 500) -Es normal. Traefik se comunica con Portainer por HTTPS (cert autofirmado interno), pero el usuario final ve el certificado de Let's Encrypt. +Portainer usa certificado SSL autofirmado. Traefik necesita el `serversTransport` para aceptarlo: + +```yaml +labels: + - "traefik.http.services.portainer.loadbalancer.server.scheme=https" + - "traefik.http.services.portainer.loadbalancer.serversTransport=insecure@file" +``` + +El transport `insecure@file` está definido en `dynamic/config.yml` del repositorio de Traefik. + +**Nota**: El usuario final ve el certificado de Let's Encrypt, no el autofirmado de Portainer. ## Ejemplo Completo @@ -200,6 +211,7 @@ services: - "traefik.http.routers.portainer.tls.certresolver=letsencrypt" - "traefik.http.services.portainer.loadbalancer.server.port=9443" - "traefik.http.services.portainer.loadbalancer.server.scheme=https" + - "traefik.http.services.portainer.loadbalancer.serversTransport=insecure@file" networks: proxy: @@ -208,6 +220,26 @@ networks: ### docker-compose.override.yaml con Seguridad +```yaml +services: + portainer: + networks: + - proxy + labels: + - "traefik.enable=true" + - "traefik.http.routers.portainer.rule=Host(`portainer.tudominio.com`)" + - "traefik.http.routers.portainer.entrypoints=websecure" + - "traefik.http.routers.portainer.tls.certresolver=letsencrypt" + - "traefik.http.services.portainer.loadbalancer.server.port=9443" + - "traefik.http.services.portainer.loadbalancer.server.scheme=https" + - "traefik.http.services.portainer.loadbalancer.serversTransport=insecure@file" + - "traefik.http.routers.portainer.middlewares=security-headers@file,rate-limit@file" + +networks: + proxy: + external: true +``` + ```yaml services: portainer: @@ -234,448 +266,3 @@ networks: - [Wiki Traefik](https://git.ictiberia.com/groales/traefik/wiki) **Volver a**: [Página Principal](Home) - -```yaml -services: - portainer: - networks: - - traefik_network - labels: - - "traefik.enable=true" - - "traefik.http.routers.portainer-http.rule=Host(`portainer.tudominio.com`)" - - "traefik.http.routers.portainer-http.entrypoints=web" - - "traefik.http.routers.portainer-http.middlewares=redirect-to-https" - - "traefik.http.routers.portainer.rule=Host(`portainer.tudominio.com`)" - - "traefik.http.routers.portainer.entrypoints=websecure" - - "traefik.http.routers.portainer.tls=true" - - "traefik.http.routers.portainer.tls.certresolver=letsencrypt" - - "traefik.http.services.portainer.loadbalancer.server.port=9443" - - "traefik.http.services.portainer.loadbalancer.server.scheme=https" - - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - - "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true" - -networks: - traefik_network: - external: true -``` - -**Ventaja**: El `docker-compose.yaml` base permanece sin cambios, y los overrides se aplican automáticamente. - -### 4. Reiniciar Portainer - -```bash -docker compose down -docker compose up -d -``` - -### 5. Verificar - -```bash -# Ver logs de Traefik -docker logs traefik - -# Verificar que Portainer está en la red de Traefik -docker network inspect traefik_network -``` - -Acceder a: `https://portainer.tudominio.com` - -## Configuración de Traefik - -### traefik.yml Mínimo - -Si aún no tienes Traefik configurado, aquí un ejemplo básico: - -```yaml -# traefik.yml -api: - dashboard: true - insecure: false - -entryPoints: - web: - address: ":80" - http: - redirections: - entryPoint: - to: websecure - scheme: https - websecure: - address: ":443" - -providers: - docker: - endpoint: "unix:///var/run/docker.sock" - exposedByDefault: false - network: traefik_network - -certificatesResolvers: - letsencrypt: - acme: - email: admin@tudominio.com - storage: /letsencrypt/acme.json - httpChallenge: - entryPoint: web -``` - -### docker-compose.yml de Traefik - -```yaml -version: '3.8' - -services: - traefik: - image: traefik:latest - container_name: traefik - restart: always - ports: - - "80:80" - - "443:443" - - "8080:8080" # Dashboard (proteger en producción) - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - ./traefik.yml:/traefik.yml:ro - - ./letsencrypt:/letsencrypt - networks: - - traefik_network - labels: - # Dashboard (opcional, configurar autenticación) - - "traefik.enable=true" - - "traefik.http.routers.traefik.rule=Host(`traefik.tudominio.com`)" - - "traefik.http.routers.traefik.entrypoints=websecure" - - "traefik.http.routers.traefik.tls=true" - - "traefik.http.routers.traefik.tls.certresolver=letsencrypt" - - "traefik.http.routers.traefik.service=api@internal" - -networks: - traefik_network: - external: true -``` - -Iniciar Traefik: - -```bash -docker network create traefik_network -mkdir letsencrypt -touch letsencrypt/acme.json -chmod 600 letsencrypt/acme.json - -docker compose up -d -``` - -## Configuración Avanzada - -### Autenticación Básica (Opcional) - -Proteger Portainer con autenticación HTTP básica: - -```bash -# Generar password hash -echo $(htpasswd -nb admin tu_password) | sed -e s/\\$/\\$\\$/g -# Resultado: admin:$$apr1$$... -``` - -Añadir labels: - -```yaml -labels: - # ... labels existentes ... - - "traefik.http.routers.portainer.middlewares=portainer-auth" - - "traefik.http.middlewares.portainer-auth.basicauth.users=admin:$$apr1$$..." -``` - -### Headers de Seguridad - -```yaml -labels: - # ... labels existentes ... - - "traefik.http.routers.portainer.middlewares=security-headers" - - "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000" - - "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true" - - "traefik.http.middlewares.security-headers.headers.stsPreload=true" - - "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true" - - "traefik.http.middlewares.security-headers.headers.frameDeny=true" - - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true" - - "traefik.http.middlewares.security-headers.headers.browserXssFilter=true" -``` - -### Rate Limiting - -Limitar peticiones para prevenir abusos: - -```yaml -labels: - # ... labels existentes ... - - "traefik.http.routers.portainer.middlewares=rate-limit" - - "traefik.http.middlewares.rate-limit.ratelimit.average=100" - - "traefik.http.middlewares.rate-limit.ratelimit.burst=50" -``` - -### IP Whitelist - -Restringir acceso solo desde IPs específicas: - -```yaml -labels: - # ... labels existentes ... - - "traefik.http.routers.portainer.middlewares=ip-whitelist" - - "traefik.http.middlewares.ip-whitelist.ipwhitelist.sourcerange=192.168.1.0/24,10.0.0.0/8" -``` - -### Múltiples Dominios - -Acceder a Portainer desde varios dominios: - -```yaml -labels: - - "traefik.http.routers.portainer.rule=Host(`portainer.tudominio.com`) || Host(`docker.tudominio.com`)" -``` - -### Configuración con Subdirectorio - -Acceder en `https://tudominio.com/portainer`: - -```yaml -labels: - - "traefik.http.routers.portainer.rule=Host(`tudominio.com`) && PathPrefix(`/portainer`)" - - "traefik.http.routers.portainer.middlewares=portainer-stripprefix" - - "traefik.http.middlewares.portainer-stripprefix.stripprefix.prefixes=/portainer" -``` - -⚠️ **Nota**: Portainer puede tener problemas con subdirectorios. Recomendamos usar subdominios. - -## Wildcard Certificates - -Para certificados wildcard con DNS challenge: - -```yaml -# traefik.yml -certificatesResolvers: - letsencrypt: - acme: - email: admin@tudominio.com - storage: /letsencrypt/acme.json - dnsChallenge: - provider: cloudflare # O tu proveedor DNS - delayBeforeCheck: 30 -``` - -Variables de entorno para Cloudflare: - -```yaml -# docker-compose.yml de Traefik -services: - traefik: - environment: - - CF_API_EMAIL=tu@email.com - - CF_API_KEY=tu_api_key -``` - -Labels en Portainer: - -```yaml -labels: - - "traefik.http.routers.portainer.tls.domains[0].main=tudominio.com" - - "traefik.http.routers.portainer.tls.domains[0].sans=*.tudominio.com" -``` - -## Docker Compose Completo - -### Portainer con Traefik - -Archivo completo `docker-compose.override.yaml`: - -```yaml -version: '3.8' - -services: - portainer: - networks: - - traefik_network - labels: - # Habilitar Traefik - - "traefik.enable=true" - - # Dominio - - "traefik.http.routers.portainer.rule=Host(`portainer.tudominio.com`)" - - # Entrypoints - - "traefik.http.routers.portainer.entrypoints=websecure" - - # TLS - - "traefik.http.routers.portainer.tls=true" - - "traefik.http.routers.portainer.tls.certresolver=letsencrypt" - - # Service backend - - "traefik.http.services.portainer.loadbalancer.server.port=9443" - - "traefik.http.services.portainer.loadbalancer.server.scheme=https" - - # Middlewares (opcional) - - "traefik.http.routers.portainer.middlewares=security-headers" - - "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000" - - "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true" - - "traefik.http.middlewares.security-headers.headers.frameDeny=true" - - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true" - - "traefik.http.middlewares.security-headers.headers.browserXssFilter=true" - -networks: - traefik_network: - external: true -``` - -## Troubleshooting - -### Certificado No Se Genera - -1. **Verificar logs de Traefik**: - ```bash - docker logs traefik | grep -i error - ``` - -2. **Revisar permisos de acme.json**: - ```bash - chmod 600 letsencrypt/acme.json - ``` - -3. **Verificar DNS**: - ```bash - nslookup portainer.tudominio.com - dig portainer.tudominio.com - ``` - -4. **Rate limits de Let's Encrypt**: - - Máximo 5 certificados por semana por dominio - - Usar staging durante pruebas: - ```yaml - certificatesResolvers: - letsencrypt: - acme: - caServer: https://acme-staging-v02.api.letsencrypt.org/directory - ``` - -### Error 502 Bad Gateway - -1. **Verificar que Portainer está corriendo**: - ```bash - docker ps | grep portainer - ``` - -2. **Verificar red compartida**: - ```bash - docker network inspect traefik_network | grep portainer - ``` - -3. **Revisar puerto del servicio**: - ```yaml - # Asegurarse de usar el puerto interno correcto - - "traefik.http.services.portainer.loadbalancer.server.port=9443" - ``` - -4. **Verificar scheme HTTPS**: - ```yaml - - "traefik.http.services.portainer.loadbalancer.server.scheme=https" - ``` - -### Redirección Infinita - -Si hay loop de redirecciones: - -```yaml -labels: - # Remover redirección HTTP si Traefik ya lo maneja globalmente - # Comentar estas líneas: - # - "traefik.http.routers.portainer-http.rule=..." - # - "traefik.http.routers.portainer-http.middlewares=redirect-to-https" -``` - -### Certificado Autofirmado Aún Se Muestra - -Portainer usa su propio certificado SSL. Traefik termina SSL antes de llegar a Portainer, por lo que: - -1. **Es normal** que Portainer siga usando su cert autofirmado internamente -2. El usuario final ve el certificado de Let's Encrypt de Traefik -3. Traefik se comunica con Portainer por HTTPS (con cert autofirmado) - -Si quieres evitar warnings en logs de Traefik: - -```yaml -labels: - - "traefik.http.services.portainer.loadbalancer.server.scheme=https" - - "traefik.http.services.portainer.loadbalancer.serversTransport=ignorecert" - - # En traefik.yml: - # http: - # serversTransports: - # ignorecert: - # insecureSkipVerify: true -``` - -## Monitorización - -### Logs de Traefik - -```bash -# Ver requests a Portainer -docker logs -f traefik | grep portainer - -# Ver errores TLS -docker logs traefik | grep -i "tls\|certificate" -``` - -### Verificar Certificado SSL - -```bash -# Ver información del certificado -echo | openssl s_client -servername portainer.tudominio.com -connect localhost:443 2>/dev/null | openssl x509 -noout -dates - -# Verificar desde exterior -curl -vI https://portainer.tudominio.com 2>&1 | grep -i "SSL\|certificate" -``` - -## Seguridad Adicional - -### 1. Fail2ban con Traefik - -Proteger contra brute-force: - -```bash -# /etc/fail2ban/filter.d/traefik-auth.conf -[Definition] -failregex = ^ - - .* "(GET|POST|HEAD).*HTTP.*" 401 .*$ -ignoreregex = -``` - -```ini -# /etc/fail2ban/jail.local -[traefik-auth] -enabled = true -port = http,https -logpath = /var/log/traefik/access.log -maxretry = 5 -bantime = 3600 -``` - -### 2. Autenticación de Dos Factores - -Portainer tiene 2FA integrado (no depende de Traefik): - -1. **Settings** → **Authentication** -2. Habilitar **OAuth** o **LDAP** con 2FA -3. O usar middleware de Traefik con OAuth2 Proxy - -### 3. VPN como Alternativa - -Para máxima seguridad, no exponer Portainer a Internet: - -1. Acceso solo via VPN (WireGuard, OpenVPN) -2. Traefik escucha solo en IP privada -3. Usuarios conectan primero a VPN - ---- - -**Recursos adicionales**: -- [Documentación Traefik](https://doc.traefik.io/traefik/) -- [Let's Encrypt Rate Limits](https://letsencrypt.org/docs/rate-limits/) -- [Traefik Middlewares](https://doc.traefik.io/traefik/middlewares/overview/) - -**Volver a**: [Página Principal](Home)