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:
@@ -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
@@ -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
@@ -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
@@ -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 |
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user