Ir al contenido

WiFi corporativo, un certificado fantasma y dos lecciones sobre capas ocultas de configuración

Rogelio Guerra Riverón
Autor
Rogelio Guerra Riverón
Construyendo mi propia infraestructura web desde cero. Aquí documento cada paso: servidores, redes, contenedores y lo que vaya surgiendo.

Dos incidentes con el mismo síntoma superficial — usuarios que dejan de poder conectarse al WiFi corporativo — y causas completamente distintas. Esto es un postmortem anonimizado de una tarde de troubleshooting en una organización mediana, más lo que una revisión de seguridad de paso dejó al descubierto.

Contexto
#

La organización tiene WiFi corporativo con 802.1X (usuarios y contraseñas de Active Directory) y una VPN con segundo factor TOTP, ambas autenticando contra el mismo servidor FreeRADIUS integrado vía Winbind/Kerberos — el mismo patrón que se describe en FreeRADIUS + TOTP + Active Directory, aunque aquí el WiFi usa PEAP en lugar de TOTP por las razones ya explicadas en EAP-TLS vs PEAP para WiFi empresarial.

Un día cualquiera, un usuario deja de poder conectarse. El mensaje de error en el log es de lo más inútil que existe:

Login incorrect (eap_peap: The users session was previously rejected: returning reject (again.))

Ese mensaje no dice por qué falló. Solo dice “ya había fallado antes, así que sigo rechazando”. Es un eco, no una causa. Perseguir el eco en vez de la causa original fue exactamente el primer error de este troubleshooting.

Pista falsa: “debe ser un problema de caché de sesión TLS”
#

La hipótesis inicial, razonable a primera vista: PEAP reutiliza sesiones TLS, y si una sesión falló una vez, el servidor cachea el rechazo y no vuelve a evaluar credenciales en los reintentos siguientes dentro de la misma sesión. Tiene sentido — hasta que se revisa la configuración y se confirma que el caché de sesión TLS estaba explícitamente deshabilitado (cache { enable = no }). No podía ser eso.

Lección: un mensaje de log que “suena” a un mecanismo conocido no garantiza que ese mecanismo esté siquiera activo. Verificar la configuración real antes de perseguir la hipótesis más obvia.

Causa real: un parámetro que enruta el tráfico a un sitio sin salida
#

Revisando los módulos de autenticación puestos en juego, el submódulo interno usado por el túnel PEAP tenía un auth_type apuntando a un método de autenticación distinto al que realmente se necesitaba. El efecto práctico: todo el tráfico de ese tipo de autenticación se desviaba silenciosamente a una rama de configuración vacía — sin contraseña conocida configurada — que siempre falla, sin invocar en ningún momento al módulo real de validación de credenciales, ni sus logs, ni sus mensajes de éxito o fallo.

Ese fue el hallazgo decisivo: la ausencia total de logs de un componente puede ser la pista más importante de todas. Si un módulo que debería procesar cada intento de login nunca aparece ni una sola vez en su propio log, probablemente el tráfico nunca está llegando a él — un patrón que también aparece en los errores frecuentes de FreeRADIUS con PAM.

Comparar la configuración activa contra un backup de una versión anterior conocida-como-funcional (que por suerte existía en el mismo servidor) confirmó el diagnóstico en segundos: el parámetro había sido cambiado respecto al estado documentado como correcto.

Lección: guardar y fechar copias de configuraciones que funcionan no es solo higiene de control de cambios — es una herramienta de diagnóstico de primera línea. Un diff contra un estado bueno-conocido es más rápido que reconstruir la lógica de un sistema complejo desde cero.

Efecto colateral: una cadena de autenticación con una dependencia implícita
#

Como parte de este incidente, se decidió desactivar temporalmente el segundo factor solo para el camino de autenticación WiFi (dejándolo intacto en la VPN), separando la configuración en dos rutas independientes. Al quitar el módulo que generaba el segundo factor de la cadena, el módulo siguiente — el que valida usuario y contraseña contra el directorio — empezó a fallar con un error distinto: “no se pudo obtener la contraseña del usuario”.

La causa: ese módulo estaba configurado para reutilizar una contraseña ya obtenida por el módulo anterior en la cadena (un flag típico de configuraciones PAM encadenadas), en vez de obtenerla él mismo. Al quitar el módulo que la obtenía, nadie se la entregaba.

Lección: en cualquier cadena de autenticación por módulos (PAM y equivalentes), los flags de tipo “usar la credencial ya obtenida” crean una dependencia implícita e invisible en el orden de la cadena. Quitar un eslabón sin revisar qué asume el siguiente rompe cosas de forma no obvia.

Segundo incidente: mismo síntoma superficial, causa completamente distinta
#

Poco después, otro usuario reportó un fallo de WiFi con un mensaje distinto pero igual de genérico:

(TLS) Alert read:fatal:unknown CA

Aquí el cliente rechaza el certificado del servidor durante el propio handshake TLS — ni siquiera llega a pedir usuario y contraseña. Investigar reveló que no era un caso aislado: un grupo creciente de equipos venía fallando así desde días antes, todos con el mismo patrón.

El certificado del servidor estaba en regla: vigente, cadena válida, sin cambios recientes. El problema estaba enteramente del lado cliente, y era sutil. La organización usa políticas de grupo (GPO) para distribuir confianza de certificados a los equipos del dominio, pero existen dos políticas independientes para esto, no una:

  • Una que empuja el certificado de la CA al almacén general de certificados de confianza de cada equipo.
  • Otra, completamente separada, que define el propio perfil de la red inalámbrica corporativa (SSID, tipo de EAP, etc.) — y que trae su propia lista embebida de autoridades de confianza específica para esa red, independiente del almacén general.

La primera estaba correctamente aplicada. La segunda — la que realmente valida el certificado durante el 802.1X — no tenía la CA marcada en su propia lista interna. Un equipo podía tener el certificado “instalado y de confianza” según el sistema operativo, y aun así rechazarlo en la conexión WiFi, porque el componente que realmente decide ahí mira una lista distinta.

Como curiosidad adicional al editar esa segunda política: el editor solo permite marcar como confiables las autoridades que ya estén presentes en el almacén de certificados del equipo desde el que se está editando la política — no ofrece un listado centralizado. Si la CA no aparece como opción, hay que instalarla primero localmente en esa máquina de administración, y recién entonces aparece disponible para marcar.

Lección: en infraestructuras híbridas (políticas centralizadas más configuración local, o varias políticas superpuestas), “está aplicado” no es lo mismo que “está aplicado en el lugar correcto”. Cuando dos mecanismos parecen resolver el mismo problema, hay que verificar ambos por separado — uno puede estar perfecto y el otro completamente vacío, y el síntoma es idéntico.

Revisión de seguridad de red y puertos
#

Aprovechando el incidente, se hizo una revisión de la exposición de red del servidor. Algunos hallazgos relevantes, sin entrar en detalles específicos de la infraestructura:

Una regla de firewall redundante y más permisiva de lo previsto. Existía una regla que restringía el acceso administrativo remoto a un rango de red interno — correcto — pero, más abajo en el conjunto de reglas, había una segunda regla, más amplia, que permitía ese mismo puerto desde cualquier origen. En la práctica, la regla amplia anulaba la utilidad de la restrictiva. No había evidencia de explotación activa, pero es exactamente el tipo de fallo de defensa en profundidad que solo se detecta revisando el conjunto completo de reglas, no una regla aislada.

Un bloqueo automático “activado” que nunca disparó. Una regla de bloqueo automático de intentos de login (tipo fail2ban) llevaba tiempo activada pero nunca disparó ni una sola vez, pese a que el patrón de fallos que se supone debía detectar ocurría repetidamente en los logs reales. La causa: la expresión regular del filtro estaba escrita contra un formato de log que ya no correspondía a la versión de software en producción. Una protección que existe solo “en el papel” da una falsa sensación de cobertura — hay que probarla contra logs reales, no asumir que funciona porque el servicio dice enabled = true.

Una campaña de fuerza bruta activa y sostenida contra el servicio de autenticación remota, con decenas de direcciones IP distintas rotando constantemente, probando nombres de usuario genéricos. El bloqueo automático por IP individual apenas hace mella en este patrón: cada IP prueba solo un puñado de veces antes de cambiar a otra, quedando siempre por debajo del umbral de bloqueo. Este tipo de ataque distribuido requiere controles adicionales — limitación de tasa más agresiva, listas de reputación de IP, o restringir el servicio a rangos de origen conocidos cuando sea posible — más allá del bloqueo reactivo por IP.

Secretos compartidos funcionales pero cortos para las claves usadas por el protocolo de autenticación remota — no rotos, pero por debajo de la longitud recomendada como buena práctica para ese tipo de secreto. El proceso para rotarlos sin cortar el servicio está detallado en rotación de shared secrets RADIUS.

Lecciones aplicables a cualquier troubleshooting de infraestructura
#

  • Un mensaje de error puede ser el eco de un fallo anterior, no la causa — perseguir el eco hace perder tiempo.
  • Comparar contra una configuración anterior conocida-como-buena (un backup, un control de versiones) es una de las formas más rápidas de encontrar qué cambió.
  • Los flags de “reutilizar credencial ya obtenida” en cadenas de autenticación modulares crean dependencias implícitas en el orden — documentarlas explícitamente evita sorpresas al modificar la cadena.
  • Cuando la confianza de certificados, o cualquier configuración, se gestiona por más de un mecanismo a la vez, “aplicado” no implica “aplicado donde importa”. Hay que verificar cada mecanismo por separado.
  • Una regla de firewall de más es tan peligrosa como una de menos — conviene auditar el conjunto completo de reglas periódicamente, no solo las que se acaban de agregar.
  • Las reglas de bloqueo automático deben probarse activamente contra logs reales; que estén habilitadas no significa que estén funcionando.
  • La fuerza bruta distribuida — muchas IPs, pocos intentos cada una — rompe los mecanismos de defensa basados únicamente en el conteo por IP individual.