# Checklist: Añadir una nueva app Cada vez que levantes un nuevo servicio que deba ser accesible desde internet, sigue estos pasos. --- ## 1. Registrar el dominio (DNS + Tunnel) ```bash cd cloudflared ./add-domain.sh ``` Esto hace en un solo comando: - Crea el registro DNS `.sherlockhomeless.net` → Cloudflare Tunnel (CNAME con proxy naranja) - Añade el hostname al ingress del tunnel → `http://traefik:80` Espera ~30 segundos para que Cloudflare propague el DNS. --- ## 2. Labels de Traefik en el docker-compose.yml Añade estas labels al servicio que quieres exponer. Sustituye `miapp` por el nombre corto del servicio y `${MI_DOMINIO}` por la variable de entorno del dominio (ej. `${MIAPP_DOMAIN}`). ### Caso A — App pública con CrowdSec y sin login (APIs, webhooks) ```yaml labels: traefik.enable: "true" traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}" traefik.http.routers.miapp.rule: "Host(`${MI_DOMINIO}`)" traefik.http.routers.miapp.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}" traefik.http.routers.miapp.tls: "true" traefik.http.routers.miapp.tls.certresolver: "${TRAEFIK_CERTRESOLVER}" traefik.http.routers.miapp.middlewares: "crowdsec-bouncer@file" traefik.http.services.miapp.loadbalancer.server.port: "XXXX" ``` ### Caso B — App privada con CrowdSec + Authentik SSO (panel de admin, dashboards) ```yaml labels: traefik.enable: "true" traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}" traefik.http.routers.miapp.rule: "Host(`${MI_DOMINIO}`)" traefik.http.routers.miapp.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}" traefik.http.routers.miapp.tls: "true" traefik.http.routers.miapp.tls.certresolver: "${TRAEFIK_CERTRESOLVER}" traefik.http.routers.miapp.middlewares: "crowdsec-bouncer@file,${TRAEFIK_AUTH_MIDDLEWARE}" traefik.http.services.miapp.loadbalancer.server.port: "XXXX" ``` > El orden importa: CrowdSec bloquea primero, luego Authentik autentica. > `${TRAEFIK_AUTH_MIDDLEWARE}` se resuelve a `ths-authentik@docker`. ### Caso C — App con su propio login (no necesita Authentik) ```yaml labels: traefik.enable: "true" traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}" traefik.http.routers.miapp.rule: "Host(`${MI_DOMINIO}`)" traefik.http.routers.miapp.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}" traefik.http.routers.miapp.tls: "true" traefik.http.routers.miapp.tls.certresolver: "${TRAEFIK_CERTRESOLVER}" traefik.http.routers.miapp.middlewares: "crowdsec-bouncer@file" traefik.http.services.miapp.loadbalancer.server.port: "XXXX" ``` Igual que el Caso A — CrowdSec siempre, Authentik solo cuando la app no tiene auth propia. --- ## 3. Variables de entorno obligatorias en stack.env / .env Incluye siempre estas variables en el `stack.env` de la nueva app para que las labels funcionen: ```env ##### Traefik / dominios ##### TRAEFIK_DOCKER_NETWORK=proxy MIAPP_DOMAIN=miapp.sherlockhomeless.net TRAEFIK_ENTRYPOINT_SECURE=websecure TRAEFIK_CERTRESOLVER=letsencrypt TRAEFIK_AUTH_MIDDLEWARE=ths-authentik@docker # solo si usas Caso B ``` > **Gotcha conocido**: Traefik NO expande variables de entorno dentro de labels. > Los valores como puertos y dominios que van dentro de `Host(...)` deben estar > ya resueltos cuando docker compose procesa el archivo. Usa variables de entorno > solo donde docker compose las expande (fuera de las comillas de las labels si es un valor directo). --- ## 4. Configurar Authentik (solo Caso B) Si usas `ths-authentik@docker`, Authentik debe conocer la app para que el outpost la sirva correctamente. ### En el panel de Authentik (`https://auth.sherlockhomeless.net`): 1. **Crear Provider** → Applications → Providers → Create - Tipo: **Proxy Provider** - Name: `Mi App` - External host: `https://miapp.sherlockhomeless.net` - Authentication flow: `default-authentication-flow` - Authorization flow: `default-provider-authorization-implicit-consent` - Mode: **Forward auth (single application)** 2. **Crear Application** → Applications → Create - Name: `Mi App` - Slug: `miapp` - Provider: el creado en el paso anterior 3. **Asociar al Embedded Outpost** → Outposts → `authentik Embedded Outpost` → Edit - Añade la nueva aplicación a la lista de aplicaciones seleccionadas - Guarda > Si no completas el paso 3, Authentik redirige al login pero luego da error 404. --- ## 5. Red Docker El contenedor debe estar en la red `proxy` para que Traefik lo pueda alcanzar: ```yaml networks: proxy: external: true # red interna opcional para bases de datos, etc. miapp_internal: driver: bridge ``` Y el servicio que expone la app debe tener: ```yaml services: miapp: networks: - proxy - miapp_internal # si tiene db ``` Los contenedores de base de datos u otros servicios internos solo necesitan la red interna, **no** la red `proxy`. --- ## 6. Desplegar ```bash cd miapp docker compose --env-file .env up -d ``` --- ## Resumen visual ``` Nueva app │ ├── 1. ./cloudflared/add-domain.sh → DNS + Tunnel │ ├── 2. Labels en docker-compose.yml │ ├── Caso A (pública) → crowdsec-bouncer@file │ ├── Caso B (privada) → crowdsec-bouncer@file + ths-authentik@docker │ └── Caso C (auth propia)→ crowdsec-bouncer@file │ ├── 3. Variables en stack.env / .env │ ├── 4. Authentik Provider + App + Outpost (solo Caso B) │ ├── 5. Red proxy en docker-compose.yml │ └── 6. docker compose --env-file .env up -d ``` --- ## Middlewares de referencia | Middleware | Definido en | Qué hace | |---|---|---| | `crowdsec-bouncer@file` | `/opt/traefik/dynamic/crowdsec-bouncer.yml` | Bloquea IPs baneadas por CrowdSec y filtra con AppSec WAF | | `ths-authentik@docker` | Labels del stack `authentik` | ForwardAuth hacia Authentik — requiere login SSO | --- ## Gotchas conocidos | # | Problema | Causa | Solución | |---|----------|-------|----------| | 1 | Traefik no enruta la app | `traefik.enable: "true"` falta | Añadir el label | | 2 | Traefik no encuentra el contenedor | Contenedor no está en la red `proxy` | Añadir red proxy al servicio | | 3 | SSL no funciona | `tls.certresolver` mal escrito o dominio no existe en DNS | Verificar DNS con `dig miapp.sherlockhomeless.net` | | 4 | Authentik redirige pero da 404 | App no añadida al Embedded Outpost | Paso 4.3 | | 5 | CrowdSec da 403 | IP baneada | `docker exec crowdsec cscli decisions list` — si apareces, `cscli decisions delete --ip ` | | 6 | Variable no expandida en label | Traefik no expande vars en labels de docker | Hardcodear el valor o usar la variable en stack.env |