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>
This commit is contained in:
+201
@@ -0,0 +1,201 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user