Resumen
En mi laboratorio de seguridad administro las máquinas desde el navegador, sin VPN y sin abrir puertos hacia Internet. Este post explica cómo está armado, cómo lo verifiqué desde fuera y, sobre todo, qué encontré cuando lo revisé como lo haría un auditor.
Lo que funciona: nadie llega a la pantalla de Guacamole sin pasar antes por Cloudflare Access. Lo que me faltaba: una política de acceso condicional que estaba en modo “solo informe”, un MFA que se recuerda en lugar de pedirse cada vez y ningún monitoreo de estos accesos. Al final dejo las mejoras que voy a aplicar, en orden de prioridad.
Los nombres de host, identificadores y direcciones de este post son valores de ejemplo (
acceso.ejemplo.com,<equipo>.cloudflareaccess.com). No corresponden a mi entorno real.
El problema
Las dos formas clásicas de llegar a una consola de administración tienen un costo:
- Abrir RDP o SSH a Internet expone el servicio a escaneos y fuerza bruta las 24 horas.
- Una VPN da acceso a toda la red a quien se conecte, hay que mantener el concentrador y suele ser la puerta de entrada de muchos incidentes.
La alternativa es publicar solo una aplicación web de acceso, protegida por identidad, y que ella abra las sesiones hacia dentro.
La arquitectura
- El administrador abre la dirección pública de Guacamole.
- Cloudflare Access intercepta la petición y exige iniciar sesión.
- Access delega la autenticación en Entra ID (SSO).
- Con la sesión válida, Cloudflare Tunnel entrega el tráfico a Guacamole. El túnel es una conexión de salida desde el laboratorio, así que no hay puertos abiertos en el firewall.
- Guacamole, en un segmento de administración aislado, abre RDP o SSH hacia Kali, el controlador de dominio y Windows 11.
Cómo se arma
Los pasos son los habituales, con valores de ejemplo:
- Túnel. En una máquina del segmento de administración instalas
cloudflared, creas un túnel y publicas un nombre de host (acceso.ejemplo.com) que apunte a la aplicación local de Guacamole. - Entra ID como proveedor de identidad. Registras una aplicación de un solo tenant en Entra ID. Su única URI de redirección es el callback de Access:
https://<equipo>.cloudflareaccess.com/cdn-cgi/access/callback. Activas “asignación de usuarios obligatoria” y asignas solo a quien debe entrar. - Aplicación en Access. Creas una aplicación de Access para
acceso.ejemplo.comcon una política de tipo Allow y el proveedor de Entra ID como método de inicio de sesión. - Guacamole. Dentro del segmento de administración, con las conexiones RDP y SSH hacia las máquinas que necesites y reglas en el firewall que limiten a qué puede llegar.
Lo que verifiqué desde fuera
Con una petición sin sesión, el servidor no sirve Guacamole: responde con una redirección al inicio de sesión de Access.
| |
Al abrir esa dirección en el navegador aparece la pantalla “Log in to guacamole” de Cloudflare Access, no la de Guacamole. Esa es la propiedad que importa: antes de la identidad, no hay aplicación.
Lo que encontré al revisar mi propia configuración
Aquí está lo valioso del ejercicio. Todo “funcionaba”, pero no estaba como yo creía.
1. Una política de acceso condicional que no protegía nada
Tenía una política llamada “Require MFA - Cloudflare Access SSO” pensada para esa aplicación. Estaba en modo “solo informe”: evalúa y registra, pero no bloquea ni exige nada. Además, la única política activa que podía aplicarme excluía mi propia cuenta.
En los inicios de sesión, el acceso condicional figuraba como “No aplicada”. Si solo hubiera mirado el nombre de la política, habría dado por bueno el control.
2. El MFA existía, pero por otra vía y se recordaba
El inicio de sesión sí pedía MFA, pero lo hacía MFA por usuario (el modelo heredado), no el acceso condicional. Y en los dos inicios de sesión que revisé, el requisito se cumplió “por un claim en el token”: la directiva Recordar autenticación multifactor reutilizaba una verificación anterior.
No es un fallo, pero cambia lo que puedes afirmar. No es “MFA cada vez que entras”, es “MFA exigido y recordado”.
Un detalle práctico: estos datos no los vi consultando la API de Microsoft Graph (los detalles de autenticación llegaban vacíos). Solo aparecieron en el portal de Entra, en el inicio de sesión, pestaña Detalles de autenticación. Si automatizas una auditoría, no te fíes de un campo vacío.
3. Una puerta que parecía abierta
La pantalla de Access ofrece Entra ID y, debajo, una opción de código por correo (“Send login code”). Probé con mi propio correo y no recibí ningún código, lo que sugiere que mi política no lo permite. Aun así la opción sigue visible, y es superficie innecesaria. Solo probé con mi cuenta, así que no puedo afirmar nada de otras.
4. Sin monitoreo
Los registros de Access y de Guacamole todavía no llegan a ningún SIEM. Hoy, si alguien intentara entrar, yo no lo vería. Es la brecha más grande de las cuatro, y tiene su propio plan más abajo.
Lo que este diseño no resuelve
Ocultar los puertos no vuelve inmune a la aplicación:
- Una vulnerabilidad en Guacamole sigue siendo explotable por quien logre pasar Access.
- Una sesión robada (la cookie de Access o la sesión del navegador) permite entrar sin autenticarse de nuevo.
- El token del túnel es una credencial: quien lo tenga puede registrar un conector. Trátalo como un secreto.
- Una cuenta de administrador comprometida abre todas las máquinas que Guacamole alcanza.
- Cloudflare pasa a ser un punto de confianza y de disponibilidad.
Mejoras planeadas
Ninguna de estas mejoras está aplicada todavía. Las ordeno por impacto y esfuerzo, y actualizaré este post a medida que las cierre.
| # | Mejora | Por qué | Estado |
|---|---|---|---|
| 1 | Activar la política de acceso condicional dedicada, con una fuerza de autenticación de MFA (idealmente resistente a phishing) | Convierte un control que solo informa en uno que protege | Pendiente |
| 2 | Quitar el código por correo como método de inicio de sesión de la aplicación | Reduce superficie: deja Entra ID como única vía | Pendiente |
| 3 | Enviar los registros a Sentinel y crear alertas (plan abajo) | Hoy un intento de acceso pasaría desapercibido | Pendiente |
| 4 | Definir la frecuencia de inicio de sesión y revisar el “recordar MFA” | Que el MFA se pida con la frecuencia que decida yo, no la que dé el recuerdo | Pendiente |
| 5 | Limitar la política de Access a un grupo de Entra ID y no a una cuenta | Facilita revocar o añadir accesos sin tocar la política | Pendiente |
| 6 | En Guacamole: segundo factor propio, mantenerlo actualizado y quitar cuentas por defecto | Es la aplicación que queda expuesta tras pasar Access | Pendiente |
| 7 | En el firewall: que Guacamole solo llegue a los destinos y puertos necesarios | Limita el daño si Guacamole se compromete | Pendiente |
| 8 | Proteger y rotar el token del túnel | Es una credencial: quien lo tenga puede registrar un conector | Pendiente |
Plan de monitoreo
Hay tres fuentes de datos, cada una con su camino hacia Sentinel:
- Inicios de sesión de Entra ID. Hay que exportar los registros de inicio de sesión a un workspace de Log Analytics (por la configuración de diagnóstico o el conector de Entra ID). Es la fuente que más rápido da valor, porque ya contiene el resultado, la ubicación y el estado del acceso condicional.
- Solicitudes de Cloudflare Access. Access registra cada solicitud y se pueden exportar con Logpush hacia un almacenamiento, y de ahí a Log Analytics.
- Guacamole. Los intentos de autenticación fallidos y las sesiones quedan en el log de la aplicación. Hay que reenviarlos con un agente o por syslog.
Estas son las consultas con las que empezaría. Todavía no las he desplegado, y conviene ajustarlas al esquema de tu workspace.
Inicios de sesión a la aplicación en los que el acceso condicional no aplicó. Es justo lo que encontré a mano:
Fallos o accesos desde un país que no es el habitual. Candidata a regla de alerta:
| |
Cuántas veces el MFA se cumple con una verificación nueva y cuántas con una anterior. Mide el efecto del “recordar MFA”:
Cuando una de estas cosas esté en marcha, la marcaré como hecha en la tabla.
Cómo probarlo tú
- Sin sesión: pide la dirección pública con
curl -sIy comprueba que recibes una redirección a Access, no la aplicación. - Desde fuera: escanea la IP pública del laboratorio y confirma que no hay puertos de administración abiertos.
- Con sesión: abre tu inicio de sesión en el portal de Entra y mira Detalles de autenticación: requisito, método y si se cumplió con una verificación nueva o “por claim en el token”.
Conclusión
Publicar una consola sin VPN y sin puertos abiertos es sencillo. Lo difícil es saber qué está protegiendo realmente cada capa. En mi caso, la capa de identidad funcionaba, pero una de las políticas que daba por activas no lo estaba y el MFA no se pedía como yo suponía.
Por eso conviene auditar tu propia configuración como lo haría alguien externo, y documentar lo que no está resuelto, no solo lo que sí.