Problema
Muchas organizaciones tienen un catálogo creciente de aplicaciones web internas que se ejecutan en contenedores Docker o en clústers de Kubernetes. Los usuarios necesitan acceder a esas apps desde sus smartphones, pero el parque de dispositivos es mixto: algunos están gestionados mediante Intune, otros son BYOD y no pueden recibir agentes, certificados ni configuraciones especiales.
El reto consiste en ofrecer acceso clientless (a través del navegador o una app mínima) manteniendo:
- Autenticación única con Entra ID (OIDC).
- Tráfico dentro de la infraestructura propia, sin depender de proxies externos.
- Un modelo que escale con pocos operadores y que no requiera cambios en cada dispositivo.
Causa
Los fallos habituales en este tipo de despliegues provienen de tres áreas:
-
Exposición directa de los contenedores
Publicar puertos de los pods o de los contenedores Docker en Internet rompe el principio de “defensa en profundidad”. Además, los firewalls suelen bloquear tráfico inesperado y los navegadores móviles pueden rechazar certificados autofirmados. -
Autenticación fragmentada
Cuando la SSO se implementa solo en la capa de la aplicación, los dispositivos BYOD no pueden completar el flujo OIDC porque la red interna no está accesible. Los intentos de “basic auth” o de tokens estáticos son inseguros y difíciles de rotar. -
Falta de segmentación y control de acceso
Sin un punto de entrada único, cada aplicación necesita reglas de firewall propias. El mantenimiento se vuelve caótico y los errores de configuración aparecen con frecuencia (puertos abiertos, rutas mal definidas, etc.).
Solución
Un enfoque reutilizable combina un reverse proxy en el perímetro, un controlador de ingreso OIDC y políticas de Zero Trust en Entra ID. El esquema básico es:
- Punto de entrada único – Un load balancer con WAF en la DMZ recibe todo el tráfico HTTPS (puerto 443).
- Reverse proxy con OIDC – Nginx (o Traefik) ejecuta
oauth2-proxycomo sidecar. El proxy valida la sesión contra Entra ID y, una vez autenticado, genera una cookie de sesión corta. - Ingress en Kubernetes – Cada aplicación se expone mediante un Ingress que solo permite tráfico proveniente del proxy interno (IP del load balancer).
- Conditional Access – Entra ID define dos grupos:
- Managed – dispositivos Intune‑compliant → política que permite acceso sin MFA si la señal de cumplimiento está presente.
- Unmanaged – cualquier BYOD → política que exige MFA y revisa la ubicación (IP del perímetro).
- TLS terminada en el perímetro – El certificado público es gestionado por la autoridad interna o por Let’s Encrypt; el tráfico entre el proxy y los pods sigue cifrado (mutual TLS opcional).
Paso a paso
a) Configurar el load balancer y WAF
- Asignar un FQDN público (p.ej.
apps.corp.example.com). - Habilitar reglas WAF para OWASP Top 10 y limitar los métodos HTTP a GET, POST, HEAD.
b) Desplegar oauth2-proxy
- Registrar una aplicación en Entra ID con redirect URI
https://apps.corp.example.com/oauth2/callback. - Guardar
client_idyclient_secreten un secret de Kubernetes. - Ejecutar
oauth2-proxycon los scopesopenid profile emaily concookie-refreshde 5 min.
c) Configurar Nginx como front‑end
- Definir un bloque
serverque redirija/oauth2/aoauth2-proxy. - Proteger los demás
locationconauth_request /oauth2/auth. - Añadir encabezados
X-Forwarded-Userpara que las apps internas conozcan al usuario.
d) Ingress por aplicación
- Cada app tiene su propio Ingress que apunta al servicio interno.
- Se usa la anotación
nginx.ingress.kubernetes.io/whitelist-source-rangecon la IP del proxy.
e) Políticas de Conditional Access
- Crear una política que aplique a la aplicación “Internal Apps”.
- Condición “Device platform = iOS/Android”.
- Grant: “Require multi‑factor authentication” para usuarios no compliant; “Require device to be marked as compliant” para los gestionados.
f) Opcional: Azure AD Application Proxy como fallback
- Si alguna app necesita acceso externo sin pasar por el perímetro (p.ej. pruebas rápidas), habilitar Application Proxy solo para esa ruta. Mantener la regla de firewall que permita tráfico saliente al agente de Proxy.
Cuándo aplicar esta solución
Vale cuando:
- Se dispone de un perímetro con load balancer y WAF.
- Los usuarios usan Entra ID y se cuenta con licencias E5 (para Conditional Access).
- El equipo de operaciones es pequeño y necesita una única superficie de gestión.
- Las apps están containerizadas y pueden exponerse mediante Ingress.
No aplica si:
- No hay control de red en la DMZ (por ejemplo, solo VPN sin firewall de aplicación).
- La organización prohíbe cualquier tráfico HTTPS fuera de la red interna (ni siquiera al FQDN público).
- Se requiere acceso sin SSO (p.ej., apps legacy sin OIDC).
Código
# Deploy oauth2-proxy as a Kubernetes Deployment
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: oauth2-proxy
spec:
replicas: 2
selector:
matchLabels:
app: oauth2-proxy
template:
metadata:
labels:
app: oauth2-proxy
spec:
containers:
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:latest
args:
- --provider=oidc
- --oidc-issuer-url=https://login.microsoftonline.com/<tenant-id>/v2.0
- --client-id=$(CLIENT_ID)
- --client-secret=$(CLIENT_SECRET)
- --cookie-secret=$(COOKIE_SECRET)
- --email-domain=*
- --upstream=file:///dev/null
- --http-address=0.0.0.0:4180
envFrom:
- secretRef:
name: oauth2-proxy-secret
ports:
- containerPort: 4180
EOF
# Nginx snippet for the reverse proxy (placed in ConfigMap)
cat <<'NGINX' > nginx.conf
server {
listen 443 ssl;
server_name apps.corp.example.com;
ssl_certificate /etc/nginx/certs/tls.crt;
ssl_certificate_key /etc/nginx/certs/tls.key;
location /oauth2/ {
proxy_pass http://oauth2-proxy:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
auth_request /oauth2/auth;
error_page 401 = @error401;
proxy_pass http://internal-apps;
proxy_set_header X-Forwarded-User $http_x_auth_request_user;
}
location @error401 {
return 302 https://$host/oauth2/start?rd=$request_uri;
}
}
NGINX
Verificación
-
Desde un dispositivo gestionado
- Abre
https://apps.corp.example.comen Edge o Chrome. - Debería redirigir a la página de login de Entra ID y, tras la autenticación, entrar sin solicitar MFA.
- Abre
-
Desde un BYOD
- Repite el paso anterior. El flujo debe pedir MFA (push, código o biometría).
-
Comprobar encabezados
- En la consola del navegador, verifica que la petición a la app interna contiene
X-Forwarded-Usercon el UPN del usuario.
- En la consola del navegador, verifica que la petición a la app interna contiene
-
Revisar logs del proxy
kubectl logs -l app=oauth2-proxydebe mostrar “authenticated_user” y la duración de la sesión.
-
Escaneo de puertos
- Desde fuera de la red, intenta conectar directamente al puerto del contenedor (p.ej.,
curl -k https://10.0.5.12:8443). Debería fallar, confirmando que solo el proxy está expuesto.
- Desde fuera de la red, intenta conectar directamente al puerto del contenedor (p.ej.,
Notas adicionales
- Rotación de secretos – Usa Azure Key Vault o sealed‑secrets para que
client_secretycookie_secretse renueven automáticamente. - CSP y HSTS – Añade encabezados de seguridad en Nginx para mitigar XSS y ataques de downgrade.
- Auditoría de tokens – Configura Azure AD para registrar los eventos de login y revocar tokens comprometidos.
- Pruebas de carga – Simula 100 conexiones concurrentes con
heyok6para validar que el proxy y el WAF no se convierten en cuello de botella. - Fallos típicos –
- Redirecciones infinitas cuando el
redirect_urino coincide exactamente
- Redirecciones infinitas cuando el