Crafting Platforms' Book
Capítulo 05

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.

— Gene Spafford
Author Note
Este capítulo aún se está redactando y puede contener imprecisiones o información incompleta. Por favor, vuelve a consultarlo más tarde e informa de cualquier problema que encuentres a ezequiel+book@foncubierta.com.
Story

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 en Tier("sandbox") o la resolución urgente de problemas en Tier("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 de Tenant("payments") en Tier("live").
  • Group("payments", "live", "admin"): El grupo para los miembros de Tenant("payments") que necesitan acceso administrativo de emergencia en Tier("live").
  • Group("payments", "live", "contributor"): El grupo para los desarrolladores de Tenant("payments") que necesitan realizar resolución de problemas activa u operaciones manuales en Tier("live").
  • Group("payments", "live", "reader"): El grupo para todos en Tenant("payments") que necesiten ver el estado de su entorno Tier("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.

Tip
Delegación sobre control No intentes ser el dueño del directorio de usuarios. Alinea tu plataforma con el IdP corporativo existente. Utiliza el Inicio de Sesión Único (SSO, Single Sign-On) para todo: consolas de nube, clústeres de Kubernetes y portales internos. Si un usuario es desactivado en el IdP corporativo, su acceso a toda la plataforma debería desaparecer instantáneamente.

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

  1. Estado por defecto: Un desarrollador es miembro de Group(Tenant, Tier, "reader").
  2. La solicitud: El desarrollador necesita corregir un error. Solicita unirse a Group(Tenant, Tier, "contributor") a través de una CLI o un portal.
  3. 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.
  4. La activación/aprobación: La plataforma verifica la pertenencia. Para Role("contributor") en Tier("sandbox"), esto puede ser automático. Para Role("admin") o Tier("live"), puede requerir la aprobación de un compañero, añadiendo un segundo par de ojos a las operaciones de alto riesgo.
  5. 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.
  6. 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.

Note
Herramientas JIT nativas No es necesario construir un sistema JIT desde cero. Azure tiene Privileged Identity Management (PIM) integrado directamente en Entra ID. AWS ofrece IAM Identity Center (antes AWS SSO), que admite conjuntos de permisos basados en sesiones. Para entornos independientes de la nube, herramientas como Teleport u Okta Advanced Server Access proporcionan una escalada similar limitada en el tiempo para SSH, Kubernetes y bases de datos.

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 PlataformaEspacioClusterRole de K8sAlcance de K8s
Role("reader")CoreviewClúster
Role("contributor")CoreeditClúster
Role("admin")CoreadminClúster
Role("operator")CoreadminClúster
Role("reader")TenantviewNamespace
Role("contributor")TenanteditNamespace
Role("admin")TenantadminNamespace
Role("operator")TenantadminNamespace

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:

  1. 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.
  2. Sincronización de pertenencia: Añade a los individuos listados a estos grupos.
  3. Asignación de roles: Crea las asignaciones de roles en las cuentas de nube que mapean estos grupos al Role correcto (p. ej., mapear Group("payments", "live", "reader") al Role("reader") en la suscripción ("ecommerce", "live")).
  4. Configuración de JIT: Configura las reglas de escalada (p. ej., configurar Azure PIM para que solo los miembros de la lista de contributors puedan activar el Role("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.

Warning
Membresía siempre basada en individuos Evita añadir grupos dentro de tu 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") y Role("operator") para reducir la carga cognitiva.
  • Roles vs. Grupos: Mantén una separación estricta entre las capacidades de un Role y el alcance de membresía de un Group(Tenant, Tier, Role).
  • Reader por defecto: Elimina los privilegios de escritura permanentes. Utiliza la escalada Just-In-Time (JIT) para unirse a un grupo contributor o admin.
  • 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.yaml central 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

AI Skill
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.
AI Skill
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.
AI Skill
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.
AI Skill
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.
AI Skill
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.
AI Skill
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