Compare commits
28 Commits
9dd4b54b37
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fef085efef | |||
| ba4d444f7e | |||
| 7c0b7b62c1 | |||
| 1822c4f3fb | |||
| 91e4a2f8af | |||
| 416bfe0430 | |||
| 811b0ad61a | |||
| f6e2ddd714 | |||
| 24d46bfbf3 | |||
| 400d41687b | |||
| dec01f536c | |||
| 7575b004ab | |||
| 6e7067c91f | |||
| f3815ae8df | |||
| 4e9a74850c | |||
| 77666edf14 | |||
| cf589780f9 | |||
| afb95f4375 | |||
| 4d754b244b | |||
| 055dc1fbbe | |||
| 1313a183c7 | |||
| 21d7a347da | |||
| 2d23c45c6c | |||
| 090390fac1 | |||
| beb873c825 | |||
| 2d342edebf | |||
| 01faed3e76 | |||
| f4937238bb |
@@ -1,8 +0,0 @@
|
||||
# Email de Let's Encrypt para notificaciones
|
||||
ACME_EMAIL=admin@tudominio.com
|
||||
|
||||
# Dominio para el dashboard (opcional)
|
||||
TRAEFIK_DASHBOARD_DOMAIN=traefik.tudominio.com
|
||||
|
||||
# Zona horaria
|
||||
TZ=Europe/Madrid
|
||||
95
README.md
95
README.md
@@ -1,6 +1,6 @@
|
||||
# Infraestructura: Traefik
|
||||
|
||||
# Traefik v2 — Reverse Proxy con Let's Encrypt
|
||||
# Traefik — Reverse Proxy con Let's Encrypt
|
||||
|
||||
Este repositorio despliega **Traefik** como proxy inverso con HTTPS automático (Let's Encrypt), listo para servir como puerta de entrada a tus servicios Docker mediante la red compartida `proxy`.
|
||||
|
||||
@@ -12,9 +12,10 @@ Traefik es un reverse proxy moderno y ligero que detecta servicios Docker autom
|
||||
|
||||
- 🔒 HTTPS automático con Let's Encrypt (HTTP-01)
|
||||
- 🧠 Descubrimiento automático de servicios Docker
|
||||
- 🧷 Redirección HTTP→HTTPS
|
||||
- 🧿 Redirección HTTP→HTTPS
|
||||
- 🧰 Dashboard web (seguro por dominio)
|
||||
- 🧩 Middlewares: auth básica, headers de seguridad, rate limit, etc.
|
||||
- 📊 Logs de acceso y errores (stdout/stderr)
|
||||
|
||||
## Requisitos
|
||||
|
||||
@@ -31,39 +32,38 @@ Internet → Traefik (80/443) → Servicios (en red proxy)
|
||||
|
||||
## Despliegue
|
||||
|
||||
### 1) Crear red `proxy`
|
||||
|
||||
```bash
|
||||
docker network create proxy
|
||||
```
|
||||
|
||||
### 2) Clonar y desplegar
|
||||
### 1) Clonar y configurar
|
||||
|
||||
```bash
|
||||
git clone https://git.ictiberia.com/groales/traefik
|
||||
cd traefik
|
||||
|
||||
# Crear carpeta y archivo para ACME
|
||||
# Crear carpeta para ACME
|
||||
mkdir -p letsencrypt
|
||||
# Windows PowerShell
|
||||
echo $null > .\letsencrypt\acme.json
|
||||
# Linux/macOS
|
||||
# touch ./letsencrypt/acme.json
|
||||
|
||||
# Permisos (Linux)
|
||||
# chmod 600 ./letsencrypt/acme.json
|
||||
|
||||
# Desplegar
|
||||
docker compose up -d
|
||||
touch ./letsencrypt/acme.json
|
||||
chmod 600 ./letsencrypt/acme.json
|
||||
```
|
||||
|
||||
### 3) Configurar dominio y email
|
||||
### 2) Editar configuración
|
||||
|
||||
Edita `traefik.yml` y ajusta:
|
||||
- `certificatesResolvers.letsencrypt.acme.email`
|
||||
- (Opcional) Cambia el dominio del dashboard en labels del contenedor (`traefik.tudominio.com`)
|
||||
**IMPORTANTE:** Antes de desplegar, edita los siguientes archivos con tus datos reales:
|
||||
|
||||
**traefik.yml:**
|
||||
```yaml
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
email: tu-email@tudominio.com # ← EDITA AQUÍ
|
||||
```
|
||||
|
||||
**docker-compose.yml:**
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.traefik.rule=Host(`traefik.tudominio.com`)" # ← EDITA AQUÍ
|
||||
```
|
||||
|
||||
### 3) Desplegar
|
||||
|
||||
Reinicia:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -72,23 +72,27 @@ docker compose up -d
|
||||
|
||||
Este stack expone el dashboard por dominio usando TLS y el servicio interno `api@internal`.
|
||||
|
||||
Para proteger con autenticación básica (opcional):
|
||||
**Autenticación básica habilitada:** El dashboard está protegido mediante el middleware `auth-basic@file` definido en `dynamic/config.yml`.
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.traefik.middlewares=traefik-auth"
|
||||
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$<hash>"
|
||||
```
|
||||
### Configurar contraseña
|
||||
|
||||
Generar hash (htpasswd):
|
||||
1. Genera el hash bcrypt:
|
||||
```bash
|
||||
# Linux/macOS
|
||||
htpasswd -nb admin 'TuPassword'
|
||||
|
||||
# PowerShell con OpenSSL (alternativa)
|
||||
# openssl passwd -apr1 TuPassword
|
||||
docker run --rm httpd:alpine htpasswd -nbB admin tu_password_segura
|
||||
```
|
||||
|
||||
2. Edita `dynamic/config.yml` (sección middlewares > auth-basic) y reemplaza el hash de ejemplo:
|
||||
```yaml
|
||||
auth-basic:
|
||||
basicAuth:
|
||||
users:
|
||||
- "admin:$2y$05$tu_hash_generado_aqui"
|
||||
```
|
||||
|
||||
3. Guarda el archivo. Traefik recargará automáticamente en ~10 segundos (no requiere reinicio).
|
||||
|
||||
**Usuario por defecto:** `admin` (cambia el hash según tu contraseña)
|
||||
|
||||
## Exponer Servicios Detrás de Traefik
|
||||
|
||||
Conecta tus servicios a la red `proxy` y añade labels. Ejemplo: Portainer
|
||||
@@ -127,6 +131,23 @@ certificatesResolvers:
|
||||
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
Traefik envía logs a stdout/stderr (sin persistencia en disco):
|
||||
|
||||
```bash
|
||||
# Ver logs en tiempo real
|
||||
docker logs -f traefik
|
||||
|
||||
# Filtrar errores
|
||||
docker logs traefik | Select-String -Pattern error
|
||||
|
||||
# Ver logs de acceso
|
||||
docker logs traefik | Select-String -Pattern "GET|POST"
|
||||
```
|
||||
|
||||
**Nivel de log**: INFO (configurable en `traefik.yml` → `log.level`)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Certificado no se emite:
|
||||
|
||||
@@ -11,6 +11,7 @@ services:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./traefik.yml:/traefik.yml:ro
|
||||
- ./letsencrypt:/letsencrypt
|
||||
- ./dynamic:/etc/traefik/dynamic:ro
|
||||
environment:
|
||||
TZ: "Europe/Madrid"
|
||||
networks:
|
||||
@@ -18,15 +19,15 @@ services:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Dashboard seguro por dominio
|
||||
- "traefik.http.routers.traefik.rule=Host(`traefik.tudominio.com`)"
|
||||
- "traefik.http.routers.traefik.rule=Host(`traefik.tudominio.com`)" # EDITA CON TU DOMINIO
|
||||
- "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"
|
||||
# Opcional: Autenticación básica
|
||||
# - "traefik.http.routers.traefik.middlewares=traefik-auth"
|
||||
# - "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$..."
|
||||
# Autenticación básica (usa middleware del archivo dynamic/config.yml)
|
||||
- "traefik.http.routers.traefik.middlewares=auth-basic@file"
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
name: proxy
|
||||
driver: bridge
|
||||
83
dynamic/README.md
Normal file
83
dynamic/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Configuración Dinámica de Traefik
|
||||
|
||||
Este directorio contiene configuración que Traefik recarga automáticamente sin reiniciar el contenedor.
|
||||
|
||||
## Archivos
|
||||
|
||||
- **config.yml**: Configuración dinámica completa (serversTransports, middlewares, routers, servicios)
|
||||
|
||||
**Importante:** Con `directory:` en el proveedor file, se debe usar un único archivo consolidado con la estructura `http:` como raíz.
|
||||
|
||||
## ServersTransports
|
||||
|
||||
El archivo incluye el transport `insecure` para servicios con certificados autofirmados (como Portainer):
|
||||
|
||||
```yaml
|
||||
http:
|
||||
serversTransports:
|
||||
insecure:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
**Uso en labels**:
|
||||
```yaml
|
||||
- "traefik.http.services.mi-servicio.loadbalancer.serversTransport=insecure@file"
|
||||
```
|
||||
|
||||
## Autenticación Básica
|
||||
|
||||
El middleware `auth-basic` está **habilitado por defecto** para proteger el dashboard de Traefik.
|
||||
|
||||
### Configurar tu contraseña
|
||||
|
||||
1. Genera hash bcrypt:
|
||||
```bash
|
||||
docker run --rm httpd:alpine htpasswd -nbB admin tu_password
|
||||
```
|
||||
|
||||
2. Copia el hash completo (después de `admin:`)
|
||||
|
||||
3. Edita `config.yml` (sección http > middlewares > auth-basic):
|
||||
```yaml
|
||||
auth-basic:
|
||||
basicAuth:
|
||||
users:
|
||||
- "admin:$2y$05$HASH_GENERADO_AQUI"
|
||||
```
|
||||
|
||||
4. Guarda → recarga automática en ~10 segundos
|
||||
|
||||
## Uso
|
||||
|
||||
### Aplicar middleware a un servicio
|
||||
|
||||
En el `docker-compose.yml` de tu servicio:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mi-servicio:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.mi-servicio.rule=Host(`app.tudominio.com`)"
|
||||
- "traefik.http.routers.mi-servicio.entrypoints=websecure"
|
||||
- "traefik.http.routers.mi-servicio.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.mi-servicio.middlewares=security-headers@file,rate-limit@file"
|
||||
```
|
||||
|
||||
**Nota:** El sufijo `@file` indica que el middleware viene de configuración dinámica.
|
||||
|
||||
### Cadena de middlewares
|
||||
|
||||
Puedes combinar varios:
|
||||
```yaml
|
||||
- "traefik.http.routers.app.middlewares=security-headers@file,rate-limit@file,ip-allowlist@file"
|
||||
```
|
||||
|
||||
## Recarga automática
|
||||
|
||||
Traefik detecta cambios en este directorio y recarga sin reiniciar. Espera ~10 segundos tras editar.
|
||||
|
||||
## Ejemplos adicionales
|
||||
|
||||
- **Routers y Servicios:** Ver ejemplos comentados en `config.yml` (secciones routers y services) para configurar rutas sin labels Docker
|
||||
- **Middlewares avanzados:** Consulta la wiki: https://git.ictiberia.com/groales/traefik/wiki/Middlewares-Seguridad
|
||||
116
dynamic/config.yml
Normal file
116
dynamic/config.yml
Normal file
@@ -0,0 +1,116 @@
|
||||
# ============================================
|
||||
# CONFIGURACIÓN DINÁMICA DE TRAEFIK
|
||||
# ============================================
|
||||
# Este archivo contiene middlewares, routers y servicios
|
||||
# Traefik recarga automáticamente los cambios (~10s)
|
||||
|
||||
http:
|
||||
# ============================================
|
||||
# SERVERS TRANSPORTS
|
||||
# ============================================
|
||||
serversTransports:
|
||||
# Transport para servicios con certificados autofirmados
|
||||
insecure:
|
||||
insecureSkipVerify: true
|
||||
|
||||
# ============================================
|
||||
# MIDDLEWARES
|
||||
# ============================================
|
||||
middlewares:
|
||||
# Headers de seguridad
|
||||
security-headers:
|
||||
headers:
|
||||
stsSeconds: 63072000
|
||||
forceSTSHeader: true
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
frameDeny: true
|
||||
contentTypeNosniff: true
|
||||
browserXssFilter: true
|
||||
referrerPolicy: "strict-origin-when-cross-origin"
|
||||
customResponseHeaders:
|
||||
X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
|
||||
|
||||
# Rate limiting
|
||||
rate-limit:
|
||||
rateLimit:
|
||||
average: 100
|
||||
burst: 200
|
||||
period: 1m
|
||||
|
||||
# IP Allow List (ejemplo - ajusta tus IPs)
|
||||
ip-allowlist:
|
||||
ipAllowList:
|
||||
sourceRange:
|
||||
- "127.0.0.1/32"
|
||||
- "10.0.0.0/8"
|
||||
- "172.16.0.0/12"
|
||||
- "192.168.0.0/16"
|
||||
|
||||
# Autenticación básica (genera hash con: docker run --rm httpd:alpine htpasswd -nbB admin tu_password)
|
||||
auth-basic:
|
||||
basicAuth:
|
||||
users:
|
||||
- "admin:$2y$05$example_hash_CHANGE_THIS" # CAMBIA ESTE HASH
|
||||
|
||||
# Redirect www a root
|
||||
redirect-www:
|
||||
redirectRegex:
|
||||
regex: "^https?://www\\.(.+)"
|
||||
replacement: "https://${1}"
|
||||
permanent: true
|
||||
|
||||
# ============================================
|
||||
# ROUTERS (Ejemplos comentados)
|
||||
# ============================================
|
||||
# routers:
|
||||
# # Ejemplo: Router para aplicación web con HTTPS y middlewares
|
||||
# whoami:
|
||||
# rule: "Host(`whoami.tudominio.com`)"
|
||||
# entryPoints:
|
||||
# - websecure
|
||||
# middlewares:
|
||||
# - security-headers
|
||||
# - rate-limit
|
||||
# service: whoami-service
|
||||
# tls:
|
||||
# certResolver: letsencrypt
|
||||
#
|
||||
# # Ejemplo: Router con autenticación básica y restricción IP
|
||||
# admin-panel:
|
||||
# rule: "Host(`admin.tudominio.com`)"
|
||||
# entryPoints:
|
||||
# - websecure
|
||||
# middlewares:
|
||||
# - auth-basic
|
||||
# - ip-allowlist
|
||||
# - security-headers
|
||||
# service: admin-service
|
||||
# tls:
|
||||
# certResolver: letsencrypt
|
||||
|
||||
# ============================================
|
||||
# SERVICES (Ejemplos comentados)
|
||||
# ============================================
|
||||
# services:
|
||||
# # Ejemplo: Servicio apuntando a contenedor local
|
||||
# whoami-service:
|
||||
# loadBalancer:
|
||||
# servers:
|
||||
# - url: "http://whoami:80"
|
||||
#
|
||||
# # Ejemplo: Servicio apuntando a servidor externo
|
||||
# admin-service:
|
||||
# loadBalancer:
|
||||
# servers:
|
||||
# - url: "http://192.168.1.100:8080"
|
||||
#
|
||||
# # Ejemplo: Servicio con health check
|
||||
# api-service:
|
||||
# loadBalancer:
|
||||
# servers:
|
||||
# - url: "http://api:3000"
|
||||
# healthCheck:
|
||||
# path: "/health"
|
||||
# interval: "10s"
|
||||
# timeout: "3s"
|
||||
12
traefik.yml
12
traefik.yml
@@ -1,7 +1,15 @@
|
||||
# Traefik Dashboard
|
||||
api:
|
||||
dashboard: true
|
||||
insecure: false
|
||||
|
||||
# Logs
|
||||
log:
|
||||
level: INFO # DEBUG, INFO, WARN, ERROR
|
||||
|
||||
accessLog: {}
|
||||
|
||||
# Puertos de escucha de Traefik y redirección automatica
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
@@ -21,11 +29,11 @@ providers:
|
||||
file:
|
||||
directory: /etc/traefik/dynamic
|
||||
watch: true
|
||||
|
||||
# Certificates from an ACME server
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
email: admin@tudominio.com
|
||||
email: admin@tudominio.com # EDITA CON TU EMAIL REAL
|
||||
storage: /letsencrypt/acme.json
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
|
||||
Reference in New Issue
Block a user