feat(authentik): añadir script de creación de aplicaciones OAuth

- create-auth-app.sh: script interactivo para crear providers y
  aplicaciones en Authentik vía API, automatizando la integración SSO
- docker-compose.yml y stack.env: ajustes del stack
- README.md: documentación actualizada

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Eduardo David Paredes Vara
2026-05-25 05:47:33 +00:00
parent 174d282fd9
commit 61134d7834
4 changed files with 647 additions and 27 deletions
+248 -3
View File
@@ -17,13 +17,13 @@ Este stack despliega Authentik configurado para funcionar con Traefik mediante *
- Repository URL: `<tu-repositorio>`
- Repository reference: `main`
- Compose path: `authentik/docker-compose.yml`
5. Carga el archivo de variables de entorno: `authentik/stack.env`
5. Carga el archivo de variables de entorno: `authentik/.env`
6. O añade manualmente las variables de entorno necesarias
7. Haz clic en **Deploy the stack**
### Variables de Entorno Importantes
Edita el archivo `stack.env` con tus valores:
Edita el archivo `.env` con tus valores:
```env
# Secretos (genera valores aleatorios seguros)
@@ -213,6 +213,251 @@ labels:
traefik.http.routers.portainer.middlewares: "authentik@file"
```
### 7.1 Patrón recomendado para nuevas apps
En este repositorio, **proteger un servicio con `ths-authentik@docker` no es suficiente por sí solo**. Además del middleware en Traefik, debes crear en Authentik:
1. un **Proxy Provider**
2. una **Application**
3. y asociar ese provider al **`authentik Embedded Outpost`**
Si falta cualquiera de esas piezas, el síntoma típico es ver una página de Authentik con **`Not Found`** al entrar al dominio protegido.
#### Patrón que seguimos
Para una nueva app publicada detrás de Traefik, usa este esquema:
1. **En el stack Docker del servicio**
- Añade el router de Traefik para el dominio final
- Añade el middleware:
```yaml
labels:
traefik.http.routers.mi-app.middlewares: "ths-authentik@docker"
```
2. **En Authentik crea un Proxy Provider**
- **Name**: `Provider for Mi App`
- **Mode**: `forward_single`
- **External host**: `https://mi-app.tudominio.com`
- **Authorization flow**: el mismo flow compartido que ya usan las demás apps protegidas
- **Intercept header authentication**: activado
3. **En Authentik crea una Application**
- **Name**: `Mi App`
- **Slug**: `mi-app`
- **Provider**: el provider creado en el paso anterior
- **Launch URL**: `https://mi-app.tudominio.com`
- **Group**: la categoría que te convenga en el portal de Authentik
4. **Añade el provider al `authentik Embedded Outpost`**
- Ve a **Outposts** → **authentik Embedded Outpost**
- Añade el nuevo provider a la lista de aplicaciones/providers publicados por el outpost
#### Qué copiar de una app existente
Si ya tienes una app similar funcionando, lo más seguro es **replicar su patrón**:
- reutiliza el mismo **authorization flow**
- reutiliza el mismo **mode** (`forward_single`)
- deja `authentication_flow` vacío si el patrón existente también lo deja vacío
- mantén `intercept_header_auth` activado si sigues el patrón actual del repositorio
Las referencias más claras en este entorno son:
- `Provider for Homepage`
- `Provider for Dozzle`
- `PiHole`
#### Checklist rápida antes de dar una app por buena
1. El servicio responde internamente por su puerto/URL local
2. Traefik tiene el router correcto para el dominio
3. El router usa `ths-authentik@docker`
4. Existe un **Proxy Provider** con el **External host** exacto
5. Existe una **Application** enlazada a ese provider
6. El provider está añadido al **Embedded Outpost**
7. El outpost está **Healthy**
8. El dominio resuelve DNS hacia el servidor
#### Verificación rápida
Desde el host, una app bien enlazada con Authentik suele devolver un `302` hacia Authentik cuando no hay sesión:
```bash
curl -k -I https://mi-app.tudominio.com
```
Si ves:
- **`302` a `/application/o/authorize/`** → el patrón está bien enlazado
- **página `Not Found` de Authentik** → falta provider/app/outpost o están mal enlazados
- **`404/502` de Traefik** → problema de router, servicio o resolución
### 7.2 Automatizar con la API (sin usar la UI)
Se puede crear una aplicación completa (Provider + Application + Outpost) mediante la API REST de Authentik sin tocar la interfaz web. Esto es útil para automatizar despliegues o cuando ya conoces el patrón exacto.
#### Patrón de llamadas API
El script `create-auth-app.sh` encapsula estas 3 llamadas:
**1. Crear Proxy Provider**
```bash
curl -sk -X POST "https://auth.tudominio.com/api/v3/providers/proxy/" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Provider for Mi App",
"authentication_flow": null,
"authorization_flow": "<FLOW_UUID>",
"invalidation_flow": "<INVAL_FLOW_UUID>",
"property_mappings": ["<UUID1>","<UUID2>","<UUID3>","<UUID4>","<UUID5>"],
"external_host": "https://mi-app.midominio.com",
"internal_host": "",
"internal_host_ssl_validation": true,
"mode": "forward_single",
"intercept_header_auth": true,
"cookie_domain": "",
"access_token_validity": "hours=1",
"refresh_token_validity": "days=30"
}'
```
**2. Crear Application**
```bash
curl -sk -X POST "https://auth.tudominio.com/api/v3/core/applications/" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Mi App",
"slug": "mi-app",
"provider": <PROVIDER_PK>,
"policy_engine_mode": "any",
"meta_launch_url": "https://mi-app.midominio.com"
}'
```
**3. Añadir al Embedded Outpost**
```bash
curl -sk -X PATCH "https://auth.tudominio.com/api/v3/outposts/instances/<OUTPOST_UUID>/" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"providers": [<existing_pks>, <new_pk>]}'
```
> Los UUIDs de los flows (`FLOW_UUID`, `INVAL_FLOW_UUID`) y property mappings se copian de un provider existente. El script los obtiene automáticamente.
#### Generar un token de API
El script genera un token temporal automáticamente mediante `ak shell` dentro del contenedor. Si quieres hacerlo manual:
```bash
docker exec ths-authentik-server bash -c 'echo "
from authentik.core.models import Token, User
u = User.objects.filter(username=\"akadmin\").first()
t, _ = Token.objects.update_or_create(user=u, identifier=\"mi-script\",
defaults={\"intent\": \"api\", \"expiring\": True})
print(t.key)
" | ak shell 2>/dev/null' | tail -1
```
### 7.3 Script automatizado: `create-auth-app.sh`
El script `create-auth-app.sh` en este directorio automatiza todo el proceso. Basta con pasarle el dominio y opcionalmente el nombre y slug.
#### Uso
```bash
cd authentik
./create-auth-app.sh <dominio> [nombre] [slug]
```
**Ejemplos:**
```bash
# Especificando todo
./create-auth-app.sh crowdsec.sherlockhomeless.net CrowdSec crowdsec
# Solo dominio (nombre y slug se derivan automáticamente)
./create-auth-app.sh mi-app.sherlockhomeless.net
# → nombre: "Mi App", slug: "mi-app"
# Con nombre personalizado pero slug automático
./create-auth-app.sh dashboard.sherlockhomeless.net "Mi Dashboard"
# → nombre: "Mi Dashboard", slug: "dashboard"
```
#### Qué hace el script
| Paso | Acción |
|------|--------|
| 1 | Genera un token temporal de API usando `ak shell` |
| 2 | Localiza el authorization flow `default-provider-authorization-implicit-consent` |
| 3 | Crea un **Proxy Provider** (o lo salta si ya existe para ese `external_host`) |
| 4 | Crea una **Application** (o la salta si ya existe ese slug) |
| 5 | Localiza el **Embedded Outpost** |
| 6 | Añade el provider al outpost (o lo salta si ya está asociado) |
| Verificación | Comprueba que el dominio redirige a Authentik (HTTP 302) |
#### Requisitos
- Contenedor `ths-authentik-server` corriendo y healthy
- `jq` instalado (`sudo apt install jq`)
- El archivo `.env` en el directorio `authentik/` (opcional, para leer `AUTHENTIK_DOMAIN`)
#### Idempotencia
El script es seguro re-ejecutarlo. Si el provider, aplicación o asociación ya existen, los salta y continúa. Ideal para usarlo en despliegues repetibles o CI/CD.
#### Salida esperada
```
=========================================
Authentik — Crear Application
=========================================
Dominio: crowdsec.sherlockhomeless.net
External host: https://crowdsec.sherlockhomeless.net
Nombre app: CrowdSec
Slug: crowdsec
=========================================
[1/6] Generando token temporal de API...
Token obtenido correctamente.
[2/6] Buscando authorization flow (implicit-consent)...
Flow UUID: 08080511-9d73-45f4-b394-19b5a2452422
[3/6] Verificando si ya existe un provider...
Provider creado: pk=39
[4/6] Verificando si ya existe la aplicación...
Aplicación creada: pk=51f0c37d-...
[5/6] Buscando el Embedded Outpost...
Outpost UUID: 27a4dc69-cefa-4575-b853-caed79515a13
[6/6] Añadiendo provider al outpost...
Provider añadido al outpost.
=========================================
Verificación
=========================================
OK: https://crowdsec.sherlockhomeless.net redirige a Authentik (302)
=========================================
Resumen
=========================================
Provider: Provider for CrowdSec (pk=39)
Application: CrowdSec (slug=crowdsec, pk=51f0c37d-...)
Outpost: authentik Embedded Outpost (...)
URL: https://crowdsec.sherlockhomeless.net
=========================================
```
#### Casos especiales
- **Servicios con widget en Homepage**: una cosa es proteger la UI pública con Authentik y otra la URL interna que Homepage usa para el widget. Homepage normalmente debe hablar con la app por red Docker interna, no pasando por el login web.
- **Servicios con auth propia + Homepage widget**: a menudo conviene dejar la UI pública protegida por Authentik, pero el widget de Homepage apuntando a la URL interna con credenciales/API key propias.
## 🔐 Gestión de Usuarios y Grupos
### Crear Usuarios
@@ -252,7 +497,7 @@ En la configuración del provider:
Para notificaciones y recuperación de contraseñas:
1. Ve a **System** → **Settings** → **Email**
2. Configura los parámetros SMTP (o usa las variables de entorno en `stack.env`)
2. Configura los parámetros SMTP (o usa las variables de entorno en `.env`)
### Configurar Autenticación de Dos Factores (2FA)
+327
View File
@@ -0,0 +1,327 @@
#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# create-auth-app.sh — Crea una aplicación protegida por
# Authentik (Proxy Provider + Application + Outpost) vía API
# ============================================================
#
# Uso:
# ./create-auth-app.sh <dominio> [nombre] [slug]
#
# Ejemplos:
# ./create-auth-app.sh crowdsec.sherlockhomeless.net "CrowdSec" crowdsec
# ./create-auth-app.sh mi-app.midominio.com
#
# Requisitos:
# - Contenedor ths-authentik-server corriendo y healthy
# - jq instalado (sudo apt install jq)
# ============================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
# -----------------------------------------------------------
# Cargar configuración
# -----------------------------------------------------------
AK_HOST_DEFAULT="https://auth.sherlockhomeless.net"
AK_CONTAINER="ths-authentik-server"
if [ -f "${ENV_FILE}" ]; then
set -a; source "${ENV_FILE}"; set +a
fi
AK_HOST="${AUTHENTIK_DOMAIN:-$AK_HOST_DEFAULT}"
AK_HOST="https://${AK_HOST#https://}" # normalizar
# -----------------------------------------------------------
# Parámetros
# -----------------------------------------------------------
DOMAIN="${1:-}"
APP_NAME="${2:-}"
APP_SLUG="${3:-}"
if [ -z "$DOMAIN" ]; then
echo "Uso: $0 <dominio> [nombre] [slug]"
echo ""
echo "Ejemplos:"
echo " $0 crowdsec.sherlockhomeless.net CrowdSec crowdsec"
echo " $0 mi-app.midominio.com"
echo ""
echo "Si no se especifica nombre/slug, se derivan del dominio."
exit 1
fi
# Derivar nombre y slug del dominio si no se especificaron
if [ -z "$APP_NAME" ]; then
# Extraer subdominio: mi-app.midominio.com -> Mi App
SUB=$(echo "$DOMAIN" | cut -d. -f1 | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g')
APP_NAME="$SUB"
fi
if [ -z "$APP_SLUG" ]; then
APP_SLUG=$(echo "$DOMAIN" | cut -d. -f1 | tr '[:upper:]' '[:lower:]')
fi
EXTERNAL_HOST="https://${DOMAIN}"
echo "========================================="
echo " Authentik — Crear Application"
echo "========================================="
echo " Dominio: ${DOMAIN}"
echo " External host: ${EXTERNAL_HOST}"
echo " Nombre app: ${APP_NAME}"
echo " Slug: ${APP_SLUG}"
echo "========================================="
echo ""
# -----------------------------------------------------------
# Verificar dependencias
# -----------------------------------------------------------
if ! command -v jq &>/dev/null; then
echo "ERROR: jq no está instalado. Instálalo con: sudo apt install jq"
exit 1
fi
if ! docker ps --format '{{.Names}}' | grep -q "^${AK_CONTAINER}$"; then
echo "ERROR: El contenedor '${AK_CONTAINER}' no está corriendo."
exit 1
fi
# -----------------------------------------------------------
# 1. Generar token temporal de API
# -----------------------------------------------------------
echo "[1/6] Generando token temporal de API..."
TOKEN_SCRIPT="
from authentik.core.models import Token, User
user = User.objects.filter(username='akadmin').first()
if not user:
print('ERROR:NO_USER')
else:
token, _ = Token.objects.update_or_create(
user=user,
identifier='setup-script',
defaults={'intent': 'api', 'expiring': True, 'expires': None, 'description': 'Token temporal para create-auth-app.sh'}
)
print(token.key)
"
AK_TOKEN=$(docker exec "$AK_CONTAINER" bash -c "echo \"$TOKEN_SCRIPT\" | ak shell 2>/dev/null" | grep -v '^###' | grep -v '^$' | grep -v 'objects imported' | tail -1)
if [ -z "$AK_TOKEN" ] || [[ "$AK_TOKEN" == ERROR:* ]]; then
echo "ERROR: No se pudo generar el token de API. ¿Existe el usuario akadmin?"
echo "Respuesta: $AK_TOKEN"
exit 1
fi
echo " Token obtenido correctamente."
# -----------------------------------------------------------
# 2. Obtener el authorization flow
# -----------------------------------------------------------
echo "[2/6] Buscando authorization flow (implicit-consent)..."
FLOW_JSON=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/flows/instances/?slug=default-provider-authorization-implicit-consent" 2>/dev/null)
FLOW_UUID=$(echo "$FLOW_JSON" | jq -r '.results[0].pk // empty')
if [ -z "$FLOW_UUID" ]; then
echo "ERROR: No se encontró el flow 'default-provider-authorization-implicit-consent'."
exit 1
fi
echo " Flow UUID: ${FLOW_UUID}"
# Buscar un provider existente para copiar sus property_mappings e invalidation_flow
EXISTING_PROVIDER=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/providers/proxy/?page_size=1" 2>/dev/null)
INVAL_FLOW=$(echo "$EXISTING_PROVIDER" | jq -r '.results[0].invalidation_flow // empty')
PROPERTY_MAPPINGS=$(echo "$EXISTING_PROVIDER" | jq -c '.results[0].property_mappings // []')
# -----------------------------------------------------------
# 3. Verificar si ya existe un provider para este dominio
# -----------------------------------------------------------
echo "[3/6] Verificando si ya existe un provider para ${EXTERNAL_HOST}..."
# Listar todos los providers y filtrar por external_host exacto (el API no filtra por este campo)
EXISTING_PK=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/providers/proxy/?page_size=100" 2>/dev/null | jq -r --arg HOST "${EXTERNAL_HOST}" '.results[] | select(.external_host == $HOST) | .pk' 2>/dev/null)
if [ -n "$EXISTING_PK" ]; then
echo " Ya existe un provider (pk=${EXISTING_PK}). Saltando creación."
PROVIDER_PK="$EXISTING_PK"
else
# Crear provider
PROVIDER_JSON=$(curl -sk -X POST "${AK_HOST}/api/v3/providers/proxy/" \
-H "Authorization: Bearer $AK_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Provider for ${APP_NAME}\",
\"authentication_flow\": null,
\"authorization_flow\": \"${FLOW_UUID}\",
\"invalidation_flow\": \"${INVAL_FLOW}\",
\"property_mappings\": ${PROPERTY_MAPPINGS},
\"external_host\": \"${EXTERNAL_HOST}\",
\"internal_host\": \"\",
\"internal_host_ssl_validation\": true,
\"mode\": \"forward_single\",
\"intercept_header_auth\": true,
\"cookie_domain\": \"\",
\"access_token_validity\": \"hours=1\",
\"refresh_token_validity\": \"days=30\"
}" 2>/dev/null)
PROVIDER_PK=$(echo "$PROVIDER_JSON" | jq -r '.pk // empty')
if [ -z "$PROVIDER_PK" ]; then
echo "ERROR: No se pudo crear el provider."
echo "Respuesta: $PROVIDER_JSON"
exit 1
fi
echo " Provider creado: pk=${PROVIDER_PK}"
fi
# -----------------------------------------------------------
# 4. Verificar si ya existe una aplicación con este slug
# -----------------------------------------------------------
echo "[4/6] Verificando si ya existe la aplicación '${APP_SLUG}'..."
EXISTING_APP=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/core/applications/?slug=${APP_SLUG}" 2>/dev/null | jq -r '.results[0].pk // empty')
if [ -n "$EXISTING_APP" ]; then
echo " Ya existe la aplicación (pk=${EXISTING_APP}). Saltando creación."
APP_PK="$EXISTING_APP"
else
# Crear aplicación
APP_JSON=$(curl -sk -X POST "${AK_HOST}/api/v3/core/applications/" \
-H "Authorization: Bearer $AK_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"${APP_NAME}\",
\"slug\": \"${APP_SLUG}\",
\"provider\": ${PROVIDER_PK},
\"policy_engine_mode\": \"any\",
\"meta_launch_url\": \"${EXTERNAL_HOST}\"
}" 2>/dev/null)
APP_PK=$(echo "$APP_JSON" | jq -r '.pk // empty')
if [ -z "$APP_PK" ]; then
echo "ERROR: No se pudo crear la aplicación."
echo "Respuesta: $APP_JSON"
exit 1
fi
echo " Aplicación creada: pk=${APP_PK}"
fi
# -----------------------------------------------------------
# 5. Copiar policy binding de una app existente
# -----------------------------------------------------------
echo "[5/6] Aplicando policy binding..."
REFERENCE_APP_POLICY=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/policies/bindings/?target=${APP_PK}" 2>/dev/null | jq -r '.results[0].policy // empty')
if [ -n "$REFERENCE_APP_POLICY" ]; then
echo " La app ya tiene un binding. Saltando."
else
# Tomar la policy del primer binding de cualquier app existente
SAMPLE_APP=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/core/applications/?page_size=5" 2>/dev/null | jq -r '.results[] | select(.slug != "'"${APP_SLUG}"'") | .pk' | head -1)
POLICY_PK=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/policies/bindings/?target=${SAMPLE_APP}" 2>/dev/null | jq -r '.results[0].policy // empty')
if [ -n "$POLICY_PK" ]; then
curl -sk -X POST "${AK_HOST}/api/v3/policies/bindings/" \
-H "Authorization: Bearer $AK_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"policy\": \"${POLICY_PK}\", \"target\": \"${APP_PK}\", \"enabled\": true, \"order\": 0}" 2>/dev/null > /dev/null
echo " Policy binding creado (policy=${POLICY_PK})."
else
echo " WARN: No se encontró una policy de referencia. Añade el binding manualmente en Authentik."
fi
fi
# -----------------------------------------------------------
# 6. Obtener el Embedded Outpost
# -----------------------------------------------------------
echo "[6/7] Buscando el Embedded Outpost..."
OUTPOST_JSON=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/outposts/instances/?name=authentik%20Embedded%20Outpost" 2>/dev/null)
OUTPOST_UUID=$(echo "$OUTPOST_JSON" | jq -r '.results[0].pk // empty')
if [ -z "$OUTPOST_UUID" ]; then
echo "ERROR: No se encontró el Embedded Outpost."
exit 1
fi
echo " Outpost UUID: ${OUTPOST_UUID}"
# -----------------------------------------------------------
# 6. Añadir provider al outpost
# -----------------------------------------------------------
echo "[7/7] Añadiendo provider al outpost..."
CURRENT_PROVIDERS=$(curl -sk -H "Authorization: Bearer $AK_TOKEN" \
"${AK_HOST}/api/v3/outposts/instances/${OUTPOST_UUID}/" 2>/dev/null | jq -r '.providers | join(",")')
# Verificar si el provider ya está en la lista
if echo "$CURRENT_PROVIDERS" | grep -qw "${PROVIDER_PK}"; then
echo " El provider ya está asociado al outpost."
else
NEW_PROVIDERS="${CURRENT_PROVIDERS},${PROVIDER_PK}"
# Convertir a array JSON
PROVIDER_ARRAY=$(echo "$NEW_PROVIDERS" | tr ',' '\n' | jq -R 'tonumber' | jq -s '.')
curl -sk -X PATCH "${AK_HOST}/api/v3/outposts/instances/${OUTPOST_UUID}/" \
-H "Authorization: Bearer $AK_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"providers\": ${PROVIDER_ARRAY}}" 2>/dev/null > /dev/null
echo " Provider añadido al outpost."
fi
# -----------------------------------------------------------
# Verificación
# -----------------------------------------------------------
echo ""
echo "========================================="
echo " Verificación"
echo "========================================="
HTTP_CODE=$(curl -sk -o /dev/null -w "%{http_code}" "${EXTERNAL_HOST}/" 2>/dev/null)
REDIRECT=$(curl -sk -o /dev/null -w "%{redirect_url}" "${EXTERNAL_HOST}/" 2>/dev/null)
if [ "$HTTP_CODE" = "302" ] && echo "$REDIRECT" | grep -q "/application/o/authorize/"; then
echo " OK: ${EXTERNAL_HOST} redirige a Authentik (302)"
elif [ "$HTTP_CODE" = "502" ] || [ "$HTTP_CODE" = "503" ]; then
echo " WARN: El servicio backend no responde (HTTP ${HTTP_CODE})."
echo " La protección Authentik funciona, pero el servicio no está UP."
elif [ "$HTTP_CODE" = "200" ] && echo "$REDIRECT" | grep -q "authentik"; then
echo " WARN: Redirige a Authentik pero con código 200."
echo " Puede que necesites loguearte primero."
else
echo " INFO: HTTP ${HTTP_CODE}"
echo " Verifica manualmente: curl -kI ${EXTERNAL_HOST}"
fi
echo ""
echo "========================================="
echo " Resumen"
echo "========================================="
echo " Provider: Provider for ${APP_NAME} (pk=${PROVIDER_PK})"
echo " Application: ${APP_NAME} (slug=${APP_SLUG}, pk=${APP_PK})"
echo " Outpost: authentik Embedded Outpost (${OUTPOST_UUID})"
echo " URL: ${EXTERNAL_HOST}"
echo "========================================="
echo ""
echo "Listo. El middleware 'ths-authentik@docker' ya debería proteger ${EXTERNAL_HOST}."
echo "Si usas CrowdSec también, añade: crowdsec-bouncer@file,ths-authentik@docker"
+41 -4
View File
@@ -8,7 +8,7 @@ services:
POSTGRES_USER: ${AUTHENTIK_DB_USER}
POSTGRES_DB: ${AUTHENTIK_DB_NAME}
volumes:
- /opt/authentik/postgres:/var/lib/postgresql/data:Z
- ${AUTHENTIK_POSTGRES_PATH}:/var/lib/postgresql/data:Z
networks:
- ths_authentik_internal
@@ -18,7 +18,7 @@ services:
restart: unless-stopped
command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"]
volumes:
- /opt/authentik/redis:/data:Z
- ${AUTHENTIK_REDIS_PATH}:/data:Z
networks:
- ths_authentik_internal
@@ -42,26 +42,53 @@ services:
AUTHENTIK_BOOTSTRAP_EMAIL: ${AUTHENTIK_BOOTSTRAP_EMAIL}
AUTHENTIK_BOOTSTRAP_PASSWORD: ${AUTHENTIK_BOOTSTRAP_PASSWORD}
AUTHENTIK_BOOTSTRAP_TOKEN: ${AUTHENTIK_BOOTSTRAP_TOKEN}
AUTHENTIK_EMAIL__HOST: ${AUTHENTIK_EMAIL__HOST}
AUTHENTIK_EMAIL__PORT: ${AUTHENTIK_EMAIL__PORT}
AUTHENTIK_EMAIL__USERNAME: ${AUTHENTIK_EMAIL__USERNAME}
AUTHENTIK_EMAIL__PASSWORD: ${AUTHENTIK_EMAIL__PASSWORD}
AUTHENTIK_EMAIL__FROM: ${AUTHENTIK_EMAIL__FROM}
AUTHENTIK_EMAIL__USE_TLS: ${AUTHENTIK_EMAIL__USE_TLS}
AUTHENTIK_EMAIL__USE_SSL: ${AUTHENTIK_EMAIL__USE_SSL}
depends_on:
- ths-authentik-postgres
- ths-authentik-redis
expose:
- "9000"
- "${AUTHENTIK_HTTP_PORT}"
networks:
- ths_authentik_internal
- mail_internal
- proxy
labels:
traefik.enable: "true"
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
# Service Authentik (panel + endpoints)
traefik.http.services.ths-authentik.loadbalancer.server.port: "9000"
traefik.http.services.ths-authentik.loadbalancer.server.port: "${AUTHENTIK_HTTP_PORT}"
# Panel Authentik
traefik.http.routers.ths-authentik.rule: "Host(`${AUTHENTIK_DOMAIN}`)"
traefik.http.routers.ths-authentik.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
traefik.http.routers.ths-authentik.tls: "true"
traefik.http.routers.ths-authentik.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
traefik.http.routers.ths-authentik.service: "ths-authentik"
# Middleware forwardAuth (para proteger otros servicios) -> usar ths-authentik@docker en tus stacks THS
traefik.http.middlewares.ths-authentik.forwardauth.address: "http://ths-authentik-server:${AUTHENTIK_HTTP_PORT}/outpost.goauthentik.io/auth/traefik"
traefik.http.middlewares.ths-authentik.forwardauth.trustForwardHeader: "true"
traefik.http.middlewares.ths-authentik.forwardauth.authResponseHeaders: "X-Authentik-Username,X-Authentik-Groups,X-Authentik-Email,X-Authentik-Uid,X-Authentik-Jwt"
traefik.http.middlewares.ths-authentik.forwardauth.maxResponseBodySize: "1048576"
# Outpost para subdominios sherlockhomeless.net, además de apex/www
traefik.http.routers.ths-authentik-outpost.rule: "(HostRegexp(`[a-z0-9-]+\\.sherlockhomeless\\.net`) || Host(`sherlockhomeless.net`) || Host(`www.sherlockhomeless.net`)) && PathPrefix(`/outpost.goauthentik.io/`)"
traefik.http.routers.ths-authentik-outpost.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
traefik.http.routers.ths-authentik-outpost.tls: "true"
traefik.http.routers.ths-authentik-outpost.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
traefik.http.routers.ths-authentik-outpost.service: "ths-authentik"
traefik.http.routers.ths-authentik-outpost.priority: "1000"
ths-authentik-worker:
image: ${AUTHENTIK_IMAGE}
@@ -78,6 +105,13 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_DB_PASSWORD}
AUTHENTIK_REDIS__HOST: ths-authentik-redis
AUTHENTIK_EMAIL__HOST: ${AUTHENTIK_EMAIL__HOST}
AUTHENTIK_EMAIL__PORT: ${AUTHENTIK_EMAIL__PORT}
AUTHENTIK_EMAIL__USERNAME: ${AUTHENTIK_EMAIL__USERNAME}
AUTHENTIK_EMAIL__PASSWORD: ${AUTHENTIK_EMAIL__PASSWORD}
AUTHENTIK_EMAIL__FROM: ${AUTHENTIK_EMAIL__FROM}
AUTHENTIK_EMAIL__USE_TLS: ${AUTHENTIK_EMAIL__USE_TLS}
AUTHENTIK_EMAIL__USE_SSL: ${AUTHENTIK_EMAIL__USE_SSL}
depends_on:
- ths-authentik-postgres
@@ -85,8 +119,11 @@ services:
networks:
- ths_authentik_internal
- mail_internal
networks:
mail_internal:
external: true
proxy:
external: true
ths_authentik_internal:
+31 -20
View File
@@ -1,30 +1,41 @@
##### Imágenes #####
AUTHENTIK_POSTGRES_IMAGE=
AUTHENTIK_REDIS_IMAGE=
AUTHENTIK_IMAGE=
AUTHENTIK_POSTGRES_IMAGE=docker.io/library/postgres:16.11-alpine3.22
AUTHENTIK_REDIS_IMAGE=docker.io/library/redis:7-alpine
AUTHENTIK_IMAGE=ghcr.io/goauthentik/server:latest
##### Base de datos Authentik #####
AUTHENTIK_DB_PASSWORD=
AUTHENTIK_DB_USER=
AUTHENTIK_DB_NAME=
AUTHENTIK_DB_HOST=
AUTHENTIK_POSTGRES_PATH=
AUTHENTIK_DB_PASSWORD=change-me
AUTHENTIK_DB_USER=authentik
AUTHENTIK_DB_NAME=authentik
AUTHENTIK_DB_HOST=authentik-postgres
AUTHENTIK_POSTGRES_PATH=/opt/authentik/postgres
##### Redis #####
AUTHENTIK_REDIS_HOST=
AUTHENTIK_REDIS_PATH=
AUTHENTIK_REDIS_HOST=authentik-redis
AUTHENTIK_REDIS_PATH=/opt/authentik/redis
##### Authentik #####
AUTHENTIK_SECRET_KEY=
AUTHENTIK_BOOTSTRAP_EMAIL=
AUTHENTIK_BOOTSTRAP_PASSWORD=
AUTHENTIK_BOOTSTRAP_TOKEN=
AUTHENTIK_SECRET_KEY=change-me
AUTHENTIK_BOOTSTRAP_EMAIL=admin@sherlockhomeless.net
AUTHENTIK_BOOTSTRAP_PASSWORD=change-me
AUTHENTIK_BOOTSTRAP_TOKEN=change-me
AUTHENTIK_HTTP_PORT=9000
##### Traefik / dominios #####
TRAEFIK_DOCKER_NETWORK=
AUTHENTIK_DOMAIN=
GITEA_DOMAIN=
TRAEFIK_ENTRYPOINT_SECURE=
TRAEFIK_CERTRESOLVER=
##### Correo (SMTP) #####
AUTHENTIK_EMAIL__HOST=mail-relay
AUTHENTIK_EMAIL__PORT=587
AUTHENTIK_EMAIL__USERNAME=
AUTHENTIK_EMAIL__PASSWORD=
AUTHENTIK_EMAIL__FROM=authentik@sherlockhomeless.net
AUTHENTIK_EMAIL__USE_TLS=false
AUTHENTIK_EMAIL__USE_SSL=false
##### Traefik / dominios #####
TRAEFIK_DOCKER_NETWORK=proxy
AUTHENTIK_DOMAIN=auth.sherlockhomeless.net
GITEA_DOMAIN=gitea.sherlockhomeless.net
THS_DOMAIN=sherlockhomeless.net,www.sherlockhomeless.net
TRAEFIK_ENTRYPOINT_SECURE=websecure
TRAEFIK_CERTRESOLVER=letsencrypt
SERVICE_URL_THS_AUTHENTIK_SERVER=https://auth.sherlockhomeless.net
SERVICE_FQDN_THS_AUTHENTIK_SERVER=auth.sherlockhomeless.net