Files
Portainer/NUEVA-APP.md
T
Eduardo David Paredes Vara 5e220591e7 chore: actualizar meta del repo y configuración base
- .gitignore: añadir reglas para .env, *.live.yaml, .codex, .vscode, certs
- README.md y COOLIFY-TEMPLATE.md: actualizados con nuevos stacks
- NUEVA-APP.md: guía para añadir nuevas apps al homelab
- PROMPT-SSO-ISSUE.md: contexto de debugging SSO/Authentik
- .github/copilot-instructions.md: instrucciones de arquitectura para Copilot
- docker-compose.yml raíz: ajustes al stack de Portainer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 05:47:11 +00:00

202 lines
6.6 KiB
Markdown

# 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 <subdominio>
```
Esto hace en un solo comando:
- Crea el registro DNS `<subdominio>.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 <sub> → 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 <tu-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 |