# Identidad, acceso y seguridad

<span>Donde se decide </span>**quién entra, cómo y con qué dolor**.

# Authentik

Parcheando lo que Authentik no explica hasta que ya estás logueado fuera y cagándote en todo.

# Actualizar el fondo de todos los flows en Authentik (vía SQL)

---

Cambiar los fondos de todos los flows de Authentik (pantalla de login, registro, recuperación de contraseña, etc.) desde la interfaz puede ser lento y tedioso si se hace uno a uno. Para evitar ese paseo clic por clic, lo más práctico es lanzar una consulta SQL que los actualice todos de golpe.

---

### Características

* Cambia el fondo de todos los flows de Authentik de una sola vez.
* No hace falta reiniciar contenedores ni hacer magia negra.
* Ideal para dejarlo bonito con una sola línea SQL.

---

### Paso a paso

#### 1. Entra en el contenedor de la base de datos

Asumiendo que usas Docker, el primer paso es meterse dentro del contenedor de PostgreSQL:

```bash
docker exec -it <nombre_del_contenedor_postgres> bash
```

Sustituye `<nombre_del_contenedor_postgres>` por el nombre real de tu contenedor. Si usas `docker ps`, lo verás fácil.

---

#### 2. Conéctate a PostgreSQL

Normalmente, el usuario y la base de datos se llaman igual: `authentik`. Si no has tocado nada raro, este comando debería bastar:

```bash
psql -U authentik -d authentik
```

Cuando te lo pida, mete la contraseña. Es la misma que tienes en el `.env`, bajo `PG_PASS`.

---

#### 3. Cambia los fondos de todos los flows

Aquí viene la línea mágica:

```sql
UPDATE authentik_flows_flow SET background = 'https://w.wallhaven.cc/full/2y/wallhaven-2yp6gg.png';
```

Obviamente, puedes poner cualquier URL de imagen que te guste. Yo suelo usar [Wallhaven](https://wallhaven.cc/), clic derecho en la imagen y “copiar enlace”.

---

#### 4. Sal de la base de datos

Cuando termines, escribe:

```sql
\q
```

Y luego `exit` para salir del contenedor.

---

#### 5. Prueba si ha funcionado

Abre una ventana en modo incógnito (para evitar cacheos raros) y accede a tu pantalla de login. Si todo ha ido bien, el cambio es instantáneo. No hace falta reiniciar nada.

---

### Conclusión

Así de fácil es dejar tu Authentik con estilo. Este truco no está en la documentación oficial, pero funciona perfecto si sabes por dónde meterle mano. Y si un día te aburres del fondo… repite el proceso con otra URL.

---

### Referencias

* [Comentario en GitHub](https://github.com/goauthentik/authentik/issues/4340#issuecomment-1402460831)

# Cloudflare Turnstile en Authentik - Captcha previo a la autenticación

---

### Introducción

Este artículo documenta la integración de **Cloudflare Turnstile** como etapa de captcha **previa** al proceso de autenticación en **Authentik**, con el objetivo de filtrar tráfico automatizado **antes incluso de mostrar el formulario de login**.

La integración se sitúa directamente dentro del *authentication flow* de Authentik, actuando como una barrera inicial que reduce ruido, ataques de fuerza bruta y automatización maliciosa sin introducir fricción innecesaria para usuarios legítimos. No sustituye otros mecanismos (MFA, rate‑limit, CrowdSec), sino que los refuerza desde el primer punto de entrada.

---

### Enfoque general / Arquitectura

El enfoque se basa en insertar una **Captcha Stage** como primera etapa del `default-authentication-flow`:

```
Cliente → Captcha (Cloudflare Turnstile) → Formulario de login → MFA / resto de etapas
```

Características clave del enfoque:

* El captcha se presenta **antes** de solicitar credenciales.
* No se depende de puntuaciones de riesgo (Turnstile no las expone).
* Compatible con modos *Managed* e *Invisible*.
* Integración nativa en Authentik, sin proxies intermedios ni JS personalizado.

Este planteamiento reduce carga en Authentik y en servicios posteriores, descartando tráfico basura desde el primer renderizado de la página.

---

### Requisitos previos

* Instancia funcional de Authentik con acceso al **Admin Interface**.
* Dominio gestionado en Cloudflare.
* Acceso a la sección **Turnstile** en Cloudflare.
* Flujo de autenticación estándar (`default-authentication-flow`) sin modificaciones incompatibles.

---

### Desarrollo

#### Qué se hizo y por qué

Se opta por **Cloudflare Turnstile** frente a reCAPTCHA por varios motivos:

* No requiere interacción explícita en la mayoría de escenarios.
* No depende de tracking invasivo ni cookies de terceros.
* Integración directa soportada por Authentik.
* Menor fricción para usuarios humanos reales.

La decisión de colocarlo **antes del login** es deliberada: cualquier captcha posterior ya implica haber servido HTML sensible (formularios, endpoints, lógica de sesión).

---

#### Configuración utilizada (solo enlaces)

* Documentación oficial de Turnstile:
  [Widgets Turnstile](https://developers.cloudflare.com/turnstile/concepts/widget/)

* Widget Turnstile (Cloudflare Dashboard):
  Creación del widget desde el dominio correspondiente para obtener:

  * **Site Key**
  * **Secret Key**

---

#### Configuración en Authentik (conceptual)

1. Crear una **Captcha Stage** desde:
   *Admin → Flows and Stages → Stages → Create → Captcha Stage*

2. Parámetros relevantes de la etapa:

   * **Public Key** → *Turnstile Site Key*
   * **Private Key** → *Turnstile Secret Key*
   * **Enable Interactive** → Activado si el widget está en modo *Managed* o *Invisible*
   * **JS URL**
     `https://challenges.cloudflare.com/turnstile/v0/api.js`
   * **API URL**
     `https://challenges.cloudflare.com/turnstile/v0/siteverify`

   Las opciones de *score* se dejan por defecto, ya que **no aplican a Turnstile**.

3. Inserción en el flujo:
   *Admin → Flows and Stages → Flows → `default-authentication-flow`*

   En **Stage Bindings**:

   * Añadir la Captcha Stage.
   * Asignarle el **orden más bajo** del flujo para que se ejecute antes que cualquier otra etapa.

---

### Validación

Comprobaciones mínimas tras la integración:

* Acceso al endpoint de login:

  * El captcha aparece **antes** del formulario.
* Tráfico humano normal:

  * No se presentan desafíos visibles en la mayoría de casos.
* Bots y escáneres:

  * No alcanzan el formulario de autenticación.
* Logs de Authentik:

  * Sin errores de validación contra la API de Cloudflare.
* Dashboard de Cloudflare:

  * Métricas de Turnstile registrando solicitudes correctamente.

---

### Decisiones importantes o problemas detectados

* Turnstile **no soporta puntuaciones**, por lo que cualquier lógica basada en umbrales debe descartarse.
* El orden de la etapa es crítico: si no es la primera, pierde sentido.
* No interfiere con MFA ni con autenticación por WebAuthn: actúa antes de todo.

---

### Resumen breve

* Captcha basado en Cloudflare Turnstile.
* Integrado como primera etapa del `default-authentication-flow`.
* Filtrado de bots **antes del login**.
* Sin fricción visible para usuarios legítimos.
* Implementación nativa, limpia y mantenible.

---

### Referencias

* [Cloudflare Turnstile — Documentación oficial](https://developers.cloudflare.com/turnstile/)

* [Authentik — Captcha Stages](https://docs.goauthentik.io/add-secure-apps/flows-stages/stages/captcha/)

* [Protect Authentik with Cloudflare Turnstile - Créditos al autor del vídeo en YouTube](https://www.youtube.com/watch?v=Fe5SttNa2lU)

# Creación de aplicaciones, providers y outposts en Authentik

---
### Introducción

Authentik es una plataforma open source pensada para gestionar la autenticación y los accesos de usuarios en sistemas multiusuario, ya sea en entornos personales, empresariales o mixtos. Su enfoque modular y flexible permite centralizar la seguridad y el control de quién entra, cuándo y a qué aplicaciones. En esta guía, vamos a ver cómo crear una aplicación dentro de Authentik, configurarle su provider correspondiente y enlazarla con un outpost para dejarla operativa. Todo explicado paso a paso y sin rodeos.

---

### Crear aplicación + provider con el asistente

1. Accede al panel de administración de Authentik.

2. En la sección lateral, ve a **Applications**.

3. Si tienes una versión reciente, verás este mensaje:

   > *You can now configure both an application and its authentication provider at the same time with our new Application Wizard.*

4. Pulsa en **Create with wizard**. Este asistente te permitirá crear la aplicación y su provider en una misma secuencia, ahorrándote tiempo y errores.

#### Paso 1: Configurar la aplicación

* En el campo **Name**, escribe un nombre claro para tu aplicación, por ejemplo: `test`.
* El **slug** se autocompleta con base en el nombre, pero puedes editarlo si lo necesitas.
* En **UI Settings**, rellena el campo **Launch URL** con la dirección pública de la app, como `https://test.dominio.es`.
* Pulsa **Next** para continuar.

#### Paso 2: Elegir el tipo de provider

* Selecciona **Proxy provider** como método de autenticación.
* Este tipo es el más habitual para forward authentication con proxies como Caddy o Traefik.
* Pulsa **Next**.

#### Paso 3: Configurar el provider

* En **Authorization Flow**, elige la opción `default-provider-authorization-explicit-consent`, que obliga al usuario a aceptar el acceso a la aplicación explícitamente.
* En **Forward auth**, selecciona `Single Application` para que este provider funcione solo con esta app.
* En **External Host**, pon la misma URL pública que en el paso anterior: `https://test.dominio.es`.
* Pulsa **Next**.

#### Paso 4: Asignar permisos de acceso

* Aquí puedes definir quién puede acceder a la aplicación. Pulsa **Bind existing policy/group/user**.
* Selecciona **Group** y luego escoge el grupo que quieras permitir. Por ejemplo: `authentik Admins`.
* Puedes añadir más grupos o usuarios si lo necesitas más adelante.
* Pulsa **Save** o **Next** para continuar.

#### Paso 5: Finalizar creación

* Revisa que todo esté correcto y pulsa **Submit**.
* Con esto, tanto la aplicación como su provider quedarán listos y configurados.

---

### Asociar la app a un outpost

Ahora toca vincular la aplicación a un outpost, que es el componente de Authentik encargado de hacer de "puente" entre las apps y el sistema de autenticación.

1. Desde el menú lateral, entra en **Outposts**.
2. Verás uno ya creado por defecto llamado **authentik Embedded Outpost**. Haz clic en el lápiz para editarlo.
3. Asegúrate de que el **Type** esté en `Proxy`, que es el necesario para apps con forward authentication.
4. Baja a la sección **Applications**. Allí verás dos columnas: una con aplicaciones disponibles y otra con asignadas.
5. Busca tu app (`test`) en la lista de disponibles y haz doble clic sobre ella para moverla a la columna de asignadas.
6. Pulsa **Update** para guardar todos los cambios.

---

### Conclusión

¡Ya está! Acabas de crear y configurar tu primera aplicación en Authentik, junto con su provider correspondiente y asignación al outpost. Esto te da control centralizado sobre qué usuarios pueden acceder, desde qué grupos y con qué reglas. Si en el futuro necesitas añadir más aplicaciones, usuarios o personalizar políticas, ya conoces la base para moverte con soltura por Authentik. Todo desde una única interfaz, sin complicaciones y con una arquitectura limpia.

# Directiva de Authentik para Caddy

---
Si estás utilizando Authentik como solución de autenticación y quieres integrarlo en tu configuración de Caddy, puedes usar la siguiente directiva lista para funcionar (o al menos, funcional en mi caso):

```caddyfile
# Directiva de Authentik (mostrando la IP real del cliente, por si usas Cloudflare o similar)
(authentik) {
    reverse_proxy /outpost.goauthentik.io/* http://192.168.1.90:19000
    forward_auth http://192.168.1.90:19000 {
        uri /outpost.goauthentik.io/auth/caddy
        copy_headers X-Authentik-Username X-Authentik-Email X-Authentik-Groups X-Authentik-Name X-Authentik-Uid
        header_up X-Real-IP {client_ip}
        header_up X-Client-IP {client_ip}
    }
}

```
---

### Personalización de la configuración

Para adaptar esta directiva a tu entorno, sustituye:
- **`192.168.1.90`** por la IP de tu servidor donde se ejecuta Authentik.
- **`19000`** por el puerto que hayas configurado.

Si has desplegado Authentik mediante Docker, asegúrate de que el puerto configurado en `docker-compose.yml` coincide con el utilizado en esta directiva. Puedes encontrar o definir este puerto en el archivo `.env` que se usa al desplegar Authentik:

```env
AUTHENTIK_PORT_HTTP=19000
```
---
### Importando la directiva en Caddy

Una vez configurada la directiva de Authentik, solo queda importarla dentro de la configuración de Caddy para proteger las aplicaciones deseadas. Un ejemplo práctico para una biblioteca digital con **Calibre** sería:

```caddyfile
# Biblioteca Digital
calibre.example.com {
    reverse_proxy http://192.168.1.90:20980
    import authentik
}
```
---
### Notas finales

- Asegúrate de que las aplicaciones en Authentik están correctamente configuradas antes de importar la directiva.
- Si estás usando Docker, revisa que los contenedores estén corriendo y que los puertos sean accesibles.
- Caddy debe ser reiniciado o recargado (`caddy reload`) después de cualquier cambio en la configuración.

Con esto, Authentik debería empezar a proteger las aplicaciones configuradas en Caddy de manera efectiva.

# Evitar MFA en casa con Authentik

---
### Introducción

Evitar que Authentik pida el segundo factor en casa, donde es una molestia constante. Se aplica solo bajo una IP fija, y sin comprometer la seguridad externa.

---

### Características

* Login sin MFA desde casa (o IP elegida).
* Flujo normal para el resto del mundo.
* Compatible con Cloudflare como proxy.
* Logs limpios con IP y usuario reales.
* Reversible en segundos.

---

### Requisitos previos

* IP pública fija o semiestable (por rango).
* Caddy como reverse proxy.
* Outpost de Authentik funcionando.
* Dominio configurado en Cloudflare con API activa.

---

### Flujo general

```txt
Navegador ─▶ Cloudflare ─▶ Caddy ─▶ Authentik Outpost ─▶ App
                 ▲ IP real via X-Forwarded-For     ▲
                 └─────────────────────────────────┘
```

* Caddy extrae la IP real con `{client_ip}` y la pasa como cabecera.
* Authentik la lee en `ak_client_ip` y salta el MFA si coincide con la IP marcada.

---

### Caddyfile adaptado

> Importante: este ejemplo solo aplica a aplicaciones que estén protegidas por Authentik. Si una app no utiliza Authentik como sistema de autenticación, este snippet no tendrá efecto sobre ella.

> Antes de modificar nada, se recomienda hacer una copia de seguridad de tu `Caddyfile` actual. Por la naturaleza de los cambios (cabeceras, autenticación, DNS...), un error puede dejarte sin acceso a tus servicios.

> Antes de modificar nada, se recomienda hacer una copia de seguridad de tu `Caddyfile` actual. Por la naturaleza de los cambios (cabeceras, autenticación, DNS...), un error puede dejarte sin acceso a tus servicios.

> Ejemplo con datos falsos para documentar el flujo.

```caddyfile
{
	email     ejemplo@dominio.com
	acme_dns  cloudflare "API_KEY_FAKE123"

	servers {
		trusted_proxies static private_ranges \
			203.0.113.0/24 198.51.100.0/24 192.0.2.0/24
	}
}

(authentik) {
	reverse_proxy /outpost.goauthentik.io/* http://10.0.0.3:15500

	forward_auth http://10.0.0.3:15500 {
		uri /outpost.goauthentik.io/auth/caddy

		copy_headers {
			X-Authentik-Username
			X-Authentik-Email
			X-Authentik-Groups
			X-Authentik-Name
			X-Authentik-Uid
		}

		header_up X-Real-IP   {client_ip}
		header_up X-Client-IP {client_ip}
	}
}

# Cada app que requiera autenticación debe seguir este mismo patrón.
# Primero importamos el snippet de Authentik, luego definimos el reverse_proxy de la app.

app.midominio.com {
	log {
		output file /var/log/caddy/app-access.log
		format transform "{request>headers>X-Forwarded-For>[0]:request>remote_ip} - {user_id} [{ts}] \"{request>method} {request>uri} {request>proto}\" {status} {size}" {
			time_format "02/Jan/2006:15:04:05 -0700"
		}
	}

	route {
		# 1) Llama al snippet que gestiona la autenticación
		import authentik

		# 2) Redirige a la app protegida
		reverse_proxy http://10.0.0.3:8080
	}
}
```

---

### Policy en Authentik

1. **Expression Policy**
   *Nombre:* `Skip-Home-IP`

   ```python
   from ipaddress import ip_address
   return ak_client_ip == ip_address("203.0.113.42")
   ```

2. **Aplica la policy**
   *Ruta:* Flows and Stages → Flows → `default-authentication-flow` → Stage Bindings → `default-authentication-mfa-validation`
   *Bindea:* la policy `Skip-Home-IP`
   *Orden:* `0`		*Negate:* desactivado

> Si tienes IP dinámica pero dentro de un rango:

```python
from ipaddress import ip_network
return ak_client_ip in ip_network("203.0.113.0/24")
```

---

### Verificación

| Prueba           | Esperado                                       |
| ---------------- | ---------------------------------------------- |
| Desde casa       | Sin MFA.                                       |
| Fuera de casa    | MFA/login normal.                              |
| Audit → Requests | `ALLOW (policy)` y IP correcta en `Client-IP`. |
| `caddy validate` | Configuración válida.                          |

---

### Ventajas

| Qué                      | Por qué                                                 |
| ------------------------ | ------------------------------------------------------- |
| **Sin MFA en casa**      | Nada de abrir el móvil cada dos por tres.               |
| **IP real protegida**    | `trusted_proxies` evita que te cuelen cabeceras falsas. |
| **Mantenimiento mínimo** | Solo un snippet + una policy.                           |
| **Logs decentes**        | Se ve quién accedió, desde dónde, sin cosas raras.      |
| **Reversible fácil**     | Borra la policy o el `import`, y listo.                 |

---

### Resumen breve

* `trusted_proxies` + `{client_ip}` → IP real segura.
* Policy de tipo *Expression* comparando `ak_client_ip`.
* Todo controlado desde Caddy con un solo snippet.
* Flujo transparente, sin MFA en casa pero con seguridad fuera.

---

### Referencias

* [Rangos IP de Cloudflare](https://www.cloudflare.com/ips/) — Para configurar `trusted_proxies` según los saltos reales desde el proxy.

**Caddy:**

* [Implementación de Cloudflare en Caddy](https://wiki.jtrapero.eu.org/books/caddy/page/implementacion-de-cloudflare-en-caddy)
* [Caddy: gestionar dominios fácilmente](https://wiki.jtrapero.eu.org/books/servicios/page/caddy-gestion-e-instalacion-sencilla-de-dominios)
* [Directiva de Authentik para Caddy](https://wiki.jtrapero.eu.org/books/tips-utiles/page/directiva-de-authentik-para-caddy)

**Authentik:**

* [Authentik: proteger nuestras aplicaciones web](https://wiki.jtrapero.eu.org/books/contenedores-configuracion/page/authentik-proteger-nuestras-aplicaciones-web)
* [Authentik: plataforma de autenticación y autorización centralizada](https://wiki.jtrapero.eu.org/books/contenedores-instalacion/page/authentik-plataforma-de-autenticacion-y-autorizacion-centralizada)
* [Creación de aplicaciones, providers y outposts en Authentik](https://wiki.jtrapero.eu.org/books/tips-utiles/page/creacion-de-aplicaciones-providers-y-outposts-en-authentik)

# Forzar MFA en todos los usuarios de Authentik

---
Pequeña nota para dejar claro cómo obligar a que todos los usuarios de Authentik (ya existentes o nuevos) activen y usen un segundo factor de autenticación.

---

### Requisitos previos

* Authentik ya desplegado y funcionando.
* Acceso como administrador a la interfaz web.

---

### Pasos

#### 1. Editar el stage de MFA

En **Flows and Stages → Stages**, buscar `default-authentication-mfa-validation`.

* En el campo **Not configured action**, cambiarlo a:

  * **Force the user to configure an authenticator**.
* Elegir la etapa de configuración que corresponda (ejemplo: `default-authenticator-totp-setup`).

Con esto, cualquier usuario sin MFA configurado será obligado a activarlo.

---

#### 2. Insertar el stage en el flow de autenticación

* Ir a **Flows → default-authentication-flow**.
* En la pestaña **Bind existing stage**, añadir `default-authentication-mfa-validation`.
* Colocarlo en el orden adecuado:

  * Después de `default-authentication-identification` (orden 10).
  * Antes de `default-authentication-password` (orden 20).
  * Ejemplo: asignar **15** a `default-authentication-mfa-validation`.

---

### Resultado

Con esa configuración, cualquier usuario que intente iniciar sesión sin MFA configurado se verá obligado a hacerlo antes de poder acceder.

# Login sin contraseña en Authentik usando WebAuthn

---

### Introducción

Este artículo documenta la habilitación de **autenticación passwordless** en Authentik mediante **WebAuthn**, utilizando llaves físicas (YubiKey u otras compatibles).

La integración se realiza **a nivel de flujos de autenticación**, sin eliminar el login tradicional, sino añadiendo una vía alternativa controlada desde el flujo principal. El resultado es un inicio de sesión donde Authentik detecta que el usuario dispone de un autenticador WebAuthn y ofrece iniciar sesión **sin contraseña**.

---

### Requisito previo imprescindible: dispositivo WebAuthn registrado

Antes de modificar cualquier flujo, el usuario **debe tener registrada una llave WebAuthn**.

Este paso se realiza desde la **interfaz de usuario**, no desde el panel de administración:

* Acceso al perfil de usuario.
* Ajustes → **Dispositivos de MFA**.
* Añadir un nuevo dispositivo de tipo **WebAuthn device**.
* Registrar la llave física (YubiKey u otro autenticador compatible).

Sin este requisito, el flujo passwordless no tendrá ningún dispositivo que validar y el login no funcionará.

---

### Enfoque general

El esquema que se construye es el siguiente:

1. El flujo de autenticación por defecto identifica al usuario.
2. Si el usuario dispone de dispositivos WebAuthn registrados:

   * Se ofrece un **flujo alternativo passwordless**.
3. Dicho flujo valida el autenticador WebAuthn.
4. Tras la validación, se continúa con el login estándar de Authentik.

No se sustituye el login clásico: **se complementa**.

En usuarios con WebAuthn configurado, el proceso de autenticación se resuelve únicamente mediante la llave, sin interacción adicional ni validación de contraseña.

---

### Desarrollo

#### Creación del flujo passwordless

En la interfaz de administración:

* **Flows & Stages → Flows → Create**
* Nombre: `Authentik Passwordless Flow`
* Title: el mismo nombre (o uno descriptivo equivalente)
* **Designation**: `Authentication`

Este flujo se utilizará exclusivamente para la autenticación sin contraseña.

---

#### Validación de dispositivos WebAuthn

Dentro del flujo recién creado:

* Pestaña **Stage Bindings**
* **Create & bind Stage**
* Tipo de etapa: **Authenticator Validation Stage**

Configuración relevante:

* Name: `Authentik Passwordless WebAuthn`
* Allowed devices: **WebAuthn Authenticators**
* Configuration Stages:

  * Seleccionar el flujo de configuración de WebAuthn
  * Por defecto: `default-authenticator-webauthn-setup`

Order:

* `0` (primer paso del flujo)

Esta etapa es la encargada de validar los dispositivos WebAuthn ya configurados por el usuario.

---

#### Continuación con el login estándar

Dentro del mismo flujo:

* **Stage Bindings → Bind existing stage**
* Stage: `default-authentication-login`
* Order: un valor superior al anterior (por ejemplo `10`)

De este modo:

* Primero se valida la llave WebAuthn.
* A continuación, Authentik ejecuta su lógica estándar de inicio de sesión (gestión de sesión, cookies, etc.).

---

#### Enlace del flujo passwordless al flujo principal

Para que Authentik ofrezca este método de autenticación:

* **Flows & Stages → Flows**
* Abrir el flujo `default-authentication-flow`
* Ir a la pestaña **Stage Bindings**
* Editar la etapa `default-authentication-identification`

En **Flow settings**:

* Campo **Passwordless Flow**
* Seleccionar: `Authentik Passwordless Flow`

Guardar los cambios con **Update**.

Este paso vincula el flujo passwordless con el proceso de autenticación principal.

---

### Validación

* Cerrar sesión o abrir una ventana en modo incógnito.
* Acceder de nuevo a Authentik.
* Tras introducir el usuario, debe aparecer la opción de **iniciar sesión con una llave de seguridad**.

Si el usuario tiene WebAuthn configurado correctamente, el login se realizará sin contraseña.

---

### Decisiones importantes y consideraciones

* WebAuthn requiere HTTPS válido (certificados reales y funcionales).
* El navegador debe soportar WebAuthn (la mayoría de navegadores modernos lo hacen).
* Si el usuario no tiene un dispositivo WebAuthn registrado, Authentik recurrirá al login tradicional.
* No se recomienda eliminar el uso de contraseñas sin validar previamente este flujo.

---

### Resumen breve

* Registro previo de una llave WebAuthn por usuario.
* Creación de un flujo de autenticación passwordless.
* Validación de WebAuthn antes del login estándar.
* Integración del flujo en el proceso de autenticación principal.

---

### Referencias

* [Guía en vídeo (créditos al autor)](https://www.youtube.com/watch?v=aEpT2fYGwLw)

# Gitea

Notas personales sobre Gitea: no es una guía, es una vacuna contra perder tiempo.

# Añadir una llave física en Gitea

---
### Introducción

Configuración del inicio de sesión en Gitea mediante una llave física compatible con WebAuthn (YubiKey 5C NFC en este caso), manteniendo el método TOTP activo como respaldo en caso de pérdida o fallo de la llave.

---

### Requisitos previos

* Gitea actualizado (≥ 1.21) con soporte WebAuthn.
* Navegador compatible con FIDO2/WebAuthn.
* Llave física (YubiKey, SoloKey, etc.).
* Método TOTP ya configurado (por ejemplo con Aegis o Authy) para disponer de acceso alternativo.
* Variable `ROOT_URL` correctamente definida en el archivo `app.ini` con la URL pública real de la instancia.

---

### Desarrollo / pasos

#### 1. Acceder a la configuración de seguridad

Desde el perfil de usuario: **Perfil → Configuración → Seguridad**.

#### 2. Añadir la llave física

En el apartado **Two-Factor Authentication (Security Keys)**:

* Introducir un nombre identificativo para la llave (por ejemplo: `YubiKey 5C NFC`).
* Pulsar **Añadir llave de seguridad** y seguir las instrucciones del navegador para completar el registro.

#### 3. Confirmar el registro

La llave quedará listada con el nombre asignado. Se puede verificar su funcionamiento cerrando sesión y volviendo a iniciar con la llave insertada o aproximada (en caso de NFC).

#### 4. Mantener TOTP como respaldo

No eliminar el método TOTP. Servirá para acceder si la llave se pierde, se rompe o deja de funcionar.

---

### Errores comunes o decisiones importantes

* **Error `Error validating origin`**: revisar que la variable `ROOT_URL` en `app.ini` coincide con la URL pública (por ejemplo `https://gitea.tudominio.org`).
* Algunas configuraciones de proxy (Caddy, Nginx) pueden alterar cabeceras y provocar fallos en WebAuthn.
* Recomendable registrar más de una llave si se dispone de varias.

---

### Resumen breve

* Añadir la llave desde *Configuración → Seguridad*.
* Verificar `ROOT_URL`.
* Mantener TOTP como respaldo.
* Probar inicio de sesión con la llave.

---

### Referencias o enlaces de interés

* [Documentación oficial de Gitea sobre autenticación multifactor](https://docs.gitea.com/usage/multi-factor-authentication)
* [YubiKey en Gitea: error al registrar llave](https://wiki.jtrapero.eu.org/books/gitea/page/yubikey-en-gitea-error-al-registrar-llave)

# Personalización de Gitea: Temas y otros ajustes

---
### Introducción

Este artículo recoge los pasos necesarios para personalizar Gitea mediante temas, logos, favicons y plantillas. Todo con Docker, sin complicaciones raras. Asume que tienes los datos montados como `/mnt/data:/data` en el contenedor.

---

### Estructura de carpetas

Asegúrate de tener creada la carpeta `public` dentro del volumen de datos. Por ejemplo:

```
drwxr-xr-x user user 4.0 KB ... public
```

---

### Temas personalizados

1. **Crea tu CSS personalizado**, por ejemplo `mi-tema.css`.
2. Copia el archivo a:

```
/mnt/data/public/assets/css/mi-tema.css
```

3. Edita el archivo `app.ini` (en `/mnt/data/conf/app.ini`) y añade en `[ui]`:

```ini
[ui]
THEMES = gitea,arc-green,mi-tema
DEFAULT_THEME = mi-tema
```

4. Reinicia Gitea:

```bash
docker-compose restart gitea
```

5. Desde la interfaz, ve a `Configuración > Cuenta` y selecciona el tema.

---

### Cambiar logo y favicon

1. Prepara los archivos:

   * `logo.svg`
   * `favicon.png`

2. Copia a:

```
/mnt/data/public/assets/img/logo.svg
/mnt/data/public/assets/img/favicon.png
```

3. Reinicia el contenedor:

```bash
docker-compose restart gitea
```

---

### Personalizar plantillas

1. Crea o copia las plantillas a:

```
/mnt/data/public/templates
```

2. Edita con cuidado (usa sintaxis de Go templates).
3. Reinicia Gitea para aplicar los cambios.

---

### Buenas prácticas

* Haz cambios primero en un entorno de pruebas.
* Guarda copia de los archivos originales.
* Asegúrate de que todo lo que modifiques sea compatible con tu versión actual de Gitea.

---

### Referencias

* [Documentación oficial de Gitea](https://docs.gitea.com/)
* [Instalación de Gitea](https://wiki.jtrapero.eu.org/books/contenedores-instalacion/page/gitea-plataforma-de-gestion-de-repositorios-git-autohospedada)

# YubiKey en Gitea: Error al registrar llave

---

### Descripción

Al intentar añadir una YubiKey como método 2FA en Gitea, puede aparecer el error:

```
Could not read your security key.
An unknown error occurred. Please retry.
```

Y en los logs:

```
Unable to finish registration due to error: Error validating origin
DevInfo: Expected Values: [http+unix://gitea.tudominio.org], Received: https://gitea.tudominio.org
CreateCredential: Error validating origin
```

Este error ocurre cuando Gitea genera automáticamente la variable `ROOT_URL` de forma incorrecta (por ejemplo, con el prefijo `http+unix://`).

---

### Solución

Editar el archivo de configuración dentro del contenedor:

```bash
nano /data/gitea/conf/app.ini
```

o, según la ruta interna del contenedor:

```bash
nano /gitea/conf/app.ini
```

Modificar la línea correspondiente a la URL base de la instancia:

```ini
ROOT_URL = https://gitea.tudominio.org/
```

> Importante: asegúrate de incluir la barra final `/`.

Con esto, Gitea podrá validar correctamente el origen (`origin`) durante el registro de la YubiKey y permitirá añadirla sin errores. Probado con una Yubikey 5C NFC.

---

### Referencias

* [Issue en GitHub #26758 - Error al registrar YubiKey en Gitea](https://github.com/go-gitea/gitea/issues/26758)

# RustDesk

Cosas que fallaron en RustDesk justo cuando más lo necesitaba. Aquí dejo los trucos que me salvaron.

# RustDesk, conexión no cifrada

---

### Introducción

Este artículo documenta un **tip técnico** para asegurar correctamente las conexiones en **RustDesk**, evitando sesiones sin cifrar o conexiones forzadas a través de relay cuando no es necesario.

En determinados escenarios, una instalación funcional de RustDesk puede mostrar conexiones *relayed* o *unencrypted*, lo que implica mayor latencia y menor control sobre el tráfico. El objetivo de este ajuste es garantizar **cifrado extremo a extremo** y uso preferente de conexiones directas.

---

### Contexto del problema

RustDesk separa los roles de **HBBS (ID Server)** y **HBBR (Relay Server)**, y ambos utilizan claves para asegurar la comunicación. Si estas claves no están alineadas correctamente, pueden producirse los siguientes síntomas:

* Conexiones marcadas como *relayed* incluso en redes compatibles con P2P.
* Avisos de conexión no cifrada en el cliente.
* Indicador de seguridad ausente o incorrecto en la interfaz.

El problema no suele ser de red, sino de **desajuste de claves entre servicios**.

---

### Requisitos previos

Condiciones necesarias antes de aplicar este ajuste:

* Servidor RustDesk operativo.
* Acceso a la configuración de despliegue (contenedores).
* Clientes capaces de conectarse al servidor propio.

La instalación base de RustDesk se documenta en un artículo independiente.

---

### Enfoque general de la solución

La solución se basa en dos acciones clave:

* Forzar la generación y uso de claves por parte del servidor.
* Garantizar que **HBBS y HBBR comparten exactamente las mismas claves**.

Esto asegura que ambas piezas confían entre sí y permiten sesiones cifradas de extremo a extremo.

---

### Limitación de acceso mediante clave pública

RustDesk permite restringir el acceso al servidor mediante claves públicas. Para ello, los servicios deben arrancar con el parámetro de clave activado.

En un despliegue basado en Docker, es necesario modificar el comando de arranque de **HBBS** y **HBBR**.

Ejemplo de configuración:

```yaml
hbbr:
  command: hbbr -k _

hbbs:
  command: hbbs -r <IP_DEL_SERVIDOR>:21117 -k _
```

El carácter `_` indica que RustDesk debe **generar y gestionar automáticamente las claves**, sin intervención manual. A partir de este momento, todas las conexiones que pasen por el servidor estarán asociadas a este par de claves.

Este ajuste es imprescindible para habilitar cifrado extremo a extremo real.

---

### Sincronización de claves entre HBBS y HBBR

En algunos despliegues, RustDesk genera las claves en directorios distintos para cada servicio. Si estas claves no coinciden, el cliente mostrará conexiones *unencrypted* o forzará el uso de relay.

Pasos habituales para resolver el problema:

1. Localizar el volumen persistente donde RustDesk almacena sus datos. Por ejemplo:

```
/docker/rustdesk/
```

2. Identificar el directorio asociado a **HBBS**, donde se generan las claves:

```
/docker/rustdesk/hbbs/
```

3. Copiar las claves generadas hacia el directorio utilizado por **HBBR**:

```bash
cp /docker/rustdesk/hbbs/id_ed25519* /docker/rustdesk/hbbr/
```

Con esto se garantiza que ambos servicios utilizan exactamente el mismo par de claves.

Este paso corrige el conocido *key mismatch* y es crítico para que el cifrado funcione correctamente.

---

### Aplicación de cambios

Una vez aplicados los ajustes anteriores:

1. Reiniciar los servicios de RustDesk para que carguen las nuevas claves.
2. Verificar que los contenedores arrancan sin errores relacionados con claves o certificados.

No es necesario realizar cambios adicionales en los clientes, más allá de volver a conectar.

---

### Verificación

Para confirmar que la configuración es correcta:

* Conectar un cliente RustDesk al servidor.
* Verificar que aparece el **icono de escudo verde** en la interfaz.
* Confirmar que la conexión se establece como **directa** cuando la red lo permite.

Si alguno de estos puntos no se cumple, revisar nuevamente la generación y sincronización de claves.

---

### Consideraciones importantes

* Cada regeneración de claves requiere repetir la sincronización entre servicios.
* Las claves públicas solo deben compartirse con clientes de confianza.
* Este ajuste no elimina el relay, pero evita su uso innecesario.

---

### Resumen breve

Una instalación correcta de RustDesk puede funcionar sin cifrado completo si las claves no están alineadas. Forzar la generación de claves y sincronizarlas entre HBBS y HBBR garantiza conexiones cifradas, reduce latencia y evita dependencias innecesarias del relay.

---

### Referencias

* [How to Solve RustDesk Relayed and Unencrypted Connection Issues](https://medium.com/@gdadkisson/donovan-adkisson-how-to-solve-rustdesk-relayed-and-unencrypted-connection-issues-4f4de5fddf86)
* [GitHub: RustDesk Issue #499](https://github.com/rustdesk/rustdesk/issues/499)

# Wazuh

Mis notas de supervivencia para que Wazuh no se convierta en spam. Ajustes de alertas, reglas y demás magia para que solo me moleste cuando realmente hay fuego.

# Políticas que uso hoy en día en Wazuh

---

### Introducción

Apunte rápido para tener recogidas las políticas que uso en Wazuh. Si añado más en el futuro, irán aquí. La idea es mantener un equilibrio entre conservar información útil para análisis forense y no saturar el almacenamiento de la máquina virtual donde corre Wazuh.

---

### Retención de índices en OpenSearch

#### Alerts — 90 días

[Ver en Gitea](https://gitea.jtrapero.eu.org/R4di04kt1v3/ChronosCMPS/src/branch/main/Seguridad/Wazuh/Pol%C3%ADticas/Retenci%C3%B3n/wazuh-alerts-90d.json)

Política que elimina índices `wazuh-alerts-*` con más de 90 días de antigüedad. Se prioriza así mantener alertas recientes para correlaciones o revisiones rápidas, pero sin almacenar indefinidamente los índices que ya no aportan valor.

```json
"policy_id": "wazuh-alerts-90d",
"description": "Eliminar índices wazuh-alerts-* mayores de 90 días"
```

---

#### Archives — 180 días

[Ver en Gitea](https://gitea.jtrapero.eu.org/R4di04kt1v3/ChronosCMPS/src/branch/main/Seguridad/Wazuh/Pol%C3%ADticas/Retenci%C3%B3n/wazuh-archives-180d.json)

Política que elimina índices `wazuh-archives-*` con más de 180 días de antigüedad. En este caso, la retención es más amplia porque los archives contienen un histórico completo de eventos que pueden ser útiles para investigaciones más largas o revisiones de cumplimiento.

```json
"policy_id": "wazuh-archives-180d",
"description": "Eliminar índices wazuh-archives-* mayores de 180 días"
```

---

#### Métricas — 30 días

[Ver en Gitea](https://gitea.jtrapero.eu.org/R4di04kt1v3/ChronosCMPS/src/branch/main/Seguridad/Wazuh/Pol%C3%ADticas/Retenci%C3%B3n/wazuh-metrics-30d.json)

Política que elimina índices `wazuh-monitoring-*` y `wazuh-stats-*` con más de 30 días de antigüedad. Las métricas tienen un valor más inmediato (estado de agentes, consumo de recursos, estadísticas de uso) y rara vez son necesarias más allá de un mes.

```json
"policy_id": "wazuh-metrics-30d",
"description": "Eliminar índices wazuh-monitoring-* y wazuh-stats-* mayores de 30 días"
```

---

### Limpieza en disco con `crontab`

[Ver en Gitea](https://gitea.jtrapero.eu.org/R4di04kt1v3/ChronosCMPS/src/branch/main/Seguridad/Wazuh/Pol%C3%ADticas/Retenci%C3%B3n/crontab.md)

Además de la limpieza de índices en OpenSearch, aplico un borrado periódico en disco dentro de la VM de Wazuh. El espacio de esta máquina es limitado (\~60 GB), así que conviene eliminar también los ficheros crudos que se van acumulando en `/var/ossec/logs/`. De no hacerlo, podrían saturar el disco aunque los índices ya estén rotados.

```cron
# Elimina alertas con más de 90 días de antigüedad
0 0 * * * find /var/ossec/logs/alerts/ -type f -mtime +90 -exec rm -f {} \;

# Elimina archivos de archivo (archives) con más de 90 días de antigüedad
0 0 * * * find /var/ossec/logs/archives/ -type f -mtime +90 -exec rm -f {} \;
```

* **alerts/** → se borran logs de alertas activas con más de 90 días.
* **archives/** → se borran logs de archivo con más de 90 días.

Esta doble estrategia (índices en OpenSearch + ficheros en disco) asegura que los datos no se acumulen por duplicado en dos capas distintas.

---

### Resumen breve

* **Indices en OpenSearch**: 90 días para alerts, 180 para archives, 30 para métricas.
* **Limpieza en disco**: borrado de ficheros de alerts y archives >90 días en la VM.
* **Justificación**: mantener información útil a corto/medio plazo sin comprometer el espacio limitado de la máquina virtual.