Identidad y Gestión de Accesos
El sistema más seguro es el que está apagado, cubierto de hormigón y guardado en una habitación forrada de plomo con guardias armados — e incluso así tengo mis dudas.
Javi estaba a mitad de su segunda taza de café en la oficina de Mountain Lab una lluviosa mañana de miércoles cuando llegó la notificación de Slack. No era una alerta del sistema de monitorización, sino un mensaje directo de Diego, el CTO.
“Javi, ¿qué está pasando?”, pregunta Diego. “¿Por qué se está eliminando el clúster payments-live-db?”
Javi casi se atraganta. Corrió a su terminal, con los dedos volando sobre las teclas. Una comprobación rápida de la consola de la nube confirmó la pesadilla: la base de datos de producción para el equipo de pagos estaba en medio de un proceso de eliminación.
“Yo no he sido”, escribió Javi de vuelta, con el corazón acelerado. “Comprobando los logs ahora mismo”.
Tres minutos después, encontró al culpable. No fue un actor malicioso ni un script fuera de control. Fue un comando terraform destroy ejecutado por un runner de CI/CD en la cuenta payments-sandbox.
“Espera”, murmuró Javi para sí mismo. “¿Cómo tiene permiso un runner de sandbox para borrar una base de datos de producción?”.
Marta se unió a él en su escritorio, con el rostro serio. “Yo también lo veo. Parece que cuando migramos al equipo de pagos el mes pasado, alguien añadió el principal de servicio del runner de sandbox al grupo live-contributor ‘solo para una prueba rápida’ para verificar la conectividad. Se nos olvidó quitarlo”.
La base de datos se restauró a partir de una instantánea (snapshot) en veinte minutos, pero el daño a la confianza del equipo ya estaba hecho. Un simple error humano — un permiso caduco — casi borra la fuente de ingresos de la empresa.
En el post-mortem del día siguiente, el ambiente era sombrío.
“Estamos jugando con fuego”, dijo Marta, mirando a los líderes de ingeniería reunidos. “Tenemos desarrolladores con acceso ‘Contributor’ permanente a producción ‘por si acaso’ necesitan arreglar un bug a las 2:00 AM. Tenemos claves de CI/CD guardadas en secretos de GitHub desde hace años sin rotación. Nuestro IAM es una colección de ‘arreglos rápidos’ y grupos ’temporales’ que se volvieron permanentes”.
“Pero no podemos simplemente bloquear a todo el mundo”, argumentó uno de los desarrolladores. “Si tengo que abrir un ticket con IT cada vez que necesito revisar un log o reiniciar un servicio, nunca entregaremos nada”.
Marta asiente. “Estoy de acuerdo. No vamos a volver a TicketOps. Pero hemos terminado con los privilegios permanentes (standing privileges). A partir de ahora, vuestro estado por defecto en la nube es ser un fantasma. Podéis ver, pero no podéis tocar. Y cuando necesitéis tocar, tendréis que pedir permiso — no a una persona, sino a la plataforma”.
En los capítulos anteriores, definimos nuestra estrategia de segmentación y construimos el “sistema de coordenadas” que organiza nuestra infraestructura. Pero un segmento es tan seguro como las personas y procesos a los que se les permite entrar.
La Gestión de Identidad y Accesos (IAM, Identity and Access Management) se ve a menudo como el dominio del equipo de Seguridad o de IT corporativo. Pero en una plataforma moderna, IAM es una capacidad central de la infraestructura. Es la malla invisible que conecta a nuestros usuarios con sus recursos. Si nuestro IAM es demasiado restrictivo, matamos la velocidad del desarrollador. Si es demasiado permisivo, arriesgamos todo el negocio por una sola credencial caducada.
Para hacerlo bien, necesitamos alejarnos de los modelos de acceso de “todo o nada” del pasado y avanzar hacia una capa de autorización de autoservicio impulsada por la plataforma.
Identidad vs. Acceso
El primer paso para construir una estrategia de IAM escalable es reconocer una división arquitectónica fundamental: la diferencia entre Identidad y Acceso.
- Identidad (Quién eres): Es responsabilidad de IT Corporativo. Ellos gestionan el Proveedor de Identidad (IdP, Identity Provider) — ya sea Microsoft Entra ID (antes Azure AD), Okta o Google Workspace. Se encargan del ciclo de vida del usuario: altas, bajas, Autenticación de Múltiples Factores (MFA) y agrupaciones organizativas amplias (p. ej., “El equipo de Ingeniería”).
- Acceso (Qué puedes hacer): Es responsabilidad del equipo de plataforma. Tomamos las identidades proporcionadas por IT y las mapeamos a permisos de infraestructura específicos en nuestros sectores, tiers e inquilinos (tenants).
Esta división es crítica para la cordura operativa. El equipo de plataforma no debería dedicarse a restablecer contraseñas o gestionar quién se incorporó a la empresa el lunes pasado. Del mismo modo, IT Corporativo no debería necesitar entender los matices del RBAC de Kubernetes o la sintaxis de las políticas de IAM de AWS.
En su lugar, creamos una interfaz de autorización. IT proporciona el “Quién” y el equipo de plataforma proporciona el “Qué” a través de definiciones declarativas controladas por versiones. Para simplificar la carga cognitiva de los desarrolladores, debemos abstraer los miles de permisos granulares de la nube en un puñado de roles y grupos estandarizados.
Roles
Un Role define un conjunto específico de capacidades. Utilizando la Notación de Plataforma, definimos formalmente este concepto como un tipo funcional:
- Definición de Tipo:
Role(Nombre) - Valores Estándar:
"operator","admin","contributor","reader"
En nuestra plataforma, estandarizamos en cuatro roles principales:
Role("operator"): Reservado para los runners de CI/CD y los proveedores de infraestructura. Tiene el poder de crear y destruir recursos, pero — lo que es crucial — no tiene miembros humanos. No iniciamos sesión como un operador. En su lugar, utilizamos identidad federada (como Azure Workload Identity o AWS OIDC) para conceder estos permisos a nuestras pipelines de automatización.Role("admin"): El rol orientado a humanos con mayor privilegio. Permite gestionar políticas de IAM, crear endpoints públicos y eliminar almacenes de datos críticos. Debido a su poder, este rol nunca es un privilegio permanente. Está reservado para situaciones de emergencia o la configuración inicial del tenant, y siempre requiere una escalada Just-In-Time (JIT) con una justificación obligatoria.Role("contributor"): El “camino rápido” del desarrollador para la iteración rápida enTier("sandbox")o la resolución urgente de problemas enTier("live"). Permite operaciones de “escritura” — desplegar código manualmente, actualizar configuraciones o escalar recursos — sin necesidad de gestionar políticas de IAM o infraestructura compartida. Sin embargo, no es la ruta de despliegue principal.Role("reader"): El acceso permanente para todos los miembros de la organización de ingeniería. Te permite ver las configuraciones de los recursos, visualizar métricas y leer logs. Esto es suficiente para el 90% de la resolución de problemas diarios de un desarrollador. Al ser de solo lectura, conlleva un perfil de riesgo mucho menor.
Aunque estos roles estándar son un punto de partida pragmático, a menudo son demasiado amplios para entornos de alta seguridad. Mientras que Role("contributor") es eficaz para la resolución de problemas por parte de humanos, las aplicaciones nunca deberían usarlo. En su lugar, provisiona una identidad dedicada para cada carga de trabajo con los permisos mínimos absolutos requeridos (p. ej., s3:GetObject en un solo bucket).
Groups
Un Group es el contenedor donde colocamos a los miembros (humanos o no humanos) y les asignamos un rol específico dentro de un contexto específico.
- Definición de Tipo:
Group(Tenant, Tier, Role)
Esta notación nos permite expresar la “dirección” de un permiso con claridad. Siguiendo la gramática de la Notación de Plataforma, utilizamos la inferencia posicional para los parámetros:
Group("payments", "live", "operator"): El grupo para el runner de CI/CD que despliega los servicios deTenant("payments")enTier("live").Group("payments", "live", "admin"): El grupo para los miembros deTenant("payments")que necesitan acceso administrativo de emergencia enTier("live").Group("payments", "live", "contributor"): El grupo para los desarrolladores deTenant("payments")que necesitan realizar resolución de problemas activa u operaciones manuales enTier("live").Group("payments", "live", "reader"): El grupo para todos enTenant("payments")que necesiten ver el estado de su entornoTier("live").
Al separar los roles de los grupos, nos aseguramos de que el “qué” (los permisos del rol) se mantenga consistente en toda la organización, mientras que el “quién” y el “dónde” (la membresía y el alcance del grupo) se gestionan de forma independiente por tenant.
Acceso Just-In-Time (JIT)
El incidente de Mountain Lab ocurrió porque un runner tenía privilegios permanentes — permisos que estaban activos incluso cuando no se estaban utilizando. La solución a esto es el Acceso Just-In-Time (JIT).
El acceso JIT significa que los permisos se conceden solo cuando se necesitan, durante una ventana de tiempo específica, y se revocan automáticamente cuando esa ventana se cierra. Es el equivalente arquitectónico de un procedimiento de “romper el cristal” (break-glass), pero para las operaciones diarias.
El flujo de trabajo JIT
- Estado por defecto: Un desarrollador es miembro de
Group(Tenant, Tier, "reader"). - La solicitud: El desarrollador necesita corregir un error. Solicita unirse a
Group(Tenant, Tier, "contributor")a través de una CLI o un portal. - La justificación: El desarrollador debe proporcionar un motivo para la escalada (p. ej., “incident-402”, “depuración de fuga de memoria”). Esto crea un registro de auditoría permanente, vinculando cada acción elevada a una necesidad de negocio específica.
- La activación/aprobación: La plataforma verifica la pertenencia. Para
Role("contributor")enTier("sandbox"), esto puede ser automático. ParaRole("admin")oTier("live"), puede requerir la aprobación de un compañero, añadiendo un segundo par de ojos a las operaciones de alto riesgo. - La ventana: El desarrollador es añadido al grupo durante una ventana de 4 horas. Cada acción que realice se registra y se vincula a esta activación específica.
- La revocación: Una vez que expira la ventana, la plataforma elimina automáticamente al desarrollador del grupo.
Esta filosofía de “Reader por defecto” reduce significativamente la superficie de ataque. Si roban el portátil de un desarrollador o se filtran sus credenciales, el atacante solo obtiene acceso de lectura. Para causar cualquier daño, tendría que navegar con éxito el proceso de escalada JIT, que a menudo implica MFA y registros de auditoría.
Identidad no humana
Gestionar el acceso humano es solo la mitad de la batalla. También tenemos que gestionar las identidades “no humanas”: los runners de CI/CD, las tareas programadas (cron jobs) y los microservicios que necesitan hablar con nuestros proveedores de nube.
Históricamente, hemos gestionado esto creando “cuentas de servicio” y generando claves de API de larga duración o archivos secretos JSON. Luego tomamos estos secretos y los metemos en secretos de GitHub, variables de GitLab o Jenkins.
Esto es un desastre de seguridad esperando a ocurrir. Los secretos se filtran. No se rotan. A menudo tienen demasiados privilegios.
El cambio hacia la Identidad Federada
Las plataformas modernas se están alejando de los secretos estáticos en favor de la Identidad Federada (como Azure/GCP Workload Identity o la autenticación basada en AWS OIDC).
En lugar de una clave secreta, el proveedor de la nube (AWS/Azure/GCP) está configurado para confiar en un Proveedor de Identidad externo (como GitHub Actions, GitLab o un clúster de Kubernetes). Cuando un runner de CI/CD o una aplicación necesita autenticarse, solicita un token de corta duración a su propio IdP. Luego intercambia este token con el proveedor de la nube por un conjunto temporal de credenciales limitadas a su tarea específica.
sequenceDiagram
participant App as Aplicación / Runner
participant IdP as IdP Externo (GitHub/K8s)
participant Cloud as Proveedor de Nube (AWS/Azure/GCP)
participant Resource as Recurso de Nube
App->>IdP: Solicitar Token OIDC/Workload
IdP-->>App: Token JWT Firmado
App->>Cloud: Intercambiar JWT por Credenciales Efímeras
Cloud->>Cloud: Verificar Firma del IdP
Cloud-->>App: Token de Acceso de Corta Duración / Sesión de Rol
App->>Resource: Acceder al Recurso (SDK/API)
Al utilizar identidad federada, eliminamos la necesidad de almacenar, rotar o proteger secretos de nube de larga duración. Este enfoque no es solo para el equipo de plataforma; es nuestra recomendación principal para los desarrolladores que crean aplicaciones. Ya sea que utilicen el SDK de AWS, las librías de Azure o las APIs de GCP, siempre deben confiar en identidades federadas (como Workload Identity) en lugar de gestionar sus propios tokens. La plataforma proporciona la identidad; el SDK se encarga del intercambio. Si una carga de trabajo se ve comprometida, el atacante solo tiene un token que expira en minutos, no una clave que funciona para siempre.
Identidad en Kubernetes
Aunque el sistema de coordenadas rige nuestros permisos a nivel de nube, muchos desarrolladores pasan su día a día dentro de un clúster de Kubernetes. Los principios de roles estandarizados y acceso JIT se aplican aquí con la misma fuerza, pero los primitivos cambian.
Mapeo de roles a RBAC
En Kubernetes, el límite principal para un tenant es el Namespace (hablaremos más de esto en el siguiente capítulo). Mapeamos nuestros roles de plataforma estándar a los recursos ClusterRole integrados de Kubernetes:
| Rol de Plataforma | Espacio | ClusterRole de K8s | Alcance de K8s |
|---|---|---|---|
Role("reader") | Core | view | Clúster |
Role("contributor") | Core | edit | Clúster |
Role("admin") | Core | admin | Clúster |
Role("operator") | Core | admin | Clúster |
Role("reader") | Tenant | view | Namespace |
Role("contributor") | Tenant | edit | Namespace |
Role("admin") | Tenant | admin | Namespace |
Role("operator") | Tenant | admin | Namespace |
La automatización de la plataforma se encarga de la creación de los objetos RoleBinding dentro del namespace de cada tenant, vinculando los grupos gestionados en la nube con estos roles. Para permisos más granulares (p. ej., permitir que un servicio específico lea objetos Secret pero no los modifique), el equipo de plataforma puede definir recursos Role y RoleBinding personalizados, pero la tríada view/edit/admin cubre la mayoría de los casos de uso humano.
A lo largo de este libro, asumimos que tus clústeres de Kubernetes están integrados con el sistema IAM de tu proveedor de nube (p. ej., EKS con AWS IAM, AKS con Entra ID o GKE con Google IAM). Esta integración nos permite utilizar las mismas identidades y grupos tanto para los recursos de la nube como para los de Kubernetes, proporcionando un registro de auditoría unificado y una única fuente de verdad para la pertenencia.
Si estás ejecutando Kubernetes on-premises o en un entorno “bare-metal” sin esta integración nativa, la gestión de identidades humanas se vuelve significativamente más compleja. Es posible que necesites gestionar objetos User y Group directamente o configurar un proveedor de OIDC independiente. Aunque los detalles de implementación de esas configuraciones están fuera del alcance de este libro, los principios subyecentes — mínimo privilegio, acceso JIT y roles estandarizados — siguen siendo la base de una plataforma segura independientemente de dónde resida el clúster.
Implementación
Para gestionar esto a escala, necesitamos una única fuente de verdad para la pertenencia. No queremos andar haciendo clics manualmente en la consola de Entra ID o de AWS para añadir personas a los grupos. En su lugar, definimos nuestros tenants y sus miembros como código.
Prefiero un enfoque de IAM centrado en las personas (People-First IAM). Definimos los miembros una vez a nivel de tenant, y la automatización de nuestra plataforma se encarga del complejo mapeo a Sector, Tier y roles.
# tenants/payments.yaml
name: payments
sectors:
- ecommerce
- platform
# Los miembros humanos de este tenant
members:
admins:
- marta@mountainlab.io
contributors:
- javi@mountainlab.io
- ana@mountainlab.io
readers:
- pedro@mountainlab.io
Cuando este archivo se confirma en nuestro repositorio central de tenants, un Operador de Plataforma (una pipeline) se ejecuta y realiza las siguientes acciones:
- Sincronización de grupos: Verifica que los grupos nativos de la nube (p. ej.,
Group("payments", "sandbox", "contributor"),Group("payments", "live", "reader")) existan en el IdP. - Sincronización de pertenencia: Añade a los individuos listados a estos grupos.
- Asignación de roles: Crea las asignaciones de roles en las cuentas de nube que mapean estos grupos al
Rolecorrecto (p. ej., mapearGroup("payments", "live", "reader")alRole("reader")en la suscripción("ecommerce", "live")). - Configuración de JIT: Configura las reglas de escalada (p. ej., configurar Azure PIM para que solo los miembros de la lista de
contributorspuedan activar elRole("contributor")).
Al gestionar el IAM a través de estos sencillos archivos YAML, proporcionamos a los desarrolladores una interfaz de autoservicio. Si Tenant("payments") contrata a un nuevo ingeniero, no abren un ticket. Abren una Pull Request en su archivo tenant.yaml. El equipo de plataforma revisa la PR (asegurándose de que la nueva persona pertenece realmente a ese equipo) y, una vez fusionada, la infraestructura se actualiza automáticamente.
tenant.yaml (p. ej., añadir group-all-devs a la lista de contributors). Aunque es tentador por conveniencia, oscurece el registro de auditoría y hace imposible implementar el mínimo privilegio de forma efectiva. Enumera siempre a los individuos. Deja que la automatización de la plataforma haga el trabajo de agruparlos.Resumen
- Identidad vs. Acceso: Deja que IT gestione el “Quién” (el directorio de usuarios); el equipo de plataforma gestiona el “Qué” (los permisos de infraestructura).
- Estandarizar en Roles: Utiliza una taxonomía sencilla de
Role("reader"),Role("contributor"),Role("admin")yRole("operator")para reducir la carga cognitiva. - Roles vs. Grupos: Mantén una separación estricta entre las capacidades de un
Roley el alcance de membresía de unGroup(Tenant, Tier, Role). - Reader por defecto: Elimina los privilegios de escritura permanentes. Utiliza la escalada Just-In-Time (JIT) para unirse a un grupo
contributoroadmin. - Federa tus cargas de trabajo: Deja de usar claves de API estáticas para CI/CD. Utiliza identidad federada (como Azure Workload Identity o AWS OIDC) para un acceso de corta duración y probado criptográficamente.
- IAM como código: Utiliza un
tenant.yamlcentral como fuente de verdad para la pertenencia. Permite que los equipos gestionen su propio acceso a través de pull requests y sincronización automatizada.
Al mover el IAM de un proceso manual basado en tickets a una capacidad impulsada por la plataforma, no solo hacemos que el sistema sea más seguro, sino que lo hacemos más rápido. Damos a los desarrolladores el poder que necesitan para hacer su trabajo, pero envolvemos ese poder en una red de seguridad de acceso efímero y gobernanza automatizada.
Skills de este capítulo
define-core-iam — Una skill de IA que define los grupos y roles de IAM que el equipo de plataforma necesita para operar sus propios sectores: Role("operator"), Role("admin"), Role("contributor") y Role("reader") por tier, con políticas de escalada JIT y requisitos de identidad federada.define-tenant-iam — Una skill de IA que define los grupos de IAM y el modelo de membresía para un equipo de tenant. Produce un archivo tenant.yaml — la fuente de verdad para el aprovisionamiento de grupos y la configuración de escalada JIT que alimenta todos los skills de aprovisionamiento cloud específicos.manage-azure-iam — Una skill de IA que aprovisiona y sincroniza los recursos de IAM de Azure desde tus archivos de definición: grupos de Entra ID, asignaciones de roles a nivel de Management Group y Subscription, asignaciones elegibles de PIM y credenciales de identidad federada.manage-aws-iam — Una skill de IA que aprovisiona y sincroniza los recursos de IAM de AWS desde tus archivos de definición: permission sets de IAM Identity Center, asignaciones de cuentas, proveedores de identidad OIDC y roles de IAM con límites de permisos.manage-gcp-iam — Una skill de IA que aprovisiona y sincroniza los recursos de IAM de GCP desde tus archivos de definición: grupos de Cloud Identity, vinculaciones de IAM a nivel de Organización/Carpeta/Proyecto, pools y proveedores de identidad federada, y restricciones de Organization Policy.manage-k8s-iam — Una skill de IA que aprovisiona y sincroniza los recursos de RBAC de Kubernetes desde tus archivos de definición: objetos RoleBinding con alcance de namespace que mapean grupos de la plataforma a recursos ClusterRole integrados, y objetos ServiceAccount de operador anotados para integración con IAM cloud.Suscríbete a la Newsletter
¿Te está gustando el libro? Únete a más de 1.000 ingenieros de plataformas recibiendo artículos, reflexiones e historias de las trincheras directamente en tu bandeja de entrada.
Suscribirse gratis