<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>IA on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/ia/</link><description>Recent content in IA on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Thu, 18 Jun 2026 11:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/ia/index.xml" rel="self" type="application/rss+xml"/><item><title>Cómo usamos nuestra infraestructura para cumplir las normas de defensa con IA</title><link>https://blog.lo0.es/posts/infra-cumplimiento-normativo-defensa-ia/</link><pubDate>Thu, 18 Jun 2026 11:00:00 +0200</pubDate><guid>https://blog.lo0.es/posts/infra-cumplimiento-normativo-defensa-ia/</guid><description>&lt;p>En el ámbito de defensa, el reto de poner IA en operación no es el modelo: es la seguridad y el cumplimiento. Las normas que aplican —los estándares de interoperabilidad aliada, las reglas de seguridad centrada en el dato, los marcos de acreditación y los principios de uso responsable— imponen requisitos concretos de control de acceso, clasificación, confidencialidad, trazabilidad, cadena de suministro y soberanía. Una demostración en un portátil conectado a internet ignora todo eso; una plataforma operable tiene que satisfacerlo, y tiene que poder demostrarlo ante una acreditación.&lt;/p>
&lt;p>Este artículo no es un recorrido por tecnologías. Es lo contrario: tomamos los requisitos que imponen las normas del sector y, para cada uno, mostramos cómo se usa nuestra infraestructura para cumplirlo. La infraestructura es la misma plataforma real que usamos en otros sectores —dos clústeres RKE2 (Kubernetes v1.35) con Cilium como red, GitOps con Flux, Keycloak como servidor de identidad, inferencia en GPU propias y, en preproducción, scheduling con Volcano— adaptada a un entorno con requisitos de seguridad más severos. Cada componente está aquí porque resuelve un requisito normativo, no porque quede bien en un diagrama. Y, como toda infraestructura, se mide por eficiencia y coste, lo que hace el cumplimiento sostenible.&lt;/p>
&lt;p>Conviene empezar por una idea que mucha gente desconoce: para esto &lt;strong>ya existen estándares del sector&lt;/strong>, y no hay que inventarse nada. Hay estándares de interoperabilidad aliada, hay un modelo de referencia DevSecOps sobre Kubernetes específico de defensa, hay un marco de seguridad (Zero Trust) y de acreditación de proveedores, y hay un cuerpo de principios para el uso responsable de la IA militar. Vamos primero a situar ese mapa, y después a recorrer los requisitos uno a uno.&lt;/p>
&lt;h2 id="el-mapa-de-estándares-del-sector">El mapa de estándares del sector&lt;/h2>
&lt;p>Cuatro familias de estándares se cruzan en una plataforma de IA de defensa, y entender qué cubre cada una evita reinventar ruedas.&lt;/p>
&lt;p>&lt;strong>Interoperabilidad aliada.&lt;/strong> En coalición, los sistemas tienen que entenderse. La &lt;strong>Federated Mission Networking (FMN)&lt;/strong> define cómo se federan redes de misión entre naciones por &amp;ldquo;spirals&amp;rdquo; sucesivos; el &lt;strong>MIP (Multilateral Interoperability Programme)&lt;/strong> y su modelo de datos &lt;strong>JC3IEDM&lt;/strong> estandarizan el mando y control; &lt;strong>Link-16&lt;/strong> y formatos como &lt;strong>NVG (NATO Vector Graphics)&lt;/strong> cubren el intercambio táctico y de situación. Son el equivalente, en defensa, a lo que FHIR y DICOM son en sanidad: el idioma común sin el cual la IA es un sistema aislado que no puede integrarse en la operación.&lt;/p>
&lt;p>&lt;strong>Seguridad centrada en el dato: STANAG 4774 y 4778.&lt;/strong> Esta es la pieza más característica del sector. &lt;strong>STANAG 4774&lt;/strong> define la sintaxis de una &lt;strong>etiqueta de confidencialidad&lt;/strong> (política, clasificación, categorías de seguridad), y &lt;strong>STANAG 4778&lt;/strong> define el &lt;strong>mecanismo de enlace&lt;/strong> (binding) que ata esa etiqueta al dato a lo largo de todo su ciclo de vida y entre las partes que lo comparten. En lugar de proteger solo el perímetro o el sistema, se protege el propio objeto de información: la clasificación viaja con el dato y la decisión de acceso se toma contra la etiqueta. Ambos STANAG están en proceso de ratificación por las naciones y avanzan de práctica recomendada a requisito para software de misión crítica.&lt;/p>
&lt;p>&lt;strong>DevSecOps de defensa sobre Kubernetes.&lt;/strong> Para construir y desplegar software de defensa existe un modelo de referencia muy concreto: el &lt;strong>DoD Enterprise DevSecOps Reference Design&lt;/strong> sobre &lt;strong>Kubernetes&lt;/strong>, materializado en plataformas como &lt;strong>Platform One&lt;/strong> y su línea de productos &lt;strong>Big Bang&lt;/strong> (entrega continua declarativa de paquetes endurecidos sobre un clúster Kubernetes), apoyado en &lt;strong>Iron Bank&lt;/strong>, el repositorio de &lt;strong>imágenes de contenedor endurecidas y conformes con los STIG de DISA&lt;/strong>, escaneadas de forma continua. La filosofía asociada es el &lt;strong>cATO (Continuous Authority To Operate)&lt;/strong>: la acreditación deja de ser un hito puntual y pasa a ser un estado que se mantiene con monitorización continua. Es, para defensa, el equivalente al estándar Kubernetes-native que en imagen médica representa MONAI: una forma acordada de empaquetar y operar software con garantías sobre el sustrato que ya usamos.&lt;/p>
&lt;p>&lt;strong>Marcos de seguridad y acreditación.&lt;/strong> El &lt;strong>DoD Zero Trust Reference Architecture&lt;/strong> (v2.0) fija el principio rector —no confiar por defecto, verificar siempre— con metas de adopción hacia 2027. La acreditación de proveedores se rige por &lt;strong>CMMC 2.0&lt;/strong> (cuya Fase 2, con certificación obligatoria por tercero para el nivel 2, llega en noviembre de 2026) sobre los 110 controles de &lt;strong>NIST SP 800-171&lt;/strong>. En España y la UE, el &lt;strong>Esquema Nacional de Seguridad&lt;/strong> en categoría alta y las guías &lt;strong>CCN-STIC&lt;/strong> del Centro Criptológico Nacional cumplen un papel equivalente para los sistemas que manejan información sensible o clasificada.&lt;/p>
&lt;p>&lt;strong>IA responsable en defensa.&lt;/strong> La OTAN adoptó seis &lt;strong>Principios de Uso Responsable (PRU)&lt;/strong> de la IA en defensa —legalidad, responsabilidad y rendición de cuentas, explicabilidad y trazabilidad, fiabilidad, gobernabilidad y mitigación de sesgos—, reforzados por su Estrategia de IA revisada en 2024 y por el &lt;strong>DARB (Data and AI Review Board)&lt;/strong>. Un matiz jurídico importante: el &lt;strong>AI Act de la UE excluye expresamente&lt;/strong> (art. 2.3) los sistemas de IA destinados en exclusiva a fines militares, de defensa o de seguridad nacional. Es decir, en defensa el marco de IA responsable no es el AI Act, sino los PRU de la OTAN, las reglas nacionales y, como sistema de gestión, la ISO/IEC 42001.&lt;/p>
&lt;p>Con este mapa sobre la mesa, recorramos los requisitos.&lt;/p>
&lt;h2 id="requisito-interoperabilidad-en-coalición-fmn-mipjc3iedm">Requisito: interoperabilidad en coalición (FMN, MIP/JC3IEDM)&lt;/h2>
&lt;p>En una operación aliada, un sistema que no se integra es inútil por bueno que sea. El requisito, derivado de FMN, del modelo de datos de mando y control (MIP/JC3IEDM) y de los formatos tácticos (Link-16, NVG), es que la IA consuma y produzca información en los formatos comunes de la coalición, no en un dialecto propio.&lt;/p>
&lt;p>Cómo lo cumplimos: igual que en otros sectores ponemos una capa de interoperabilidad como frontera, aquí la plataforma se integra a través de los &lt;em>gateways&lt;/em> y formatos acordados, de modo que la IA nunca habla directamente con los sistemas de misión, sino que consume y entrega información ya normalizada al estándar de la coalición. Esto mantiene a la IA como un componente que se acopla a la arquitectura federada —respetando sus perfiles y su gobierno del dato— en lugar de un silo que obliga a integraciones a medida en cada despliegue. La interoperabilidad es, además, condición para la trazabilidad y el etiquetado: solo si el dato entra y sale en un formato conocido se le puede adjuntar y preservar su etiqueta de confidencialidad.&lt;/p>
&lt;h2 id="requisito-no-confiar-por-defecto--zero-trust-y-acceso-verificado-dod-zt-ra-nist-800-171">Requisito: no confiar por defecto — Zero Trust y acceso verificado (DoD ZT RA, NIST 800-171)&lt;/h2>
&lt;p>El principio rector de la seguridad de defensa moderna es el Zero Trust: ninguna entidad —usuario, servicio, nodo— es de confianza por su posición en la red; cada acceso se autentica, se autoriza y se verifica de forma continua. NIST 800-171 detalla los controles de acceso e identificación que lo sustentan.&lt;/p>
&lt;p>Cómo lo cumplimos: un único servidor de identidad, &lt;strong>Keycloak&lt;/strong>, gobierna la autorización con OAuth 2.1 y OpenID Connect, emitiendo tokens de vida corta y atándolos a su destinatario con la validación de audiencia (&lt;code>aud&lt;/code>), de modo que una credencial emitida para un servicio no sirve contra otro. Entre componentes que tratan información sensible exigimos &lt;strong>TLS mutuo (mTLS)&lt;/strong>: no solo se autentica al usuario o al servicio, sino a la propia carga que participa en el intercambio. Y la red opera en &lt;strong>default-deny&lt;/strong> (siguiente sección), de forma que la conectividad no es un derecho por estar dentro del clúster, sino un permiso explícito. Las cargas de trabajo usan &lt;em>service accounts&lt;/em> sin permisos por defecto, y el acceso de los administradores a la API de Kubernetes se federa contra Keycloak por OIDC con roles acotados: también el acceso técnico queda autenticado, limitado y auditado. Zero Trust no es un producto que se compra; es una propiedad que emerge de identidad fuerte, mínimo privilegio y verificación continua, y así está montada la plataforma. La verificación, además, no se agota en el inicio de sesión: los tokens son de vida corta y se renuevan, las políticas se reevalúan en cada petición, y la postura de cada carga —imagen firmada, política de seguridad aplicada— condiciona si puede ejecutarse y comunicarse. Un componente que deja de cumplir su política deja de poder operar; no se le concede un margen por haber estado dentro.&lt;/p>
&lt;h2 id="requisito-seguridad-centrada-en-el-dato-y-etiquetado-stanag-47744778">Requisito: seguridad centrada en el dato y etiquetado (STANAG 4774/4778)&lt;/h2>
&lt;p>A diferencia de otros sectores, en defensa la clasificación viaja con el dato. El requisito, derivado de STANAG 4774/4778, es que cada objeto de información lleve su etiqueta de confidencialidad y que las decisiones de acceso se tomen contra esa etiqueta, no contra el sistema que la aloja.&lt;/p>
&lt;p>Cómo lo cumplimos: las decisiones de autorización no se limitan a roles, sino que incorporan &lt;strong>control de acceso basado en atributos (ABAC)&lt;/strong> evaluando la etiqueta de clasificación del dato frente a la habilitación del solicitante. La política se expresa como código y se evalúa en un punto de decisión central (un motor de políticas tipo OPA/Open Policy Agent) que la capa de aplicación y los &lt;em>gateways&lt;/em> consultan antes de servir un recurso. La etiqueta, conforme a STANAG 4774, acompaña al objeto, y el enlace conforme a STANAG 4778 se preserva a través de la plataforma —en los metadatos del objeto en almacenamiento y en las anotaciones que viajan con la petición—. Para un agente de IA, esto significa que su token le da acceso a un nivel de clasificación, y que un dato por encima de ese nivel simplemente no se le entrega, con independencia de dónde esté almacenado.&lt;/p>
&lt;p>Este enfoque encarna la regla clásica de no-lectura-hacia-arriba: una entidad con una habilitación dada no accede a información por encima de su nivel, y la decisión se toma sobre la etiqueta del objeto, no sobre suposiciones del sistema. Cuando un flujo requiere mover información a un nivel inferior —una desclasificación o sanitización—, es un paso explícito y auditado, con su propia política, nunca un efecto colateral de una consulta. La etiqueta se preserva en los metadatos del objeto en almacenamiento y en las anotaciones que acompañan a la petición, de modo que el enlace de STANAG 4778 no se pierde al cruzar los componentes de la plataforma.&lt;/p>
&lt;h2 id="requisito-segregación-por-misión-y-nivel-multitenancy-nist-800-171-ens">Requisito: segregación por misión y nivel (multitenancy, NIST 800-171, ENS)&lt;/h2>
&lt;p>Distintas misiones, distintos niveles de clasificación y distintos compartimentos no pueden compartir un espacio común sin controles. El requisito: aislamiento fuerte, de modo que lo de una misión o nivel no sea accesible desde otro.&lt;/p>
&lt;p>Cómo lo cumplimos: cada misión o compartimento vive en su propio &lt;strong>namespace&lt;/strong> de Kubernetes, con &lt;strong>RBAC&lt;/strong> acotado, &lt;strong>ResourceQuota&lt;/strong> y &lt;strong>LimitRange&lt;/strong>, almacenamiento y bases de datos dedicadas por operador —no compartidas con un filtro de aplicación— y su propia segmentación de red. La identidad se segrega por realm en Keycloak. Toda la definición vive en Git y la reconcilia &lt;strong>Flux&lt;/strong> (GitOps), de modo que la segregación es declarativa y auditable por historial de cambios. Donde el nivel de clasificación lo exige, la separación trasciende el clúster: se opera sobre clústeres o emplazamientos físicamente distintos, y el intercambio entre dominios de clasificación, cuando es necesario, se hace mediante soluciones de cruce de dominio (cross-domain) controladas, no abriendo un agujero entre namespaces. En los niveles más altos, cada dominio de seguridad puede ser un clúster independiente con su propia cadena de confianza, sus propias identidades y su propio gobierno, evitando que un único plano de control abarque clasificaciones que nunca deben mezclarse. La elección entre namespace, clúster o emplazamiento separado es, en sí misma, una decisión de acreditación: se documenta, se justifica según el nivel y el compartimento, y queda reflejada de forma declarativa en el repositorio.&lt;/p>
&lt;h2 id="requisito-confidencialidad-cifrado-y-prevención-de-exfiltración-nist-800-171-ens-zero-trust">Requisito: confidencialidad, cifrado y prevención de exfiltración (NIST 800-171, ENS, Zero Trust)&lt;/h2>
&lt;p>NIST 800-171 (familias de protección del sistema y las comunicaciones) y el ENS exigen confidencialidad, cifrado de las comunicaciones y prevención de la fuga de información. En defensa, la exfiltración no es un riesgo abstracto: es el objetivo del adversario.&lt;/p>
&lt;p>Cómo lo cumplimos: la red la gestiona &lt;strong>Cilium&lt;/strong> (eBPF) con modelo &lt;strong>default-deny&lt;/strong>. Un pod no tiene conectividad salvo la que una política le concede explícitamente, flujo a flujo, lo que convierte la exfiltración en algo que se bloquea en el kernel. Un componente solo puede hablar con los destinos estrictamente necesarios:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cilium.io/v2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">CiliumNetworkPolicy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">inferencia-egress-minimo&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">mision-alfa&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">endpointSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">serving-inferencia&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">egressDeny&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">toEntities&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">world] &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># ninguna salida fuera del clúster&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">egress&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">toEndpoints&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gateway-modelos&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">toEndpoints&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># DNS interno, sin esto no resuelve&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">k8s:io.kubernetes.pod.namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kube-system&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">k8s-app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kube-dns&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">toPorts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>{&lt;span class="w"> &lt;/span>&lt;span class="nt">port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;53&amp;#34;&lt;/span>&lt;span class="nt">, protocol&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">UDP }]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dos matices que separan una política correcta de una permeable: hay que permitir explícitamente el &lt;strong>DNS&lt;/strong> y filtrar por &lt;strong>identidad de endpoint&lt;/strong>, no por CIDR, porque en una red basada en identidad el filtrado por rangos de IP no se comporta como la intuición sugiere. Sobre esto añadimos observabilidad de flujos con &lt;strong>Hubble&lt;/strong> y seguridad en runtime con &lt;strong>Tetragon&lt;/strong> (eBPF), que vigila ejecución de procesos, acceso a ficheros y conexiones &lt;strong>dentro&lt;/strong> del contenedor, permitiendo detectar y bloquear comportamiento anómalo —un proceso inesperado en una carga sensible es justo la señal que se quiere ver en tiempo real—. El cifrado en tránsito lo cubren cert-manager (TLS en los bordes) y el cifrado transparente entre nodos de Cilium. A esto se suman los &lt;strong>Pod Security Standards&lt;/strong> en perfil restringido (sin privilegios, sin root) y una gestión disciplinada de secretos y claves, que no viven en manifiestos planos.&lt;/p>
&lt;h2 id="requisito-cadena-de-suministro-de-software-verificable-iron-bankstig-sbom-cmmc">Requisito: cadena de suministro de software verificable (Iron Bank/STIG, SBOM, CMMC)&lt;/h2>
&lt;p>La cadena de suministro de software es un vector de ataque de primer orden, y el sector lo trata como tal: imágenes endurecidas conforme a los STIG, inventario de componentes (SBOM) y verificación criptográfica de la procedencia. CMMC y NIST 800-171 lo exigen en el flujo de proveedores; la Orden Ejecutiva 14028 popularizó el SBOM.&lt;/p>
&lt;p>Cómo lo cumplimos: partimos de &lt;strong>imágenes base endurecidas&lt;/strong> (en la línea de Iron Bank y los STIG de DISA), generamos un &lt;strong>SBOM&lt;/strong> (CycloneDX/SPDX) por artefacto y &lt;strong>firmamos imágenes y SBOM con Sigstore/Cosign&lt;/strong> —proyecto graduado de la CNCF, con firma sin clave gestionada y registro de transparencia—. En el clúster, un &lt;strong>control de admisión&lt;/strong> (Kyverno/políticas) &lt;strong>rechaza cualquier imagen que no esté firmada&lt;/strong> por una identidad de confianza, de modo que no se ejecuta software cuya procedencia no se pueda verificar. La regla se expresa como código y verifica la firma de cada imagen antes de permitir su ejecución:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kyverno.io/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ClusterPolicy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">exigir-imagenes-firmadas&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">validationFailureAction&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Enforce&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">rules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">verificar-firma-cosign&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">match&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">any&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">kinds&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">Pod]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">verifyImages&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">imageReferences&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;registro.interno/*&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">attestors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">entries&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">keys&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">publicKeys&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|-&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -----BEGIN PUBLIC KEY-----
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> ...clave de confianza...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -----END PUBLIC KEY-----&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Una imagen sin firma válida es rechazada en admisión, antes de ejecutarse. Como todo el despliegue es GitOps, la cadena —del commit a la imagen firmada a la política que la admite— es trazable de extremo a extremo. Esto satisface el control de cadena de suministro de NIST 800-171 y encaja con el modelo cATO: la verificación es continua, no un sello de una sola vez.&lt;/p>
&lt;h2 id="requisito-soberanía-y-operación-desconectada-o-air-gapped-redes-clasificadas-ddil">Requisito: soberanía y operación desconectada o air-gapped (redes clasificadas, DDIL)&lt;/h2>
&lt;p>Las redes de defensa operan frecuentemente aisladas de internet (air-gapped) o en condiciones DDIL —conectividad degradada, intermitente o de bajo ancho de banda en el borde táctico—. Y el dato no puede salir a una API externa bajo ninguna circunstancia. El requisito: la plataforma tiene que funcionar sin internet y sin sacar el dato fuera.&lt;/p>
&lt;p>Cómo lo cumplimos: la inferencia se sirve &lt;strong>en local&lt;/strong>, sobre GPU propias, con un motor de serving de alto rendimiento (vLLM) y &lt;strong>modelos de pesos abiertos&lt;/strong> que se pueden alojar íntegramente dentro del perímetro. Todo lo que la plataforma necesita —imágenes de contenedor, modelos, dependencias— se replica en &lt;strong>registros y repositorios internos&lt;/strong>, de modo que el despliegue GitOps reconcilia desde fuentes internas y no requiere acceso a internet. Una &lt;strong>pasarela de modelos&lt;/strong> centraliza el acceso e invierte la carga: el comportamiento por defecto es la inferencia local, y cualquier salida es una excepción que, además de tener que habilitarse explícitamente, chocaría con las políticas de red default-deny. La soberanía no se sostiene en una promesa, sino en varios controles que se refuerzan —pasarela, política de red y aislamiento—, de modo que un descuido de configuración no se traduce en una fuga.&lt;/p>
&lt;p>Un punto que en defensa no se puede pasar por alto: ningún componente debe &amp;ldquo;llamar a casa&amp;rdquo;. Muchas herramientas envían telemetría, comprueban licencias o descargan actualizaciones contra servidores externos por defecto, y eso, en una red clasificada, es inadmisible. Por eso el default-deny no es solo una medida frente al adversario, sino también frente al propio software: si una imagen intenta una conexión de telemetría no declarada, la política la bloquea y Tetragon la registra. La ausencia de &amp;ldquo;phone-home&amp;rdquo; se verifica, no se confía.&lt;/p>
&lt;h2 id="requisito-resiliencia-y-operación-en-el-borde-táctico-ddil">Requisito: resiliencia y operación en el borde táctico (DDIL)&lt;/h2>
&lt;p>Mucha operación de defensa ocurre en condiciones DDIL —conectividad degradada, intermitente o de bajo ancho de banda—, e incluso en aislamiento total. El requisito: la plataforma debe seguir siendo útil cuando el enlace con la retaguardia se cae, y reconciliarse cuando vuelve.&lt;/p>
&lt;p>Cómo lo cumplimos: el modelo declarativo de GitOps encaja de forma natural con la operación desconectada. Un emplazamiento en el borde mantiene su propia copia del estado deseado y reconcilia contra registros y repositorios locales, de modo que opera sin depender de una conexión permanente; cuando el enlace vuelve, sincroniza. La inferencia se sirve localmente con modelos ya alojados en el nodo, sin llamadas externas. Las trazas y los eventos de auditoría se almacenan en local y se reenvían (store-and-forward) cuando hay enlace, sin perder el registro. El plano de control de alta disponibilidad y la separación en emplazamientos permiten degradar de forma controlada en vez de caer en bloque. La resiliencia no es un añadido: es una propiedad del diseño declarativo y de la inferencia soberana.&lt;/p>
&lt;h2 id="requisito-robustez-y-protección-del-propio-sistema-de-ia-pru-fiabilidad">Requisito: robustez y protección del propio sistema de IA (PRU fiabilidad)&lt;/h2>
&lt;p>El principio de &lt;strong>fiabilidad&lt;/strong> de la OTAN exige que los sistemas de IA tengan capacidades explícitas y bien probadas, y que sean seguros frente a manipulación. Esto añade un requisito que otros sectores tratan con menos urgencia: proteger el propio modelo —sus pesos y su comportamiento— como un activo sensible.&lt;/p>
&lt;p>Cómo lo cumplimos: los pesos de los modelos se tratan como dato sensible, alojados dentro del perímetro y con su acceso controlado por las mismas políticas de identidad y red que el resto. La pasarela de modelos es el punto donde se aplican salvaguardas de entrada y salida (validación, límites, filtrado) y donde se puede acotar o desconectar un modelo. La cadena de suministro firmada (SBOM y Cosign) se extiende a los artefactos de modelo, de modo que se ejecuta solo lo que tiene procedencia verificable. Y la trazabilidad alimenta la evaluación continua: medir el comportamiento del modelo a lo largo del tiempo es lo que permite detectar degradaciones o manipulaciones antes de que afecten a la operación. La robustez se construye con los mismos controles —identidad, red, cadena de suministro, trazas— aplicados ahora también al modelo. A esto se añaden salvaguardas frente a entradas adversarias —inyección de instrucciones, datos manipulados— en la pasarela, que valida y acota lo que entra y sale del modelo. En defensa, asumir que el adversario intentará manipular la IA no es pesimismo: es el supuesto de partida del diseño.&lt;/p>
&lt;h2 id="requisito-ejecución-fiable-de-cargas-de-misión-y-eficiencia-disponibilidad-sostenibilidad">Requisito: ejecución fiable de cargas de misión y eficiencia (disponibilidad, sostenibilidad)&lt;/h2>
&lt;p>Servir IA en casa solo es sostenible si la GPU —recurso escaso y caro— se aprovecha, y ciertas cargas de misión deben ejecutarse de forma fiable y completa. El planificador por defecto de Kubernetes programa pod a pod, lo que para cargas distribuidas produce trabajos a medias que retienen GPU sin avanzar.&lt;/p>
&lt;p>Cómo lo cumplimos: el reparto de GPU lo gobierna &lt;strong>Volcano&lt;/strong> (proyecto incubado en la CNCF), hoy en &lt;strong>preproducción&lt;/strong>, con &lt;strong>gang scheduling&lt;/strong> (&lt;code>PodGroup&lt;/code> con &lt;code>minAvailable&lt;/code>: el trabajo arranca completo o no arranca), &lt;strong>colas por misión&lt;/strong> con cuota y prioridad, &lt;strong>fair-share (DRF)&lt;/strong> para repartir con justicia, &lt;strong>preempción&lt;/strong> para que una carga urgente adelante a una diferible y &lt;strong>conciencia de topología&lt;/strong> (NVLink/PCIe) para reducir la fragmentación. Esto eleva la utilización efectiva del parque de GPU, que es lo que hace viable la inferencia soberana. Sobre el estado del arte: Kubernetes 1.34 llevó &lt;strong>DRA (Dynamic Resource Allocation)&lt;/strong> a disponibilidad general —asignación de aceleradores por atributos en lugar de por conteo—, y &lt;strong>Kueue&lt;/strong> cubre la gestión de cuotas de jobs; ambos se combinan con un scheduler de gang. Se gestiona con métricas: utilización efectiva frente a asignada, fragmentación, rendimiento por GPU y horas-GPU por misión. En el borde, donde la GPU es aún más escasa, estas mismas técnicas —junto con modelos cuantizados y de menor tamaño— son las que permiten servir inferencia útil con el hardware disponible en vez de depender de un centro de datos que quizá no esté alcanzable.&lt;/p>
&lt;h2 id="requisito-explicabilidad-trazabilidad-y-gobernabilidad-pru-de-la-otan-nist-au">Requisito: explicabilidad, trazabilidad y gobernabilidad (PRU de la OTAN, NIST AU)&lt;/h2>
&lt;p>Tres de los seis Principios de Uso Responsable de la OTAN tocan directamente a la infraestructura: &lt;strong>explicabilidad y trazabilidad&lt;/strong>, &lt;strong>gobernabilidad&lt;/strong> y &lt;strong>responsabilidad/rendición de cuentas&lt;/strong>. Más los controles de auditoría (AU) de NIST. El requisito: poder reconstruir cada decisión de la IA, mantener el control humano y rendir cuentas.&lt;/p>
&lt;p>Cómo lo cumplimos: cada inferencia se registra como una &lt;strong>traza auditable&lt;/strong> con Langfuse —entrada, salida, modelo y versión, coste, latencia y misión—, almacenada en componentes propios encerrados tras políticas de red que impiden su salida. Esa traza sirve la trazabilidad que exigen los PRU y los controles AU de NIST, y es la base de la supervisión humana, que se ejerce en la &lt;strong>pasarela&lt;/strong> (revisión, límites y, crucialmente, la posibilidad de intervención y desconexión que pide el principio de &lt;strong>gobernabilidad&lt;/strong>: un sistema de IA en defensa debe poder ser desactivado o acotado por el operador). Un registro de auditoría solo vale como prueba si es íntegro y está ordenado en el tiempo: por eso los eventos se conservan en almacenamiento de solo-añadir y los relojes se mantienen sincronizados por NTP. La instrumentación converge en las convenciones semánticas GenAI de OpenTelemetry.&lt;/p>
&lt;p>La rendición de cuentas exige además poder &lt;strong>atribuir&lt;/strong> una decisión: qué modelo, qué versión, qué entrada y qué operador estaban implicados. Por eso cada traza enlaza la inferencia con la identidad que la solicitó y con la versión exacta del artefacto de modelo que respondió, de modo que la cadena &amp;ldquo;quién pidió qué, con qué sistema y con qué resultado&amp;rdquo; es reconstruible. Sin esa atribución, el principio de responsabilidad y rendición de cuentas de la OTAN queda en una declaración de intenciones.&lt;/p>
&lt;h2 id="requisito-acreditación-continua-y-operación-reproducible-cato-cmmc-ens">Requisito: acreditación continua y operación reproducible (cATO, CMMC, ENS)&lt;/h2>
&lt;p>El sector ha sustituido la acreditación de una sola vez por el &lt;strong>cATO&lt;/strong>: una autorización que se mantiene mientras se demuestre, de forma continua, que los controles siguen en su sitio. El ENS y CMMC exigen igualmente gestión de la configuración y trazabilidad de los cambios.&lt;/p>
&lt;p>Cómo lo cumplimos: todo el estado de la plataforma —despliegues, políticas de red, cuotas, RBAC, identidad, políticas de admisión— se describe en Git y lo aplica &lt;strong>Flux&lt;/strong> por GitOps. Cada cambio es una revisión con autor, fecha y revisor; cada entorno es reproducible desde su repositorio. Las políticas de seguridad son &lt;strong>código&lt;/strong> (Kyverno/OPA) que se evalúa de forma continua, no una lista que alguien revisa una vez al año, y la monitorización de cumplimiento es permanente. Esto materializa el cATO: una auditoría deja de ser una foto puntual y pasa a ser una consulta a un estado que se mantiene y se verifica sin interrupción, con capacidad de reconstruir exactamente qué estaba desplegado en una fecha dada. Un beneficio práctico es que la evidencia de cumplimiento se genera sola: el estado declarativo en Git, los registros de admisión, las firmas de imagen y las trazas son, en conjunto, el cuerpo de pruebas que una acreditación necesita, producido de forma continua en lugar de recopilado a mano la semana antes de la inspección.&lt;/p>
&lt;h2 id="la-pieza-que-lo-ata-todo-isoiec-42001-y-los-principios-de-la-otan">La pieza que lo ata todo: ISO/IEC 42001 y los principios de la OTAN&lt;/h2>
&lt;p>Todo lo anterior son controles técnicos que satisfacen requisitos concretos. Pero hace falta un sistema de gestión que los ordene, evalúe y mejore, con roles y responsabilidades definidos. En defensa, ese gobierno tiene dos referencias complementarias: los &lt;strong>Principios de Uso Responsable de la OTAN&lt;/strong>, que fijan el &amp;ldquo;qué&amp;rdquo; ético y operativo, e &lt;strong>ISO/IEC 42001&lt;/strong>, la norma certificable de sistemas de gestión de IA, que aporta el &amp;ldquo;cómo&amp;rdquo; de la gestión continua. Dado que el AI Act excluye la defensa, esta combinación —PRU + ISO 42001— es la espina dorsal de gobernanza para una IA militar responsable y demostrable. Es, precisamente, el contenido del curso de Sistemas de Gestión de IA que publicaremos en breve.&lt;/p>
&lt;h2 id="visión-del-sector-a-junio-de-2026">Visión del sector a junio de 2026&lt;/h2>
&lt;p>Vale la pena fijar la foto del momento, porque varias corrientes han convergido y condicionan cualquier decisión de plataforma.&lt;/p>
&lt;p>&lt;strong>Agenda &amp;ldquo;AI-first&amp;rdquo; y la IA en redes clasificadas.&lt;/strong> En enero de 2026, el Departamento de Defensa estadounidense fijó una agenda de IA a &amp;ldquo;velocidad de guerra&amp;rdquo;, y a lo largo de la primavera se cerraron acuerdos para desplegar modelos de IA generativa &lt;strong>en redes clasificadas&lt;/strong>. La señal es clara: la IA militar deja de ser un piloto y pasa a operación, lo que dispara la exigencia sobre la infraestructura que la sostiene.&lt;/p>
&lt;p>&lt;strong>El sustrato es Kubernetes endurecido.&lt;/strong> Platform One, Big Bang e Iron Bank consolidan el patrón: clústeres Kubernetes con imágenes conformes a STIG, cadena de suministro firmada y cATO. La IA de defensa se despliega sobre ese mismo sustrato cloud-native, no sobre sistemas a medida.&lt;/p>
&lt;p>&lt;strong>Zero Trust con fecha.&lt;/strong> Las metas de adopción de Zero Trust se sitúan hacia 2027, lo que convierte la identidad fuerte, la microsegmentación y la verificación continua en requisitos con calendario, no en aspiraciones.&lt;/p>
&lt;p>&lt;strong>La acreditación de proveedores se endurece.&lt;/strong> La Fase 2 de CMMC, en noviembre de 2026, acaba con la autoacreditación para quien maneja información controlada: hará falta certificación por un tercero sobre los 110 controles de NIST 800-171. La cadena de suministro de software —SBOM y firma con Sigstore— pasa de buena práctica a condición de contrato.&lt;/p>
&lt;p>&lt;strong>Seguridad centrada en el dato, al fin operativa.&lt;/strong> Con STANAG 4774/4778 avanzando en ratificación, el etiquetado de confidencialidad enlazado al dato se vuelve un requisito real para software de misión, y no solo una recomendación.&lt;/p>
&lt;p>&lt;strong>Gobernanza de IA específica de defensa.&lt;/strong> Los Principios de Uso Responsable de la OTAN y el DARB maduran como el marco de referencia, ocupando el espacio que el AI Act deja libre al excluir la defensa. Y la economía de la GPU, junto con la madurez de los modelos de pesos abiertos, empuja el cómputo hacia infraestructura propia, justo donde el dato clasificado y la operación desconectada lo exigen.&lt;/p>
&lt;p>&lt;strong>Europa empuja su propia autonomía.&lt;/strong> En paralelo, la UE refuerza su base industrial y tecnológica de defensa —a través del Fondo Europeo de Defensa y la Agencia Europea de Defensa—, con un énfasis creciente en la soberanía tecnológica. Para una plataforma europea, esto refuerza la apuesta por infraestructura propia, modelos alojables localmente y dependencia mínima de servicios externos: exactamente la dirección en la que está montada.&lt;/p>
&lt;p>Dónde estamos nosotros en esa foto: con la plataforma sobre Kubernetes, eBPF como sustrato de red y seguridad, cadena de suministro firmada, scheduling de GPU madurando en preproducción, inferencia soberana y desconectable por diseño, trazabilidad en formato auditable y el marco de gestión (PRU + ISO 42001) como siguiente paso. No perseguimos la moda; estamos en la línea por donde el sector ha decidido avanzar.&lt;/p>
&lt;h2 id="resumen-requisito-por-requisito">Resumen: requisito por requisito&lt;/h2>
&lt;p>Cada norma o estándar del sector impone un requisito, y cada requisito se satisface con una pieza concreta de la infraestructura. El Zero Trust y el acceso verificado, con Keycloak, mTLS y default-deny. La seguridad centrada en el dato de STANAG 4774/4778, con ABAC sobre etiquetas de clasificación. La segregación por misión y nivel, con multitenancy y, donde toca, separación física y cross-domain. La confidencialidad y la prevención de fugas, con Cilium en default-deny y Tetragon. La cadena de suministro verificable, con imágenes endurecidas STIG, SBOM y firma Sigstore más control de admisión. La soberanía y la operación desconectada, con inferencia local sobre modelos abiertos y registros internos. La ejecución fiable y la eficiencia, con Volcano. La explicabilidad, trazabilidad y gobernabilidad de los PRU de la OTAN, con trazas auditables y control humano en la pasarela. La acreditación continua (cATO) y la operación reproducible, con GitOps y políticas como código. Y la gobernanza que lo ata todo, con los PRU de la OTAN e ISO 42001.&lt;/p>
&lt;p>No es un PowerPoint: es infraestructura que opera, que cumple y que —por ser infraestructura— se mide por eficiencia y coste. De eso, del control de costes de una plataforma de IA, va el próximo artículo.&lt;/p>
&lt;h2 id="fuentes">Fuentes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://standards.globalspec.com/std/10266868/stanag-4774">NATO STANAG 4774 — Confidentiality Metadata Label Syntax&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.archtis.com/achieving-nato-stanag-4774-and-4778-compliance/">NATO STANAG 4778 — Metadata Binding Mechanism&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.nato.int/cps/en/natohq/news_227234.htm">NATO — Principios de Uso Responsable de la IA y Estrategia revisada (2024)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://dodcio.defense.gov/Portals/0/Documents/Library/DoD%20Enterprise%20DevSecOps%20Reference%20Design%20-%20CNCF%20Kubernetes%20w-DD1910_cleared_20211022.pdf">DoD Enterprise DevSecOps Reference Design — CNCF Kubernetes&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/DoD-Platform-One/bigbang">DoD — Big Bang / Platform One&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://dodcio.defense.gov/Portals/0/Documents/Library/(U)ZT_RA_v2.0(U)_Sep22.pdf">DoD Zero Trust Reference Architecture v2.0&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://dodcio.defense.gov/Portals/0/Documents/CMMC/CMMC-AlignmentNIST-Standards.pdf">CMMC 2.0 y NIST SP 800-171 (cadena de suministro de defensa)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.sigstore.dev/">Sigstore / Cosign — firma de imágenes y SBOM (CNCF)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://volcano.sh/en/docs/">Volcano — scheduling batch (CNCF)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cilium.io/">Cilium y Tetragon (eBPF)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.iso.org/standard/81230.html">ISO/IEC 42001 — Sistemas de gestión de IA&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cómo usamos nuestra infraestructura para cumplir las normas sanitarias con IA</title><link>https://blog.lo0.es/posts/infra-cumplimiento-normativo-sanidad-ia/</link><pubDate>Thu, 18 Jun 2026 10:30:00 +0200</pubDate><guid>https://blog.lo0.es/posts/infra-cumplimiento-normativo-sanidad-ia/</guid><description>&lt;p>En un entorno sanitario, el reto de poner IA en producción no es el modelo: es el cumplimiento. Las normas que aplican —tanto los estándares técnicos de interoperabilidad como las leyes— imponen requisitos concretos de control de acceso, confidencialidad, trazabilidad, localización del dato y gobernanza. Una demostración en un portátil ignora todo eso; una plataforma real tiene que satisfacerlo, y tiene que poder demostrarlo ante una auditoría.&lt;/p>
&lt;p>Este artículo no es un recorrido por tecnologías. Es lo contrario: tomamos los requisitos que imponen las normas y, para cada uno, mostramos cómo se usa nuestra infraestructura para cumplirlo. La infraestructura es real —dos clústeres RKE2 (Kubernetes v1.35) con Cilium como red, GitOps con Flux, Keycloak como servidor de identidad, inferencia en GPU propias y, en preproducción, scheduling con Volcano— y cada componente está aquí porque resuelve un requisito normativo, no porque quede bien en un diagrama. Como toda infraestructura, además, se mide por eficiencia y coste, que es lo que hace el cumplimiento sostenible en lugar de un gasto que no se aguanta.&lt;/p>
&lt;p>Conviene empezar por una idea que mucha gente desconoce: para esto &lt;strong>ya existen estándares del sector&lt;/strong>, y no hay que inventarse nada. Hay estándares de interoperabilidad clínica (HL7, FHIR, DICOM y los perfiles IHE que los combinan), hay un estándar Kubernetes-native para empaquetar y ejecutar IA de imagen médica (MONAI Deploy y su MONAI Application Package), y hay un estándar emergente de cómo debe ser un clúster para ejecutar IA con garantías (el CNCF Kubernetes AI Conformance). Vamos primero a situar ese mapa de estándares, y después a recorrer los requisitos uno a uno.&lt;/p>
&lt;h2 id="el-mapa-de-estándares-del-sector">El mapa de estándares del sector&lt;/h2>
&lt;p>Tres familias de estándares se cruzan en una plataforma de IA sanitaria, y entender qué cubre cada una evita reinventar ruedas.&lt;/p>
&lt;p>&lt;strong>Interoperabilidad clínica: HL7, FHIR, DICOM e IHE.&lt;/strong> Los formatos base son conocidos —HL7 v2 para la mensajería heredada, FHIR para el dato clínico moderno sobre REST, DICOM/DICOMweb para la imagen—. Lo que mucha gente pasa por alto es que existe un cuerpo, &lt;strong>IHE (Integrating the Healthcare Enterprise)&lt;/strong>, que no inventa formatos sino que define &lt;strong>perfiles&lt;/strong>: combinaciones concretas de esos estándares para resolver un caso de uso de forma interoperable. Perfiles como &lt;strong>XDS/XCA&lt;/strong> (intercambio de documentos), &lt;strong>PIX/PDQ&lt;/strong> (identificación y consulta de pacientes) y, crucial para la seguridad, &lt;strong>ATNA (Audit Trail and Node Authentication)&lt;/strong>, que especifica los cuatro pilares de un nodo seguro: autenticación de nodo, autenticación de usuario, registro de auditoría y cifrado de las comunicaciones. Y se apoya en un perfil hermano, &lt;strong>Consistent Time (CT)&lt;/strong>, que sincroniza los relojes de todos los nodos: sin tiempo fiable, un registro de auditoría no sirve como prueba, porque no se puede ordenar ni correlacionar lo que pasó. ATNA se ha modernizado además con &lt;strong>RESTful ATNA&lt;/strong> y el perfil &lt;strong>BALP (Basic Audit Log Patterns)&lt;/strong>, que expresan la auditoría como recursos &lt;strong>FHIR AuditEvent&lt;/strong>. IHE es, en la práctica, el estándar que dice &lt;em>cómo&lt;/em> se combinan FHIR y DICOM de forma segura y auditable; es la referencia natural para diseñar los controles de cumplimiento.&lt;/p>
&lt;p>&lt;strong>IA médica sobre Kubernetes: MONAI Deploy y el MAP.&lt;/strong> Para el problema específico de empaquetar, distribuir y ejecutar aplicaciones de IA de imagen médica existe un estándar de facto: &lt;strong>MONAI Deploy&lt;/strong>, parte del proyecto MONAI (framework open source de deep learning para imagen sanitaria, del ecosistema PyTorch), y su &lt;strong>MONAI Application Package (MAP)&lt;/strong>. Un MAP es una imagen de contenedor que cumple una especificación definida por el &lt;em>MONAI Deploy working group&lt;/em> —expertos de más de una docena de instituciones de imagen médica— y, según esa especificación, &lt;strong>un MAP debe soportar el despliegue en Kubernetes&lt;/strong> e integrarse con DICOM y FHIR para el intercambio de datos. Es decir, hay un formato estándar para &amp;ldquo;una app de IA médica&amp;rdquo; que corre nativamente en Kubernetes, que es exactamente nuestro sustrato. Esto importa para el cumplimiento porque un MAP encapsula el modelo, sus dependencias y su contrato de entrada/salida de forma versionada y reproducible. Alrededor del MAP, MONAI Deploy aporta además las piezas de orquestación que un entorno clínico necesita: un &lt;em>Informatics Gateway&lt;/em> que recibe estudios DICOM y dispara la inferencia, y un &lt;em>Workflow Manager&lt;/em> que encadena los pasos de un pipeline sobre Kubernetes. No es solo un formato de empaquetado: es una manera estándar de llevar una app de imagen médica desde el registro hasta el PACS sin integraciones a medida en cada hospital.&lt;/p>
&lt;p>&lt;strong>Clústeres preparados para IA: CNCF Kubernetes AI Conformance.&lt;/strong> En noviembre de 2025 la CNCF lanzó el &lt;strong>Certified Kubernetes AI Conformance Program&lt;/strong>, que estandariza cómo se ejecutan cargas de IA sobre Kubernetes mediante un conjunto de requisitos —los &lt;strong>KARs (Kubernetes AI Requirements)&lt;/strong>— sobre integración de GPU, gestión de volúmenes y &lt;em>networking&lt;/em> a nivel de job, alineados con Kubernetes v1.35. Y lo más relevante para sanidad: el programa anunció que durante 2026 se amplía con una línea de &lt;strong>Sovereign AI&lt;/strong>, centrada en &lt;em>sandboxing&lt;/em> reforzado y privacidad del dato. Es la formalización, a nivel de industria, de que un clúster que ejecuta IA seria debe cumplir unas garantías mínimas —y de que la soberanía del dato es ya una categoría de primera clase, no una preferencia.&lt;/p>
&lt;p>Con este mapa sobre la mesa, recorramos los requisitos.&lt;/p>
&lt;h2 id="requisito-interoperabilidad-sobre-estándares-ehds-mdr-ihe">Requisito: interoperabilidad sobre estándares (EHDS, MDR, IHE)&lt;/h2>
&lt;p>El EHDS obliga a que los datos de salud se intercambien en formatos estándar, y su perfilado se construye sobre &lt;strong>FHIR R4&lt;/strong>; los perfiles IHE definen cómo se hace ese intercambio de forma interoperable; el MDR exige trazabilidad del dato cuando el software es producto sanitario. El requisito: la plataforma no puede hablar un dialecto propio.&lt;/p>
&lt;p>Cómo lo cumplimos: ponemos una &lt;strong>capa de interoperabilidad&lt;/strong> como frontera entre el dominio clínico y el de inferencia. Un servidor FHIR (R4, porque es la versión normativa y la que perfila el EHDS; R5 tiene adopción marginal y el sector espera a R6, en balotaje desde enero de 2026) expone los recursos clínicos; un nodo DICOMweb sirve la imagen mediante QIDO-RS, WADO-RS y STOW-RS; y una pasarela transforma a FHIR el HL7 v2 entrante. Sobre esa base aplicamos los perfiles IHE pertinentes: identificación de paciente con PIX/PDQ para no cruzar identidades entre fuentes, e intercambio documental con XDS/XCA cuando procede. El modelo de IA nunca toca la base de datos clínica directamente: consume FHIR y DICOMweb a través de esta capa, que es donde se normaliza, se perfila y se controla el acceso. Cuando desplegamos una aplicación de IA de imagen, el objetivo es empaquetarla como &lt;strong>MAP&lt;/strong>, de modo que su contrato con DICOM/FHIR sea el estándar del sector y no una integración a medida.&lt;/p>
&lt;h2 id="requisito-los-resultados-de-la-ia-vuelven-al-flujo-clínico-en-estándar-dicom-fhir-mdr">Requisito: los resultados de la IA vuelven al flujo clínico en estándar (DICOM, FHIR, MDR)&lt;/h2>
&lt;p>Una IA clínica no termina cuando produce una salida: esa salida tiene que volver al sistema en un formato que el clínico y la historia entiendan, y de forma trazable (MDR). Devolver un PDF o un JSON propietario rompe la interoperabilidad y deja el resultado fuera del registro clínico, donde nadie lo audita ni lo reutiliza.&lt;/p>
&lt;p>Cómo lo cumplimos: los resultados de los modelos de imagen se reinyectan como objetos DICOM estándar —&lt;strong>DICOM SR&lt;/strong> (Structured Report) para hallazgos y mediciones, &lt;strong>DICOM SEG&lt;/strong> para segmentaciones, o &lt;em>Secondary Capture&lt;/em> para representaciones visuales— mediante STOW-RS, de modo que el PACS y el visor los tratan como cualquier otra serie del estudio. Los resultados no relacionados con imagen se expresan como recursos &lt;strong>FHIR&lt;/strong> (&lt;code>Observation&lt;/code>, &lt;code>DiagnosticReport&lt;/code>) ligados al paciente y al estudio de origen. Así, el resultado de la IA es un ciudadano de primera del registro clínico —referenciable, versionado y auditable—, no un artefacto suelto en un disco. Empaquetar la aplicación como MAP ayuda también aquí: el contrato de salida hacia DICOM/FHIR forma parte de la propia especificación del paquete, no de un script de pegamento distinto en cada despliegue.&lt;/p>
&lt;h2 id="requisito-acceso-mínimo-y-autorizado-rgpd-ens-ehds-ihe-atna">Requisito: acceso mínimo y autorizado (RGPD, ENS, EHDS, IHE ATNA)&lt;/h2>
&lt;p>El RGPD impone minimización (art. 5) y protección reforzada de los datos de salud como categoría especial (art. 9); el ENS exige identificación y control de acceso; el EHDS, control sobre quién accede y para qué; e IHE ATNA exige &lt;strong>autenticación de nodo&lt;/strong> y &lt;strong>autenticación de usuario&lt;/strong> como pilares de un nodo seguro. Traducido a infraestructura: cada acceso a datos clínicos autenticado, autorizado al mínimo y atado a un propósito; y cada nodo que participa, identificado.&lt;/p>
&lt;p>Cómo lo cumplimos: un único servidor de identidad, &lt;strong>Keycloak&lt;/strong>, gobierna toda la autorización con OAuth 2.1 y OpenID Connect, perfilados por &lt;strong>SMART on FHIR&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Scopes orientados a recurso y propósito.&lt;/strong> SMART distingue &lt;code>patient/&lt;/code>, &lt;code>user/&lt;/code> y &lt;code>system/&lt;/code>, y su versión 2 separa permisos por operación (&lt;code>create&lt;/code>, &lt;code>read&lt;/code>, &lt;code>update&lt;/code>, &lt;code>delete&lt;/code>, &lt;code>search&lt;/code>). Un agente de IA recibe el mínimo, por ejemplo &lt;code>system/ImagingStudy.rs&lt;/code> (solo leer y buscar estudios). Es la minimización del RGPD implementada en el token —y la autenticación de usuario que pide ATNA.&lt;/li>
&lt;li>&lt;strong>Identidad de servicio sin secretos compartidos.&lt;/strong> Las cargas desatendidas usan &lt;code>private_key_jwt&lt;/code> (aserción firmada con clave privada).&lt;/li>
&lt;li>&lt;strong>Binding de audiencia.&lt;/strong> El token lleva en &lt;code>aud&lt;/code> la URL del servidor de destino y este la valida, evitando la reutilización lateral de credenciales; se apoya en extensiones de Keycloak específicas para FHIR.&lt;/li>
&lt;li>&lt;strong>Autenticación de nodo (ATNA) con mTLS.&lt;/strong> Entre los nodos que tratan datos clínicos exigimos TLS mutuo, de modo que no solo se autentica al usuario o al servicio, sino a la propia máquina/servicio que participa en el intercambio.&lt;/li>
&lt;/ul>
&lt;p>DICOMweb se protege con el mismo token y el mismo Keycloak. El resultado es un plano de identidad único para FHIR, DICOMweb y los servidores MCP: un solo sitio donde demostrar quién puede acceder a qué.&lt;/p>
&lt;p>El mismo principio de mínimo privilegio se aplica un nivel más abajo, al propio plano de Kubernetes, que el ENS también alcanza. Los operadores no acceden con un &lt;code>cluster-admin&lt;/code> universal: el acceso de los administradores a la API del clúster se federa contra Keycloak por OIDC, con roles acotados por namespace, de modo que también el acceso técnico queda autenticado, limitado y auditado. Y las cargas de trabajo usan &lt;em>service accounts&lt;/em> sin permisos por defecto, a las que se concede solo lo imprescindible. Quien administra la plataforma está sujeto al mismo régimen de identidad que quien consume los datos clínicos a través de ella.&lt;/p>
&lt;h2 id="requisito-confidencialidad-aislamiento-y-prevención-de-fugas-rgpd-art-32-ens-atna-cifrado">Requisito: confidencialidad, aislamiento y prevención de fugas (RGPD art. 32, ENS, ATNA cifrado)&lt;/h2>
&lt;p>El RGPD (art. 32) exige confidencialidad, integridad, disponibilidad y resiliencia, con medidas como la separación de datos y el cifrado; el ENS detalla controles de segregación y protección de las comunicaciones; ATNA exige cifrado de las comunicaciones entre nodos. En una plataforma multi-cliente esto se traduce en dos cosas: que lo de un centro no sea visible para otro, y que un componente comprometido no pueda sacar datos fuera.&lt;/p>
&lt;p>Cómo lo cumplimos en el aislamiento entre inquilinos: cada tenant vive en su propio &lt;strong>namespace&lt;/strong>, con &lt;strong>RBAC&lt;/strong> acotado, &lt;strong>ResourceQuota&lt;/strong> y &lt;strong>LimitRange&lt;/strong>, una &lt;strong>base de datos dedicada&lt;/strong> por operador (CloudNativePG) —no una base compartida con un filtro de aplicación— y su propio espacio de almacenamiento de objetos. La identidad se segrega por realm en Keycloak. Todo se define en Git y lo reconcilia &lt;strong>Flux&lt;/strong> (GitOps): la separación es declarativa y auditable por historial de cambios. Ante &amp;ldquo;demuéstrame que el centro A no ve los datos del centro B&amp;rdquo;, la respuesta es un commit.&lt;/p>
&lt;p>Cómo lo cumplimos en la confidencialidad de red y la prevención de exfiltración: la red la gestiona &lt;strong>Cilium&lt;/strong> (eBPF) con modelo &lt;strong>default-deny&lt;/strong>. Un pod no tiene conectividad salvo la que una política le concede explícitamente, flujo a flujo, lo que convierte la exfiltración en algo que se bloquea en el kernel. Por ejemplo, el componente de trazabilidad solo puede hablar con su base analítica, su PostgreSQL, el almacenamiento de objetos, la caché y el DNS:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cilium.io/v2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">CiliumNetworkPolicy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">trazabilidad-egress-minimo&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">trazabilidad&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">endpointSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">trazabilidad-web&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">egressDeny&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">toEntities&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">world] &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># nada hacia fuera del clúster por defecto&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">egress&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">toEndpoints&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">postgres&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">toEndpoints&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">analitica&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">toEndpoints&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># DNS, sin esto el pod no resuelve&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">k8s:io.kubernetes.pod.namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kube-system&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">k8s-app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kube-dns&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">toPorts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>{&lt;span class="w"> &lt;/span>&lt;span class="nt">port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;53&amp;#34;&lt;/span>&lt;span class="nt">, protocol&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">UDP }]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dos matices que separan una política correcta de una rota o permeable: hay que permitir explícitamente el &lt;strong>DNS&lt;/strong> (si no, el pod no funciona) y filtrar por &lt;strong>identidad de endpoint&lt;/strong>, no por CIDR, porque en una red basada en identidad el filtrado por rangos de IP no se comporta como la intuición sugiere. Sobre esto añadimos observabilidad de flujos con &lt;strong>Hubble&lt;/strong> y seguridad en runtime con &lt;strong>Tetragon&lt;/strong> (eBPF), que vigila ejecución de procesos, acceso a ficheros y conexiones &lt;strong>dentro&lt;/strong> del contenedor, permitiendo detectar y bloquear comportamiento anómalo en cargas que tratan datos sensibles. Para el cifrado en tránsito que exigen el art. 32 y ATNA, cert-manager gestiona TLS en los ingress y Cilium puede cifrar de forma transparente el tráfico entre nodos.&lt;/p>
&lt;p>A esto se suman dos controles de base que el art. 32 y el ENS dan por descontados. Primero, los &lt;strong>Pod Security Standards&lt;/strong> en perfil restringido —contenedores sin privilegios, sin ejecutarse como root y con el sistema de ficheros en solo lectura donde es posible—, que reducen la superficie de un contenedor comprometido. Segundo, una &lt;strong>gestión disciplinada de secretos y claves&lt;/strong>: las claves privadas de &lt;code>private_key_jwt&lt;/code>, los certificados y las credenciales de base de datos no viven en manifiestos planos en Git, sino gestionadas y rotadas, con cert-manager emitiendo y renovando certificados de forma automática. Un secreto filtrado en un repositorio es una brecha de datos en potencia, y se trata como tal desde el diseño.&lt;/p>
&lt;h2 id="requisito-ciclo-de-vida-del-dato--minimización-retención-y-pseudonimización-rgpd-ehds">Requisito: ciclo de vida del dato — minimización, retención y pseudonimización (RGPD, EHDS)&lt;/h2>
&lt;p>El RGPD no solo limita quién accede: limita cuánto dato se trata (minimización, art. 5) y cuánto tiempo se conserva (limitación del plazo de conservación), y promueve la pseudonimización como medida de protección (art. 32). El requisito: no acumular datos clínicos identificables más de lo necesario, y desidentificar donde el caso de uso lo permita.&lt;/p>
&lt;p>Cómo lo cumplimos: la &lt;strong>capa de interoperabilidad&lt;/strong> es también el punto de control del ciclo de vida del dato. Allí se aplica pseudonimización cuando el flujo de IA no necesita identidad directa —por ejemplo, evaluación o ajuste sobre cohortes—, manteniendo la tabla de reidentificación separada del resto del sistema y tras sus propias políticas de acceso. Un principio que aplicamos de forma estricta: las &lt;strong>trazas y los logs no contienen datos clínicos&lt;/strong>. Registran identificadores de recurso, modelo, coste, latencia y tenant, no el contenido de la historia ni la imagen, de modo que la capa de observabilidad no se convierte en un segundo almacén de datos sensibles que haya que proteger y auditar por duplicado. Y las &lt;strong>políticas de retención&lt;/strong> se aplican por tipo de dato —trazas, cachés, copias intermedias— con expiración automática, de forma que el plazo de conservación es una propiedad declarada y verificable, no un olvido que va acumulando riesgo en discos que nadie revisa.&lt;/p>
&lt;h2 id="requisito-localización-y-soberanía-del-dato-rgpd-ehds-cncf-sovereign-ai">Requisito: localización y soberanía del dato (RGPD, EHDS, CNCF Sovereign AI)&lt;/h2>
&lt;p>Tratar datos de salud de categoría especial enviándolos a una API de inferencia de un tercero, fuera del control del responsable, es difícil de justificar bajo el RGPD y contrario al espíritu del EHDS. Y no es solo un requisito legal: la línea de &lt;strong>Sovereign AI&lt;/strong> del CNCF AI Conformance lo eleva a categoría técnica de primera clase para 2026. El requisito implícito es que el dato clínico no salga del perímetro.&lt;/p>
&lt;p>Cómo lo cumplimos: la inferencia se sirve &lt;strong>en local&lt;/strong>, sobre GPU propias, con un motor de serving de alto rendimiento (vLLM). Las aplicaciones de IA de imagen se empaquetan, idealmente, como &lt;strong>MAP&lt;/strong>, que corren nativamente en el clúster sin sacar el dato fuera. Delante ponemos una &lt;strong>pasarela de modelos&lt;/strong> por la que pasa todo el tráfico de inferencia: es el punto donde se decide qué se ejecuta dentro y dónde, de forma que el dato clínico no sale por defecto y cualquier excepción es explícita y registrada. Que esto sea viable —y no un lujo— depende de la eficiencia: para servir en casa con un coste razonable hay que exprimir el hardware, y ahí entra el scheduling. La soberanía deja de ser una promesa de la diapositiva para ser una propiedad de cómo está montada la inferencia.&lt;/p>
&lt;p>Hay un matiz que conviene explicitar, porque marca la diferencia entre &amp;ldquo;decimos que el dato no sale&amp;rdquo; y &amp;ldquo;el dato no puede salir sin que conste&amp;rdquo;. La pasarela invierte la carga: el comportamiento por defecto es la inferencia local, y cualquier salida hacia un servicio externo es una excepción que hay que habilitar de forma explícita, que queda registrada y que, además, choca con las políticas de red default-deny si no se ha abierto el destino correspondiente. Es decir, la soberanía no se sostiene solo en una decisión de diseño, sino en varios controles que se refuerzan entre sí —pasarela, política de red y registro—, de modo que un descuido de configuración no se traduce en una fuga silenciosa. En un dato de categoría especial, esa diferencia entre confianza y garantía es justo lo que pide el RGPD cuando habla de protección de datos &lt;em>desde el diseño y por defecto&lt;/em> (art. 25).&lt;/p>
&lt;h2 id="requisito-ejecución-fiable-de-cargas-críticas-y-eficiencia-ens-cncf-ai-conformance">Requisito: ejecución fiable de cargas críticas y eficiencia (ENS, CNCF AI Conformance)&lt;/h2>
&lt;p>Mantener la inferencia en casa solo es sostenible si la GPU se aprovecha, y ciertas cargas (un lote nocturno de procesamiento de imagen) deben ejecutarse de forma fiable y completa. El KAR del CNCF AI Conformance formaliza, además, requisitos de integración de GPU y de &lt;em>networking&lt;/em> a nivel de job que un clúster serio de IA debe cumplir. El planificador por defecto de Kubernetes programa pod a pod, lo que para cargas distribuidas produce trabajos a medias que retienen GPU sin avanzar.&lt;/p>
&lt;p>Cómo lo cumplimos: el reparto de GPU lo gobierna &lt;strong>Volcano&lt;/strong> (proyecto incubado en la CNCF), hoy en &lt;strong>preproducción&lt;/strong>, con los mecanismos que importan para fiabilidad y eficiencia:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Gang scheduling&lt;/strong> (&lt;code>PodGroup&lt;/code> con &lt;code>minAvailable&lt;/code>): un trabajo distribuido arranca completo o no arranca, evitando GPU bloqueadas a la espera.&lt;/li>
&lt;li>&lt;strong>Colas por tenant&lt;/strong> con cuota y prioridad, que materializan el reparto justo entre inquilinos que la multitenancy solo declara.&lt;/li>
&lt;li>&lt;strong>Fair-share (DRF)&lt;/strong> para repartir proporcionalmente y &lt;strong>preempción&lt;/strong> para que una carga urgente adelante a un lote diferible.&lt;/li>
&lt;li>&lt;strong>Conciencia de topología&lt;/strong> (NVLink/PCIe) para reducir la fragmentación.&lt;/li>
&lt;/ul>
&lt;p>Esto eleva la utilización efectiva del parque de GPU, que es lo que hace económicamente viable la inferencia soberana. Y se gestiona con métricas, no con impresiones: utilización efectiva frente a asignada, porcentaje de tiempo en &lt;em>idle&lt;/em>, fragmentación, rendimiento por GPU (tokens por segundo) y horas-GPU consumidas por tenant. Son las cifras que permiten decidir si hace falta más hardware o si sobra capacidad mal repartida, y las que convierten una discusión sobre coste en una decisión con datos. Sobre el estado del arte: Kubernetes 1.34 llevó &lt;strong>DRA (Dynamic Resource Allocation)&lt;/strong> a disponibilidad general —asignación de aceleradores por atributos en lugar de por conteo—, y &lt;strong>Kueue&lt;/strong> cubre la gestión de cuotas de jobs; ambos se combinan con un scheduler de gang como Volcano. La eficiencia no es un extra de FinOps: aquí es la condición que hace sostenible cumplir el requisito de soberanía.&lt;/p>
&lt;p>Conviene además leer estos componentes a la luz del &lt;strong>CNCF AI Conformance&lt;/strong>: sus requisitos (los KAR) verifican que un clúster integra correctamente las GPU, gestiona los volúmenes que las cargas de IA necesitan y resuelve el &lt;em>networking&lt;/em> a nivel de job. No son adornos: son justo las capacidades sobre las que se apoyan el serving de inferencia, el almacenamiento de modelos y datos, y el scheduling distribuido que acabamos de describir. Alinearse con ese estándar es, en la práctica, garantizar que la plataforma ejecuta IA con las mismas propiedades que la industria ha acordado como mínimo exigible —y poder demostrarlo, que en un entorno regulado vale tanto como cumplirlo.&lt;/p>
&lt;h2 id="requisito-registro-de-actividad-y-supervisión-humana-ai-act-ehds-ihe-atnabalp">Requisito: registro de actividad y supervisión humana (AI Act, EHDS, IHE ATNA/BALP)&lt;/h2>
&lt;p>El AI Act, para sistemas de alto riesgo —y la IA clínica casi siempre lo es—, exige registro automático de la actividad (logging, art. 12) y supervisión humana significativa (art. 14); el EHDS impone trazabilidad sobre el uso de los datos; e IHE ATNA/BALP define &lt;strong>cómo&lt;/strong> debe ser ese registro de auditoría, expresándolo como recursos &lt;strong>FHIR AuditEvent&lt;/strong>. El requisito: poder reconstruir cada acceso y cada decisión, en un formato estándar y consultable.&lt;/p>
&lt;p>Cómo lo cumplimos en dos planos. En el plano de &lt;strong>acceso al dato&lt;/strong>, los eventos de auditoría (quién accedió a qué recurso clínico, cuándo, con qué propósito) se emiten siguiendo el patrón &lt;strong>FHIR AuditEvent&lt;/strong> de BALP, de modo que la auditoría es interoperable y no un log propietario. En el plano de &lt;strong>decisión de la IA&lt;/strong>, cada inferencia se registra como una &lt;strong>traza auditable&lt;/strong> con Langfuse —prompt, respuesta, modelo y versión, coste, latencia y tenant—, almacenada en componentes propios encerrados tras políticas de red que impiden su salida. La traza cumple el logging del AI Act y la trazabilidad del EHDS, y a la vez sirve de base para la evaluación continua y para la supervisión humana, que se ejerce en la pasarela (revisión, límites, posibilidad de intervención). La instrumentación converge en las convenciones semánticas GenAI de OpenTelemetry, correlacionables con el resto de la observabilidad.&lt;/p>
&lt;p>Un registro de auditoría solo vale como prueba si es &lt;strong>íntegro&lt;/strong> y está &lt;strong>ordenado en el tiempo&lt;/strong>. Por eso los eventos se conservan en almacenamiento de solo-añadir (sin posibilidad de edición a posteriori) con su retención definida, y los relojes de los nodos se mantienen sincronizados —el perfil Consistent Time de IHE llevado a la práctica con NTP—, de modo que la secuencia de &amp;ldquo;quién hizo qué y cuándo&amp;rdquo; es reconstruible y defendible. Sin integridad y sin tiempo fiable, un log es un apunte, no una evidencia.&lt;/p>
&lt;h2 id="requisito-disponibilidad-continuidad-y-recuperación-rgpd-art-32-ens">Requisito: disponibilidad, continuidad y recuperación (RGPD art. 32, ENS)&lt;/h2>
&lt;p>El art. 32 del RGPD exige no solo confidencialidad e integridad, sino &lt;strong>disponibilidad y resiliencia&lt;/strong>, y la capacidad de &lt;strong>restaurar&lt;/strong> el acceso a los datos tras un incidente; el ENS detalla medidas de continuidad de servicio. Un sistema clínico que se cae y no se recupera es un incumplimiento, no solo una incidencia operativa.&lt;/p>
&lt;p>Cómo lo cumplimos: la plataforma corre sobre &lt;strong>dos emplazamientos&lt;/strong> (site01 y site02), lo que permite separar cargas y disponer de capacidad ante la caída de un sitio. El plano de control de cada clúster es de &lt;strong>alta disponibilidad&lt;/strong>, con varios nodos de control y etcd replicado. Las bases de datos por tenant las gestiona un operador que automatiza réplicas y &lt;strong>copias de seguridad&lt;/strong> hacia el almacenamiento de objetos, y el estado declarativo en Git permite &lt;strong>reconstruir un entorno completo&lt;/strong> desde su repositorio. La recuperación no depende de un servidor concreto ni de la memoria de quien lo montó, sino de volver a aplicar lo que está versionado. La combinación de copias de datos y reproducibilidad de configuración es, en términos del art. 32, precisamente la capacidad de restaurar el servicio en un plazo razonable.&lt;/p>
&lt;h2 id="requisito-operación-reproducible-y-demostrable-ens-mdriso-13485-ai-act">Requisito: operación reproducible y demostrable (ENS, MDR/ISO 13485, AI Act)&lt;/h2>
&lt;p>Las normas no solo piden que el sistema haga lo correcto: piden poder demostrarlo y reproducirlo. El ENS exige gestión de la configuración y trazabilidad de cambios; el MDR (vía ISO 13485) exige control de cambios y reproducibilidad; el AI Act, gestión de riesgos y documentación técnica.&lt;/p>
&lt;p>Cómo lo cumplimos: todo el estado de la plataforma —despliegues, políticas de red, cuotas, RBAC, configuración de identidad— se describe en Git y lo aplica &lt;strong>Flux&lt;/strong> por GitOps. Cada cambio es una revisión con autor, fecha y revisor; cada entorno es reproducible desde su repositorio. Las versiones de modelo quedan registradas en las trazas, y empaquetar la IA como &lt;strong>MAP&lt;/strong> versionado refuerza la reproducibilidad: se sabe exactamente qué artefacto se ejecutó. Una auditoría de configuración se convierte así en una consulta al historial, con capacidad de reconstruir qué estaba desplegado en una fecha dada.&lt;/p>
&lt;h2 id="la-pieza-que-lo-ata-todo-isoiec-42001">La pieza que lo ata todo: ISO/IEC 42001&lt;/h2>
&lt;p>Todo lo anterior son controles técnicos que satisfacen requisitos concretos. Pero las normas también exigen un sistema de gestión que los ordene, evalúe y mejore de forma continua, con roles y responsabilidades definidos. Esa pieza es &lt;strong>ISO/IEC 42001&lt;/strong>, la norma certificable de sistemas de gestión de IA: la que transforma &amp;ldquo;tenemos los controles&amp;rdquo; en &amp;ldquo;tenemos una IA gobernada y demostrable&amp;rdquo;. Es, precisamente, el contenido del curso de Sistemas de Gestión de IA que publicaremos en breve, pensado para acompañar el salto de la implementación técnica a la gobernanza certificable.&lt;/p>
&lt;h2 id="visión-del-sector-a-junio-de-2026">Visión del sector a junio de 2026&lt;/h2>
&lt;p>Vale la pena fijar la foto del momento, porque varias corrientes han convergido justo ahora y condicionan cualquier decisión de plataforma.&lt;/p>
&lt;p>&lt;strong>Todo lo de IA converge en Kubernetes.&lt;/strong> La propia CNCF lo describió en 2026 como &amp;ldquo;la gran migración&amp;rdquo;: las plataformas de IA, antes dispersas en soluciones propietarias, se están consolidando sobre Kubernetes como sustrato común. La consecuencia práctica es que las capacidades antes reservadas a sistemas HPC —gang scheduling, conciencia de topología, integración con fabric de red— son ya ciudadanas de primera en Kubernetes.&lt;/p>
&lt;p>&lt;strong>La asignación de aceleradores se estandariza.&lt;/strong> Con &lt;strong>DRA en disponibilidad general&lt;/strong> (Kubernetes 1.34) el modelo viejo de &amp;ldquo;contar GPU&amp;rdquo; da paso a la asignación por atributos del dispositivo, con particionado dinámico. Es un cambio de fondo en cómo se reparte el hardware caro, y nuestros clústeres (v1.35) ya están en la rama donde esto es real.&lt;/p>
&lt;p>&lt;strong>Aparecen sellos de conformidad para IA.&lt;/strong> El &lt;strong>CNCF Kubernetes AI Conformance&lt;/strong> (lanzado en noviembre de 2025, con los KARs) define qué hace &amp;ldquo;AI-ready&amp;rdquo; a un clúster, y en 2026 incorpora la línea de &lt;strong>Sovereign AI&lt;/strong>: sandboxing reforzado y privacidad del dato como requisitos formales. Para sanidad esto es enorme, porque convierte la soberanía del dato —que veníamos defendiendo por obligación legal— en una propiedad certificable de la plataforma.&lt;/p>
&lt;p>&lt;strong>La IA médica de imagen tiene su estándar de empaquetado.&lt;/strong> &lt;strong>MONAI Deploy&lt;/strong> y el &lt;strong>MAP&lt;/strong> maduran como la forma estándar de construir, validar y ejecutar aplicaciones de imagen médica, con Kubernetes como destino y DICOM/FHIR como contrato. Quien despliega IA de imagen seria en 2026 mira hacia MAP, no hacia integraciones a medida.&lt;/p>
&lt;p>&lt;strong>La regulación entra en su ventana de aplicación.&lt;/strong> El &lt;strong>EHDS&lt;/strong> está en vigor desde marzo de 2025 y sus hitos se acercan: organismos de acceso a datos hacia 2027, uso primario transfronterizo hacia 2029, todo perfilado sobre FHIR R4. El &lt;strong>AI Act&lt;/strong> vio sus plazos de alto riesgo aplazados por el &lt;em>Digital Omnibus&lt;/em> (Anexo III a diciembre de 2027, Anexo I a agosto de 2028), pero sus obligaciones —gestión de riesgos, logging, supervisión humana— no se mueven. Y la transición &lt;strong>FHIR R4 → R6&lt;/strong> se planifica saltando R5. La foto es la de un sector donde la técnica (Kubernetes, eBPF, DRA, MAP) y la norma (EHDS, AI Act, IHE) por fin avanzan a la vez.&lt;/p>
&lt;p>&lt;strong>La economía de la GPU empuja hacia dentro de casa.&lt;/strong> El recurso sigue siendo escaso y caro, y eso tiene dos efectos que se refuerzan. Por un lado, la presión por la eficiencia —utilización, fragmentación, densidad— es máxima, y por eso el scheduling (Volcano) y la asignación por atributos (DRA) están en el centro del debate. Por otro, la maduración de los &lt;strong>modelos de pesos abiertos&lt;/strong>, que rinden cada vez mejor en hardware modesto, ha hecho viable servir inferencia de calidad en local sin depender de una API externa. La combinación —modelos abiertos competentes más herramientas de eficiencia sobre Kubernetes— es justo la que convierte la soberanía del dato, en sanidad, de aspiración costosa a opción razonable. La tendencia de 2026 no es solo &amp;ldquo;IA en la nube&amp;rdquo;; es también un regreso pragmático del cómputo a infraestructura propia donde el dato y el coste lo exigen.&lt;/p>
&lt;p>Dónde estamos nosotros en esa foto: con la plataforma sobre Kubernetes, eBPF como sustrato de red y seguridad, scheduling de GPU madurando en preproducción, inferencia soberana por diseño, trazabilidad en formato auditable y el marco de gestión (ISO 42001) como siguiente paso. No perseguimos la moda; estamos exactamente en la línea por donde el sector ha decidido avanzar.&lt;/p>
&lt;h2 id="resumen-requisito-por-requisito">Resumen: requisito por requisito&lt;/h2>
&lt;p>La tesis es simple: cada norma o estándar del sector impone un requisito, y cada requisito se satisface con una pieza concreta de la infraestructura. La interoperabilidad del EHDS y los perfiles IHE, con una capa FHIR R4 + DICOMweb y, para la IA de imagen, el empaquetado MAP. El acceso mínimo y autorizado del RGPD/ENS y la autenticación de ATNA, con Keycloak, SMART on FHIR y mTLS. La confidencialidad y la prevención de fugas del art. 32, con multitenancy, Cilium en default-deny y Tetragon. La soberanía del dato (RGPD/EHDS y CNCF Sovereign AI), con inferencia local viable gracias a la eficiencia de Volcano. El logging y la supervisión del AI Act y la auditoría de ATNA/BALP, con trazabilidad en formato FHIR AuditEvent. La operación reproducible del ENS y el MDR, con GitOps y MAP versionado. Y la gobernanza que lo ata todo, con ISO 42001.&lt;/p>
&lt;p>No es un PowerPoint: es infraestructura que corre, que cumple y que —por ser infraestructura— se mide por eficiencia y coste. De eso, del control de costes de una plataforma de IA sanitaria, va el próximo artículo.&lt;/p>
&lt;h2 id="fuentes">Fuentes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.hl7.org/fhir/">HL7 FHIR — especificación y versiones&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://hl7.org/fhir/smart-app-launch/">SMART App Launch (SMART on FHIR) v2&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://profiles.ihe.net/ITI/TF/Volume1/ch-9.html">IHE — Audit Trail and Node Authentication (ATNA)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://profiles.ihe.net/ITI/BALP/index.html">IHE — Basic Audit Log Patterns (BALP) con FHIR AuditEvent&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.dicomstandard.org/using/dicomweb">DICOMweb — DICOM Standard&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Project-MONAI/monai-deploy/blob/main/guidelines/monai-application-package.md">MONAI Deploy y MONAI Application Package (MAP)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/cncf/k8s-ai-conformance">CNCF — Certified Kubernetes AI Conformance&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://kubernetes.io/docs/concepts/scheduling-eviction/dynamic-resource-allocation/">Kubernetes — Dynamic Resource Allocation (DRA)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://volcano.sh/en/docs/">Volcano — scheduling batch (CNCF)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cilium.io/">Cilium y Tetragon (eBPF)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://health.ec.europa.eu/ehealth-digital-health-and-care/european-health-data-space-regulation-ehds_en">Reglamento (UE) 2025/327 — EHDS&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://artificialintelligenceact.eu/">EU AI Act (Reglamento (UE) 2024/1689)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.iso.org/standard/81230.html">ISO/IEC 42001 — Sistemas de gestión de IA&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cuando el MCP crece: ponerle autenticación con Keycloak</title><link>https://blog.lo0.es/posts/mcp-crece-autenticacion-keycloak/</link><pubDate>Thu, 18 Jun 2026 08:30:00 +0200</pubDate><guid>https://blog.lo0.es/posts/mcp-crece-autenticacion-keycloak/</guid><description>&lt;p>Cuando un hijo es pequeño, basta con vigilar que no se haga daño. Le pones una valla en la escalera, tapas los enchufes y poco más. Pero el niño crece. Empieza a salir solo, a manejar dinero, a tomar decisiones que tienen consecuencias. Y entonces la educación deja de ser «que no se caiga» para convertirse en algo mucho más exigente: enseñarle a identificarse, a pedir permiso, a saber hasta dónde puede llegar y a rendir cuentas de lo que hace.&lt;/p>
&lt;p>Con nuestros servidores MCP nos ha pasado exactamente eso.&lt;/p>
&lt;h2 id="el-mcp-ya-no-es-un-juguete">El MCP ya no es un juguete&lt;/h2>
&lt;p>Hace nada, un servidor &lt;a href="https://modelcontextprotocol.io/">Model Context Protocol&lt;/a> era un experimento que corría en el portátil de alguien para que un modelo pudiera leer un par de ficheros o llamar a una API interna. Daba igual quién lo usara: éramos nosotros, en local, sin nada en juego.&lt;/p>
&lt;p>Eso se acabó. Los MCP se han vuelto piezas de infraestructura que la IA usa para tocar sistemas de verdad. Y, como cualquier hijo que crece, han desarrollado necesidades nuevas que antes no tenían:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Multitenancy&lt;/strong>: ya no hay un único usuario, sino muchos, y lo de uno no puede ser visible para otro.&lt;/li>
&lt;li>&lt;strong>Control de costes&lt;/strong>: cada llamada del modelo consume tokens, cómputo y, a veces, APIs de pago. Hay que medir, atribuir y poner límites.&lt;/li>
&lt;li>&lt;strong>Autenticación y autorización&lt;/strong>: saber quién está al otro lado, qué permisos tiene y para qué servidor concreto vale su credencial.&lt;/li>
&lt;/ul>
&lt;p>Son tres frentes distintos. Hoy nos centramos en el tercero, que es el que abre la puerta a los otros dos: &lt;strong>la autenticación&lt;/strong>. Sin saber quién llama, no hay tenant que separar ni coste que atribuir.&lt;/p>
&lt;h2 id="por-qué-la-autenticación-del-mcp-es-un-problema-con-nombre-propio">Por qué la autenticación del MCP es un problema con nombre propio&lt;/h2>
&lt;p>La buena noticia es que aquí no hay que inventar nada. La especificación de MCP no se sacó de la manga un mecanismo de seguridad propio: se apoya en &lt;strong>OAuth&lt;/strong>, el mismo estándar que lleva años detrás del «Iniciar sesión con…» de medio internet. El MCP define cómo un servidor MCP actúa de &lt;em>recurso protegido&lt;/em> y delega la emisión de credenciales en un &lt;em>servidor de autorización&lt;/em>.&lt;/p>
&lt;p>Y ese servidor de autorización puede ser &lt;a href="https://www.keycloak.org/">Keycloak&lt;/a>, el gestor de identidades open source de la Cloud Native Computing Foundation que probablemente ya tengas montado para el resto de tus aplicaciones. La idea es no añadir un silo de identidad más solo porque ahora hablamos con modelos: reutilizar el que ya gobierna a tus usuarios.&lt;/p>
&lt;p>El matiz importante es que &lt;strong>MCP no es un único estándar congelado&lt;/strong>, sino una especificación viva con varias versiones, y cada una pide cosas distintas. A día de hoy conviven cuatro:&lt;/p>
&lt;ul>
&lt;li>&lt;code>2025-11-25&lt;/code> (la última)&lt;/li>
&lt;li>&lt;code>2025-06-18&lt;/code>&lt;/li>
&lt;li>&lt;code>2025-03-26&lt;/code>&lt;/li>
&lt;li>&lt;code>2024-11-05&lt;/code> (la inicial, que ni siquiera contemplaba autorización)&lt;/li>
&lt;/ul>
&lt;p>Igual que con la educación de un hijo no es lo mismo lo que necesita a los 6 años que a los 16, lo que Keycloak tiene que cumplir depende de la versión de MCP con la que trabajes. Veámoslo.&lt;/p>
&lt;h2 id="qué-le-exige-mcp-a-un-servidor-de-autorización-y-qué-cumple-keycloak">Qué le exige MCP a un servidor de autorización (y qué cumple Keycloak)&lt;/h2>
&lt;p>La especificación enumera una serie de estándares OAuth que el servidor de autorización debe soportar, con distintos niveles de exigencia (&lt;code>MUST&lt;/code>, &lt;code>SHOULD&lt;/code>, &lt;code>MAY&lt;/code>). Esta es la foto, cruzada con lo que Keycloak soporta:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Estándar&lt;/th>
&lt;th>2025-11-25&lt;/th>
&lt;th>2025-06-18&lt;/th>
&lt;th>2025-03-26&lt;/th>
&lt;th>Keycloak&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>OAuth 2.1 Authorization Framework (borrador)&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>Soportado&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OAuth 2.0 Authorization Server Metadata (RFC 8414)&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>Soportado&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Resource Indicators for OAuth 2.0 (RFC 8707)&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>MUST&lt;/td>
&lt;td>—&lt;/td>
&lt;td>&lt;strong>No soportado&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Dynamic Client Registration (RFC 7591)&lt;/td>
&lt;td>MAY&lt;/td>
&lt;td>SHOULD&lt;/td>
&lt;td>SHOULD&lt;/td>
&lt;td>Soportado&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OAuth Client ID Metadata Document (borrador)&lt;/td>
&lt;td>SHOULD&lt;/td>
&lt;td>—&lt;/td>
&lt;td>—&lt;/td>
&lt;td>Soportado (experimental)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Una aclaración: MCP también adopta &lt;em>OAuth 2.0 Protected Resource Metadata&lt;/em> (RFC 9728), pero ese estándar es cosa del &lt;strong>servidor MCP&lt;/strong>, no del servidor de autorización, así que no entra en la tabla de Keycloak.&lt;/p>
&lt;p>Si tomamos como criterio de conformidad que Keycloak «soporta» una versión cuando cumple todos sus &lt;code>MUST&lt;/code> y &lt;code>SHOULD&lt;/code>, el resultado es este:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Versión MCP&lt;/th>
&lt;th>Conformidad&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>2025-03-26&lt;/code>&lt;/td>
&lt;td>&lt;strong>Soportada&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>2025-06-18&lt;/code>&lt;/td>
&lt;td>Parcial, sin Resource Indicators (RFC 8707)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>2025-11-25&lt;/code>&lt;/td>
&lt;td>Parcial, sin Resource Indicators (RFC 8707)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>La única pieza que hoy le falta a Keycloak es &lt;strong>Resource Indicators for OAuth 2.0 (RFC 8707)&lt;/strong>, el parámetro &lt;code>resource&lt;/code> que ata un token a un servidor concreto. La comunidad de Keycloak ya tiene en hoja de ruta soportarlo; mientras tanto, hay un rodeo perfectamente válido que vemos más abajo. Es, siguiendo la analogía, esa asignatura que al niño aún se le resiste pero que se puede aprobar con un truco de estudio hasta que madure del todo.&lt;/p>
&lt;h2 id="manos-a-la-obra-montar-keycloak-según-tu-versión-de-mcp">Manos a la obra: montar Keycloak según tu versión de MCP&lt;/h2>
&lt;h3 id="para-mcp-2025-03-26">Para MCP 2025-03-26&lt;/h3>
&lt;p>No hay que configurar nada especial. Esta versión no exige el parámetro &lt;code>resource&lt;/code>, así que Keycloak cumple de serie. El hijo todavía es pequeño y la valla en la escalera basta.&lt;/p>
&lt;h3 id="para-mcp-2025-06-18-y-2025-11-25-atar-el-token-a-su-audiencia">Para MCP 2025-06-18 y 2025-11-25: atar el token a su audiencia&lt;/h3>
&lt;p>Aquí empieza lo interesante. Por seguridad, estas versiones exigen que un token de acceso esté &lt;strong>ligado a la audiencia&lt;/strong> para la que se emitió. En cristiano:&lt;/p>
&lt;ul>
&lt;li>El cliente MCP &lt;strong>debe&lt;/strong> incluir el parámetro &lt;code>resource&lt;/code> (de RFC 8707) en la petición de autorización y de token, con el valor que identifica al servidor MCP que va a usar.&lt;/li>
&lt;li>El servidor MCP &lt;strong>debe&lt;/strong> validar que los tokens que recibe se emitieron específicamente para él.&lt;/li>
&lt;/ul>
&lt;p>Esto evita que un token robado o reutilizado sirva para entrar en un servidor distinto del previsto. El problema, como decíamos, es que &lt;strong>Keycloak todavía no entiende el parámetro &lt;code>resource&lt;/code>&lt;/strong>.&lt;/p>
&lt;p>La solución mientras llega el soporte nativo de RFC 8707 es usar el parámetro &lt;strong>&lt;code>scope&lt;/code>&lt;/strong> de OAuth para conseguir el mismo efecto. Imaginemos esta situación:&lt;/p>
&lt;ul>
&lt;li>El servidor MCP está en &lt;code>https://example.com/mcp&lt;/code>.&lt;/li>
&lt;li>Soporta tres scopes: &lt;code>mcp:tools&lt;/code>, &lt;code>mcp:prompts&lt;/code> y &lt;code>mcp:resources&lt;/code>.&lt;/li>
&lt;li>El cliente pide un token con &lt;code>resource = https://example.com/mcp&lt;/code> y una combinación de esos scopes.&lt;/li>
&lt;li>Queremos que Keycloak emita un token cuyo &lt;code>aud&lt;/code> (audiencia) sea precisamente &lt;code>https://example.com/mcp&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Para lograrlo, configuramos Keycloak así, repitiendo el patrón para cada scope:&lt;/p>
&lt;ul>
&lt;li>Crea un &lt;strong>client scope&lt;/strong> &lt;code>mcp:tools&lt;/code> de tipo &lt;em>Optional&lt;/em> y añádele un &lt;strong>Audience mapper&lt;/strong> cuyo &lt;em>Included Custom Audience&lt;/em> sea &lt;code>https://example.com/mcp&lt;/code>.&lt;/li>
&lt;li>Crea un &lt;strong>client scope&lt;/strong> &lt;code>mcp:prompts&lt;/code> de tipo &lt;em>Optional&lt;/em> con su &lt;strong>Audience mapper&lt;/strong> apuntando a &lt;code>https://example.com/mcp&lt;/code>.&lt;/li>
&lt;li>Crea un &lt;strong>client scope&lt;/strong> &lt;code>mcp:resources&lt;/code> de tipo &lt;em>Optional&lt;/em> con su &lt;strong>Audience mapper&lt;/strong> apuntando a &lt;code>https://example.com/mcp&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>La clave es que el &lt;em>Included Custom Audience&lt;/em> de cada client scope sea &lt;strong>idéntico&lt;/strong> al valor del parámetro &lt;code>resource&lt;/code> de la petición y a la URL del servidor MCP. Con esto, si el cliente pide un token con esos tres scopes, Keycloak emite algo como:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;aud&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://example.com/mcp&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;scope&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;mcp:resources mcp:tools mcp:prompts&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>El servidor MCP ya puede comprobar el &lt;code>aud&lt;/code> y rechazar cualquier token que no fuera para él. Hemos atado la credencial a su destinatario sin esperar a RFC 8707.&lt;/p>
&lt;h3 id="si-usas-mcp-inspector">Si usas MCP Inspector&lt;/h3>
&lt;p>&lt;a href="https://github.com/modelcontextprotocol/inspector">MCP Inspector&lt;/a> es la herramienta oficial para depurar servidores MCP, y registra clientes dinámicamente contra Keycloak ejecutando JavaScript desde su backend. Para que funcione hay que ajustar &lt;strong>CORS&lt;/strong> en las &lt;em>anonymous access policies&lt;/em> del registro de clientes:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Allowed Client Scopes&lt;/strong>: incluir los scopes que soporta tu servidor MCP.&lt;/li>
&lt;li>&lt;strong>Allowed Registration Web Origins&lt;/strong>: incluir el origen web del backend de MCP Inspector.&lt;/li>
&lt;li>&lt;strong>Trusted Hosts&lt;/strong>: incluir el host o IP de la máquina que envía la petición de registro dinámico, es decir, donde corre tu navegador.&lt;/li>
&lt;/ul>
&lt;h3 id="para-mcp-2025-11-25-registro-de-clientes-con-client-id-metadata-document">Para MCP 2025-11-25: registro de clientes con Client ID Metadata Document&lt;/h3>
&lt;p>La última versión añade un capítulo nuevo: cómo se registran los clientes. Contempla tres enfoques, y eliges según tu escenario:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Client ID Metadata Documents&lt;/strong>: cuando cliente y servidor no se conocen de antes (el caso más común).&lt;/li>
&lt;li>&lt;strong>Pre-registro&lt;/strong>: cuando ya existe una relación previa.&lt;/li>
&lt;li>&lt;strong>Dynamic Client Registration&lt;/strong>: para compatibilidad hacia atrás o requisitos concretos.&lt;/li>
&lt;/ul>
&lt;p>Keycloak soporta el primero, &lt;strong>OAuth Client ID Metadata Document (CIMD)&lt;/strong>, aunque conviene saber que es una &lt;strong>función experimental&lt;/strong>: puede traer cambios incompatibles en versiones futuras. Para activarla, arranca Keycloak con el flag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">bin/kc.sh start --features&lt;span class="o">=&lt;/span>cimd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>La idea de CIMD es que el &lt;code>client_id&lt;/code> deja de ser un identificador opaco y pasa a ser &lt;strong>una URL&lt;/strong> que apunta a un documento con los metadatos del cliente. Keycloak descarga ese documento y procesa la petición con lo que encuentre. Para que lo haga, hay que crear un &lt;em>client policy profile&lt;/em> y una &lt;em>policy&lt;/em> que lo disparen.&lt;/p>
&lt;p>&lt;strong>Perfil (profile).&lt;/strong> En &lt;em>Realm Settings → Client Policies → Profiles&lt;/em>, crea un perfil (p. ej. &lt;code>cimd-profile&lt;/code>), añade el ejecutor &lt;code>client-id-metadata-document&lt;/code> y configúralo:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Allow http scheme&lt;/strong>: permite &lt;code>http&lt;/code> para las URLs (client_id, &lt;code>client_uri&lt;/code>, &lt;code>logo_uri&lt;/code>, &lt;code>jwks_uri&lt;/code>, etc.). Solo en desarrollo; en producción &lt;strong>OFF&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>Trusted domains&lt;/strong>: patrones con comodín de dominios aceptados (p. ej. &lt;code>*.example.org&lt;/code>). Si está vacío, se deniegan todos.&lt;/li>
&lt;li>&lt;strong>Restrict same domain&lt;/strong>: si está ON, exige que el &lt;code>client_id&lt;/code> URL, el &lt;code>redirect_uri&lt;/code> y las URLs de los metadatos estén todos bajo el mismo dominio de confianza.&lt;/li>
&lt;li>&lt;strong>Required properties&lt;/strong>: propiedades que el documento de metadatos debe incluir obligatoriamente.&lt;/li>
&lt;li>&lt;strong>Only Allow Confidential Client&lt;/strong>: si está ON, solo acepta clientes confidenciales (con &lt;code>jwks&lt;/code>/&lt;code>jwks_uri&lt;/code> y autenticación &lt;code>private_key_jwt&lt;/code> o &lt;code>tls_client_auth&lt;/code>).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Política (policy).&lt;/strong> En &lt;em>Realm Settings → Client Policies → Policies&lt;/em>, crea una policy (p. ej. &lt;code>cimd-policy&lt;/code>), añade la condición &lt;code>client-id-uri&lt;/code> y configúrala:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>URI scheme&lt;/strong>: esquemas a reconocer en el &lt;code>client_id&lt;/code> (en producción, solo &lt;code>https&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Trusted domains&lt;/strong>: dominios aceptados en el host del &lt;code>client_id&lt;/code>. Si se rellenan, la condición solo es verdadera cuando el host coincide; si se dejan vacíos, es siempre falsa.&lt;/li>
&lt;/ul>
&lt;p>Luego asocia el &lt;code>cimd-profile&lt;/code> a esta policy. A partir de ahí, cuando llegue una petición con un &lt;code>client_id&lt;/code> que sea una URL &lt;code>https&lt;/code> de un dominio de confianza, Keycloak descarga el documento de metadatos y lo usa para procesar la petición.&lt;/p>
&lt;p>&lt;strong>Ajustes globales del ejecutor.&lt;/strong> Algunos parámetros no están en la consola y se pasan como opciones SPI al arrancar:&lt;/p>
&lt;ul>
&lt;li>&lt;code>min-cache-time&lt;/code>: tiempo mínimo de caché del documento (por defecto 300 s).&lt;/li>
&lt;li>&lt;code>max-cache-time&lt;/code>: tiempo máximo de caché (por defecto 259200 s, 3 días).&lt;/li>
&lt;li>&lt;code>upper-limit-metadata-bytes&lt;/code>: tamaño máximo del documento (por defecto 5000 bytes).&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">bin/kc.sh start &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --spi-client-policy-executor--client-id-metadata-document--min-cache-time&lt;span class="o">=&lt;/span>&lt;span class="m">600&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --spi-client-policy-executor--client-id-metadata-document--max-cache-time&lt;span class="o">=&lt;/span>&lt;span class="m">86400&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --spi-client-policy-executor--client-id-metadata-document--upper-limit-metadata-bytes&lt;span class="o">=&lt;/span>&lt;span class="m">10000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="caso-real-vs-code-desktop-como-cliente-mcp">Caso real: VS Code desktop como cliente MCP&lt;/h3>
&lt;p>Visual Studio Code es un cliente MCP que usa CIMD. Cuando se conecta a un servidor MCP que requiere autorización, manda un &lt;code>client_id&lt;/code> que es una URL &lt;code>https&lt;/code> alojada en &lt;code>vscode.dev&lt;/code> (p. ej. &lt;code>https://vscode.dev/mcp-client&lt;/code>), y Keycloak descarga de ahí sus metadatos. El detalle a tener en cuenta es que VS Code es un &lt;strong>cliente público&lt;/strong> que usa &lt;strong>PKCE&lt;/strong> (sin secreto de cliente) y, para el callback de OAuth, levanta un servidor local con un &lt;code>redirect_uri&lt;/code> tipo &lt;code>http://127.0.0.1:&amp;lt;puerto&amp;gt;/callback&lt;/code>. Como ese redirect no está en el dominio de &lt;code>vscode.dev&lt;/code>, hay que dejar &lt;strong>Restrict same domain en OFF&lt;/strong>.&lt;/p>
&lt;p>Arranca con &lt;code>--features=cimd&lt;/code> y configura:&lt;/p>
&lt;p>&lt;strong>Perfil &lt;code>vscode-cimd-profile&lt;/code>&lt;/strong> (ejecutor &lt;code>client-id-metadata-document&lt;/code>):&lt;/p>
&lt;ul>
&lt;li>Allow http scheme: &lt;strong>OFF&lt;/strong>&lt;/li>
&lt;li>Trusted domains: &lt;code>vscode.dev&lt;/code>, &lt;code>127.0.0.1&lt;/code>&lt;/li>
&lt;li>Restrict same domain: &lt;strong>OFF&lt;/strong> (el redirect es un localhost, no &lt;code>vscode.dev&lt;/code>)&lt;/li>
&lt;li>Only Allow Confidential Client: &lt;strong>OFF&lt;/strong> (VS Code es público)&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Política &lt;code>vscode-cimd-policy&lt;/code>&lt;/strong> (condición &lt;code>client-id-uri&lt;/code>):&lt;/p>
&lt;ul>
&lt;li>URI scheme: &lt;code>https&lt;/code>&lt;/li>
&lt;li>Trusted domains: &lt;code>vscode.dev&lt;/code>&lt;/li>
&lt;li>Asocia el perfil &lt;code>vscode-cimd-profile&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Con esto, cuando VS Code lance la petición, Keycloak reconoce el &lt;code>client_id&lt;/code> como una URL de &lt;code>vscode.dev&lt;/code>, descarga el Client ID Metadata Document y completa el flujo OAuth con el callback en localhost.&lt;/p>
&lt;h2 id="la-educación-continúa">La educación continúa&lt;/h2>
&lt;p>Hemos enseñado a nuestro MCP a identificarse y a pedir permiso de forma estándar, reutilizando la identidad que ya gobierna Keycloak en el resto de la casa. No es poca cosa: con un token bien atado a su audiencia, lo demás empieza a ser posible.&lt;/p>
&lt;p>Pero, como con cualquier hijo que crece, esto no termina aquí. Quedan pendientes las otras dos asignaturas que mencionábamos al principio —&lt;strong>separar a los inquilinos (multitenancy)&lt;/strong> y &lt;strong>controlar lo que gasta (costes)&lt;/strong>— y a Keycloak todavía le falta madurar en algún punto, como el soporte nativo de Resource Indicators. Iremos cubriéndolas en próximos artículos. Por ahora, nuestro MCP ya sabe decir quién es. Y eso, en seguridad, es el primer día de colegio.&lt;/p>
&lt;h2 id="fuentes">Fuentes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.keycloak.org/securing-apps/mcp-authz-server">Keycloak — Integrating with Model Context Protocol (MCP)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization">Model Context Protocol — Authorization&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.rfc-editor.org/rfc/rfc8707">RFC 8707 — Resource Indicators for OAuth 2.0&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.rfc-editor.org/rfc/rfc8414">RFC 8414 — OAuth 2.0 Authorization Server Metadata&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.rfc-editor.org/rfc/rfc7591">RFC 7591 — OAuth 2.0 Dynamic Client Registration Protocol&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>