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:
Eduardo David Paredes Vara
2026-05-25 05:47:11 +00:00
parent 5e69ad1024
commit 5e220591e7
7 changed files with 469 additions and 25 deletions
+37
View File
@@ -0,0 +1,37 @@
# Instrucciones para Copilot
## Comandos de build, test y lint
- Este repositorio no tiene un build, lint o suite de tests centralizados; la validacion principal se hace con `docker compose config` por stack.
- Validar el stack raiz de Portainer: `docker compose -f docker-compose.yml config`
- Validar la variante inicial de bootstrap por 9443: `docker compose -f docker-compose.yml -f docker-compose.9443.yml config`
- Levantar el bootstrap inicial de Portainer: `docker compose -f docker-compose.yml -f docker-compose.9443.yml up -d`
- Validar un stack individual usando su plantilla versionada de variables: `docker compose --env-file <stack>/stack.env -f <stack>/docker-compose.yml config`
- Levantar un stack individual en local con su plantilla de variables: `docker compose --env-file <stack>/stack.env -f <stack>/docker-compose.yml up -d`
- Los unicos scripts ejecutables del repo estan en `n8n-observability/`:
- Validar la sintaxis de un script: `bash -n n8n-observability/scripts/server-summary.sh`
- Ejecutar un script: `./n8n-observability/scripts/server-summary.sh`
- Ejecutar el digest de logs para una ventana concreta: `SINCE="24 hours ago" ./n8n-observability/scripts/log-digest.sh`
## Arquitectura de alto nivel
- El repositorio agrupa varios stacks Docker Compose pensados para desplegarse desde Portainer. El orden base es: stack raiz de Portainer, despues `Traefik/`, luego `authentik/` y despues el resto de stacks de aplicaciones.
- El `docker-compose.yml` de la raiz define Portainer detras de Traefik. Separa la UI autenticada del host de API/app movil, protegido con una IP allowlist. `docker-compose.9443.yml` no es un stack independiente: es solo un override para anadir el puerto de bootstrap y debe combinarse con el compose raiz.
- `Traefik/` es la capa compartida de entrada. Mantiene la red externa `proxy`, el estado de ACME/Let's Encrypt y la configuracion dinamica basada en ficheros bajo `/opt/traefik/dynamic`.
- `authentik/` es la capa compartida de SSO. Publica el middleware reutilizable de Traefik `ths-authentik@docker` y la ruta del outpost bajo `/outpost.goauthentik.io/`; los demas stacks consumen ese middleware en vez de redefinir su propia integracion de auth.
- Cada carpeta de stack es autocontenida: incluye su propio `docker-compose.yml`, una plantilla `stack.env` versionada y, normalmente, un README con notas de despliegue y operacion.
- La mayoria de stacks de aplicacion siguen el mismo patron de red: una red bridge privada para trafico app/base de datos/cache, mas la red externa `proxy` solo para los servicios publicados por Traefik.
- `mail-relay/` es infraestructura compartida para SMTP saliente. Los stacks que necesitan correo se conectan a la red `mail_internal` y usan `mail-relay` como host SMTP.
- Hay integraciones cruzadas deliberadas entre stacks: Gitea incluye un contenedor runner para Actions, Nextcloud monta `/opt/paperless/media` en solo lectura y Paperless sincroniza su bandeja de entrada desde Nextcloud WebDAV mediante `paperless-inbox-sync`.
## Convenciones clave
- `stack.env` es la plantilla versionada de configuracion de cada stack. La documentacion de despliegue indica de forma consistente copiar `stack.env` a `.env` o cargarlo directamente como archivo de entorno; al anadir variables nuevas, actualiza `stack.env` y respeta los nombres ya existentes.
- Los datos persistentes del host se guardan en bind mounts bajo `/opt/<stack>/...`, no dentro del arbol del repositorio. Las rutas nuevas deben seguir esa convencion.
- Los bind mounts usan de forma consistente sufijos de relabel SELinux como `:Z`. No los elimines salvo que el stack deje de usar bind mounts del host de forma intencionada.
- Los servicios expuestos por HTTP deben unirse a la red `proxy` y definir labels de Traefik para router y service, especialmente `traefik.http.services.<name>.loadbalancer.server.port`.
- Los servicios solo internos se mantienen fuera de `proxy` y normalmente no llevan labels de Traefik. Solo anade `mail_internal` cuando el servicio necesite llegar al relay SMTP.
- El nombre del middleware compartido de Authentik es `ths-authentik@docker`. Reutilizalo desde las labels de los routers en vez de crear variantes por stack, salvo que el stack necesite un comportamiento distinto de verdad.
- El stack raiz de Portainer mantiene de forma intencionada routers de Traefik separados para la UI, el acceso directo a la API y dominios adicionales. Conserva esa separacion cuando cambies el enrutado raiz.
- La documentacion del repositorio esta mayoritariamente en espanol. Mantén ese estilo al editar READMEs o documentacion de servicios.
- Cuando cambies un stack, lee siempre su README junto con `docker-compose.yml` y `stack.env`; muchos README contienen restricciones operativas que no se deducen solo del compose.
+22
View File
@@ -0,0 +1,22 @@
# Archivos de entorno con secretos reales — usar stack.env como plantilla
.env
**/.env
# Configuraciones live con datos reales de producción
*.live.yaml
# Backups y temporales
*.bak
*.tmp
# Archivos de auth del CLI de Codex/OpenAI
.codex
# Secretos de VSCode / editores
.vscode/settings.json
# Otros archivos de secretos comunes
*.pem
*.key
*.p12
*.pfx
+1 -1
View File
@@ -168,7 +168,7 @@ docker network create mail_internal
1. **Ports Exposes**: set to the app's HTTP port (must match `loadbalancer.server.port` label)
2. **Domain**: set FQDN (e.g. `myapp.sherlockhomeless.net`)
3. **Base Directory**: set to the subdirectory (e.g. `/gitea`, `/n8n`)
4. **Environment Variables**: fill from `stack.env` template
4. **Environment Variables**: fill from `.env` (copy values from `stack.env` template if needed)
---
+201
View File
@@ -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 |
+191
View File
@@ -0,0 +1,191 @@
# Prompt: SSO OIDC + Forward Auth — Diagnostico y alternativas
## Contexto
Proyecto en `/home/felidae/Portainer_repo/`. Infraestructura Docker Compose con:
- **Traefik v3.6.13** como reverse proxy (red `proxy`)
- **Authentik** como SSO via Forward Auth (`ths-authentik@docker`)
- **CrowdSec** como IPS/IDS via plugin Traefik (`crowdsec-bouncer@file`)
- **Homepage** como dashboard (descubre labels Docker)
Se han levantado dos nuevos servicios que necesitan SSO con Authentik +
enlaces compartidos publicos (sin login):
| Servicio | Dominio | Imagen |
|----------|---------|--------|
| **OpenGist** | `opengist.sherlockhomeless.net` | `ghcr.io/thomiceli/opengist:latest` (puerto 6157) |
| **OneTimeSecret** | `ots.sherlockhomeless.net` | `onetimesecret/onetimesecret:latest` (puerto 3000, necesita Redis) |
---
## Que funciona
1. **DNS**: registros A creados con `cloudflared/add-domain.sh` apuntando a `193.70.84.224`
2. **Traefik**: rutas TLS con Let's Encrypt para ambos dominios
3. **CrowdSec**: bouncer activo, bloqueando IPs en todos los routers
4. **Forward Auth**: redirige a Authentik al visitar las URLs raiz (302 a `/application/o/authorize/`)
5. **Enlaces compartidos publicos**: las rutas de gist (`/{user}/{gist}`) y secretos (`/secret/*`, `/private/*`) evitan Authentik correctamente
6. **Callbacks OIDC**: las rutas `/auth/*` (OTS) y `/oauth/*` (OpenGist) evitan Authentik, llegan al backend
7. **Homepage**: labels Docker puestos, descubre ambos servicios
### Routers Traefik configurados
**OpenGist** (`opengist/docker-compose.yml`):
```
Router 1 (prio 20, SIN Authentik):
Host(opengist.sherlockhomeless.net) && (PathRegexp(^/[a-z...]+/[a-z0-9]+) || PathPrefix(/oauth/))
→ crowdsec-bouncer@file
Router 2 (prio 10, CON Authentik):
Host(opengist.sherlockhomeless.net)
→ crowdsec-bouncer@file, ths-authentik@docker
```
**OTS** (`onetimesecret/docker-compose.yml`):
```
Router 1 (prio 20, SIN Authentik):
Host(ots.sherlockhomeless.net) && (PathPrefix(/secret/) || PathPrefix(/private/) || PathPrefix(/auth/))
→ crowdsec-bouncer@file
Router 2 (prio 10, CON Authentik):
Host(ots.sherlockhomeless.net)
→ crowdsec-bouncer@file, ths-authentik@docker
```
---
## Donde se rompe
### Problema principal
El usuario visita la URL, Authentik pide login (Forward Auth funciona), pero
tras autenticarse en Authentik **la app no reconoce al usuario como logueado**.
La app muestra su propio formulario/boton de login.
La causa raiz es la diferencia entre Forward Auth y reconocimiento de sesion
dentro de la app:
- **Forward Auth**: protege la RUTA a nivel HTTP. Authentik verifica si la
peticion tiene sesion valida. Si no, redirige al login. Si si, deja pasar
la peticion y añade headers (`X-Authentik-Username`, etc.).
- **La app**: tiene su propio sistema de sesiones. No sabe leer los headers
`X-Authentik-*`. Necesita su propio flujo OIDC/OAuth para crear una sesion
interna.
Para cerrar ese gap se configuro OIDC en ambos servicios apuntando a Authentik:
| App | OIDC Provider (Authentik) | Redirect URI | Config en la app |
|-----|--------------------------|--------------|------------------|
| OpenGist | pk=45, client_id=`MZb3LrTP...` | `/oauth/callback` | `OG_OIDC_CLIENT_ID`, `OG_OIDC_CLIENT_SECRET`, `OG_OIDC_ISSUER` |
| OTS | pk=46, client_id=`Bqd0erWf...` | `/auth/oidc/callback` | `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, `OIDC_ISSUER`, `AUTH_SSO_ENABLED=true`, `AUTHENTICATION_MODE=full`, `AUTH_SECRET=...` |
### Sintoma actual reportado por el usuario
En `https://ots.sherlockhomeless.net/signin` aparece el boton "Sign in with
Authentik". Al hacer clic estando ya logueado en Authentik, **no cambia nada**,
sigue pidiendo sign in. El flujo OIDC aparentemente no completa la creacion
de sesion en OTS.
Posibles causas:
1. La cookie de sesion de Authentik no se envia en el callback (problema de
dominio/cookie SameSite)
2. El callback OIDC falla en OTS (error de configuracion Rodauth/OmniAuth)
3. Forward Auth interfiere en alguna parte del flujo (aunque las rutas de
callback ya se excluyeron)
4. La sesion de Authentik no persiste entre Forward Auth y OIDC
### Otros detalles tecnicos
- **Authentik**: el dominio es `auth.sherlockhomeless.net`. Los providers OIDC
usan issuer por provider (`issuer_mode: per_provider`).
Issuer OpenGist: `https://auth.sherlockhomeless.net/application/o/MZb3LrTP.../`
Issuer OTS: `https://auth.sherlockhomeless.net/application/o/Bqd0erWf.../`
- **OTS healthcheck**: la imagen oficial tiene un healthcheck built-in
(`bin/healthcheck.sh`) que falla en nuestro entorno. Se deshabilito con
`healthcheck: { disable: true }` porque Traefik se niega a rutear
contenedores unhealthy.
- **OTS full auth**: requiere `AUTH_SECRET` (valor generado con `openssl rand -hex 48`),
SQLite en `/app/data/auth.db` (montado en `/opt/onetimesecret/data:Z`, uid 1001),
y Redis para los datos de la app.
- **OpenGist**: usa `OG_OPENGIST_HOME=/opengist` con SQLite interno. No
requiere base de datos externa.
---
## Archivos clave
```
Portainer_repo/
├── opengist/
│ ├── docker-compose.yml
│ ├── .env
│ └── stack.env
├── onetimesecret/
│ ├── docker-compose.yml
│ ├── .env
│ └── stack.env
├── authentik/
│ ├── docker-compose.yml
│ ├── create-auth-app.sh
│ └── .env
├── Traefik/docker-compose.yml
├── crowdsec/docker-compose.yml
├── cloudflared/
│ ├── add-domain.sh
│ └── .env
└── NUEVA-APP.md (guia de despliegue de nuevas apps)
```
### Credenciales OIDC en Authentik
Obtenibles via API con token generado desde `docker exec ths-authentik-server`:
```bash
# OpenGist OIDC provider (pk=45)
curl -sk -H "Authorization: Bearer $TOKEN" \
https://auth.sherlockhomeless.net/api/v3/providers/oauth2/45/
# OTS OIDC provider (pk=46)
curl -sk -H "Authorization: Bearer $TOKEN" \
https://auth.sherlockhomeless.net/api/v3/providers/oauth2/46/
```
---
## Objetivo para el siguiente agente
Conseguir que el flujo SSO funcione de extremo a extremo:
1. Usuario visita `https://opengist.sherlockhomeless.net/` o `https://ots.sherlockhomeless.net/`
2. Authentik muestra su pagina de login (Forward Auth) — esto YA funciona
3. Usuario se autentica en Authentik
4. Al volver a la app, el usuario aparece **logueado automaticamente**
(la app reconoce al usuario via OIDC sin requerir clic extra)
5. Los enlaces compartidos (`/{user}/{gist}`, `/secret/*`) siguen siendo
publicos (sin login) — esto YA funciona
### Ideas a explorar
- **Alternativa A — Quitar Forward Auth, usar solo OIDC**: si las apps
soportan auto-redirect al provider OIDC cuando no hay sesion, se elimina
la doble autenticacion. El login seria "clic en boton" en vez de
"automatico al entrar", pero el SSO seria mas limpio.
- **Alternativa B — Authentik en modo proxy (no forward auth)**: configurar
Authentik como reverse proxy real en vez de forward auth. Authentik
manejaria todo el flujo OIDC y pasaria el usuario autenticado al backend.
- **Alternativa C — Header auth nativo**: investigar si OpenGist u OTS
soportan autenticacion por headers de proxy inverso (tipo `Remote-User`).
Si es asi, se puede usar solo Forward Auth sin OIDC, pasando el header
`X-Authentik-Username` como `Remote-User`.
- **Alternativa D — Autentik OAuth2 en vez de OIDC**: probar si el flujo
OAuth2 simple (sin OpenID Connect) funciona mejor con estas apps.
- **Alternativa E — Debug del flujo OIDC actual**: revisar logs de OTS
(`docker logs onetimesecret`) y de Authentik durante el flujo OIDC para
identificar exactamente donde falla el callback. Puede ser un problema
de SameSite cookie, CORS, o redirect_uri mismatch.
+13 -4
View File
@@ -11,6 +11,7 @@ Este repositorio contiene la configuración de Portainer y múltiples stacks de
- [Stacks Disponibles](#stacks-disponibles)
- [Configuración](#configuración)
- [Uso](#uso)
- [**→ Añadir una nueva app**](NUEVA-APP.md)
## 📖 Descripción
@@ -27,7 +28,8 @@ La arquitectura sigue este orden de despliegue:
1. **Portainer** (gestor de contenedores) - acceso directo por puerto 9443
2. **Traefik** (reverse proxy) - desplegado desde Portainer
3. **Authentik** (SSO) - desplegado desde Portainer
4. **Resto de stacks** - desplegables desde Portainer
4. **CrowdSec** (IPS/IDS + bouncer) - protección de seguridad colaborativa para todos los servicios
5. **Resto de stacks** - desplegables desde Portainer
Todos los servicios se comunican a través de la red Docker `proxy` y están protegidos por Traefik con SSL.
@@ -122,7 +124,7 @@ nslookup auth.tudominio.com
- Repository URL: `<tu-repositorio>`
- Repository reference: `main`
- Compose path: `Traefik/docker-compose.yml`
5. Añade el archivo de variables de entorno: `Traefik/stack.env` (o configúralas manualmente)
5. Añade el archivo de variables de entorno: `Traefik/.env` (puedes partir de `Traefik/stack.env` como plantilla)
6. Haz clic en **Deploy the stack**
Verifica que Traefik esté funcionando:
@@ -153,6 +155,8 @@ Una vez desplegado Authentik, necesitarás configurar:
Para instrucciones detalladas, consulta el [README de Authentik](authentik/README.md).
> **Importante**: para proteger nuevas apps con `ths-authentik@docker`, no basta con añadir el middleware en Traefik. También hay que crear en Authentik el **Proxy Provider**, la **Application** y asociarlos al **Embedded Outpost**. El patrón completo está documentado en `authentik/README.md`.
### Paso 9: Configurar Variables de Entorno para Portainer UI
Una vez Traefik y Authentik están funcionando, actualiza el archivo `.env` en la raíz del proyecto:
@@ -216,8 +220,13 @@ cd ..
| **Traefik** | Reverse proxy con SSL automático | `Traefik/` | [README](Traefik/README.md) |
| **Portainer** | Gestor visual de Docker | Raíz (docker-compose.yml) | - |
| **Authentik** | Sistema de autenticación SSO (Forward Auth) | `authentik/` | [README](authentik/README.md) |
| **CrowdSec** | IPS/IDS colaborativo + bouncer Traefik + Grafana | `crowdsec/` | [README](crowdsec/README.md) |
| **Homepage** | Dashboard principal expuesto por Traefik | `homepage/` | [README](homepage/README.md) |
| **Dozzle** | Visor de logs Docker protegido con Authentik | `dozzle/` | [README](dozzle/README.md) |
| **Beszel** | Monitorización ligera de host y contenedores | `beszel/` | [README](beszel/README.md) |
| **Gitea** | Servidor Git autoalojado con Actions | `gitea/` | [README](gitea/README.md) |
| **n8n** | Plataforma de automatización de workflows | `n8n/` | [README](n8n/README.md) |
| **Mail Relay** | Relay SMTP de salida para aplicaciones | `mail-relay/` | [README](mail-relay/README.md) |
| **AdGuard** | Bloqueador de anuncios DNS con DoT | `adguard/` | [README](adguard/README.md) |
| **Trilium** | Aplicación de notas jerárquicas | `trilium/` | [README](trilium/README.md) |
| **Wireguard** | VPN rápida y segura | `wireguard/` | [README](wireguard/README.md) |
@@ -255,7 +264,7 @@ TRAEFIK_AUTH_MIDDLEWARE=ths-authentik@docker
### Configuraciones por Stack
Cada stack puede tener su propio archivo `stack.env` o `.env` en su carpeta correspondiente.
Cada stack usa su propio archivo `.env` local. El archivo `stack.env` se conserva solo como plantilla de ejemplo para copiar o tomar como referencia.
## 🎯 Uso
@@ -352,7 +361,7 @@ sudo chcon -Rt svirt_sandbox_file_t /opt/portainer/data
## 📝 Notas Adicionales
- **Orden de despliegue**: Siempre desplegar Portainer (9443) → Traefik → Authentik → Resto de stacks
- **Orden de despliegue**: Siempre desplegar Portainer (9443) → Traefik → Authentik → CrowdSec → Resto de stacks
- **Registros DNS**: Configurar ANTES de desplegar Traefik para que Let's Encrypt funcione correctamente
- **Puerto 9443**: Mantén el acceso por puerto 9443 como respaldo en caso de problemas con Traefik
- **Backups**: Considera hacer backup regular de `/opt/portainer/data` y el archivo de secretos
+4 -20
View File
@@ -1,7 +1,9 @@
services:
portainer:
image: portainer/portainer-ee:2.33.7
image: portainer/portainer-ee:2.39.1
container_name: portainer
profiles:
- legacy
restart: unless-stopped
volumes:
@@ -17,26 +19,8 @@ services:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# 1) UI protegida Authentik
- "traefik.http.routers.portainer.rule=Host(`portainer.thehomelesssherlock.com`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.routers.portainer.middlewares=ths-authentik@docker"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
# 2) API/App móvil SIN Authentik, SOLO por VPN (WireGuard)
- "traefik.http.middlewares.portainer-api-ip.ipallowlist.sourcerange=10.8.0.0/24,172.18.0.1/32"
- "traefik.http.routers.portainer-direct.rule=Host(`portainer-api.thehomelesssherlock.com`)"
- "traefik.http.routers.portainer-direct.entrypoints=websecure"
- "traefik.http.routers.portainer-direct.tls.certresolver=letsencrypt"
- "traefik.http.routers.portainer-direct.middlewares=portainer-api-ip"
- "traefik.http.routers.portainer-direct.service=portainer"
- "traefik.http.routers.portainer-direct.priority=100"
- "traefik.enable=false"
networks:
proxy:
external: true