Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/oriolrius/kafka-keycloak-oauth
Seguridad de Infraestructura en la NubeAnálisis de VulnerabilidadesAuditoría de ConfiguraciónDevSecOpsAutenticaciónAprendizaje y Educación
GitHuboriolrius/kafka-keycloak-oauth

kafka-keycloak-oauth

Apache Kafka 4.1.0 (KRaft) con autenticación OAuth2 de Keycloak usando Strimzi: evita la restricción de la lista de permitidos de URL de CVE-2025-27817

Ver Repositorio
53hace 10 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Apache Kafka 4.1.0 con autenticación OAuth2 de Keycloak

Apache Kafka 4.1.0 listo para producción (modo KRaft) con autenticación OAuth2/OIDC de Keycloak 26.1.1 utilizando la imagen de Kafka de Strimzi.

Por qué este proyecto vs kafka-oauth-keycloak-tls-demo

Esta es una evolución del POC anterior con mejoras significativas:

  • Strimzi OAuth 0.17.0 (vs 1.0.0) - versión de producción estable incluida en la imagen de Strimzi Kafka 0.48.0
  • Sin necesidad de build Docker personalizado - utiliza la imagen oficial de Strimzi con OAuth preinstalado, elimina la complejidad del Dockerfile
  • Conciencia de CVE-2025-27817 - documenta la restricción de la lista de URLs permitidas y por qué Strimzi OAuth la evita
  • Arquitectura simplificada - modo combinado KRaft único (broker+controller), no arquitectura dividida
  • Enfoque en cliente librdkafka - probado con confluent-kafka-python (funciona sin problemas de lista de URLs permitidas), no con clientes Java nativos
  • Documentación técnica completa - lista de verificación de producción, solución de problemas, ajuste de rendimiento, detalles de mapeo de principal
  • Gestión de certificados más limpia - certificados de ejemplo incluidos para pruebas inmediatas
  • Configuración automatizada de Keycloak - creación de realm/cliente/mapper mediante scripts con configuración de audiencia
  • Suite de pruebas Python funcional - valida la entrega de mensajes OAuth de extremo a extremo
  • Manejo explícito de URL del emisor - documenta la dualidad de URL interna vs externa para el endpoint de token vs validación del emisor
  • Arquitectura

    • Distribución de Kafka: Imagen de Strimzi Kafka 0.48.0 (incluye Apache Kafka 4.1.0 + Strimzi OAuth 0.17.0 preinstalado)
    • Versión de Kafka: Apache Kafka 4.1.0 (KRaft combinado broker+controller)
    • Biblioteca OAuth: Strimzi Kafka OAuth 0.17.0 (incluida en la imagen, evita la restricción de lista de URLs permitidas de CVE-2025-27817)
    • Proveedor OAuth: Keycloak 26.1.1
    • Seguridad: SASL_SSL (OAuth) para clientes externos, PLAINTEXT para inter-broker, SSL con CA autofirmada

    Contexto de CVE-2025-27817

    Apache Kafka 4.0.0+ introdujo una lista de URLs permitidas (org.apache.kafka.sasl.oauthbearer.allowed.urls) como propiedad del sistema JVM para corregir la vulnerabilidad SSRF/lectura arbitraria de archivos. Esto rompe el uso estándar de OAuth en clientes nativos de Apache Kafka.

    Solución: La biblioteca Strimzi Kafka OAuth no implementa esta restricción, lo que permite la funcionalidad OAuth con Kafka 4.1.0.

    Requisitos previos

    • Docker Compose
    • Python 3.x con uv (para pruebas)
    • OpenSSL (para generación de certificados)

    Inicio rápido

    root@kitploit:~
    # Generar certificados SSL
    cd kafka-security
    ./generate-certs.sh
    cd ..
    
    # Iniciar servicios
    docker compose up -d
    
    # Verificar Keycloak
    curl http://localhost:8080/health/ready
    
    # Configurar realm y clientes de Keycloak
    ./scripts/setup-keycloak.sh
    
    # Probar productor OAuth
    source ~/.venv/bin/activate
    uv pip install confluent-kafka
    python tests/quick_test.py
    

    Topología de red

    root@kitploit:~
    keycloak:8080 (HTTP) ←→ kafka-broker:9093 (SASL_SSL/OAuth)
                          ↔ kafka-broker:19092 (PLAINTEXT/inter-broker)
                          ↔ kafka-broker:29093 (PLAINTEXT/controlador KRaft)
    

    Configuración SSL

    Estructura de CA

    • CA raíz: kafka-security/ca-cert + ca-key
    • Almacén de claves del broker: kafka-security/broker/kafka.server.keystore.jks (contiene certificado del servidor + clave privada)
    • Almacén de confianza del broker: kafka-security/broker/kafka.server.truststore.jks (contiene certificado de CA)
    • Contraseña: changeit (todos los almacenes de claves/confianza)

    Detalles del certificado

    root@kitploit:~
    # Certificado del broker
    CN=kafka-broker
    SAN=DNS:kafka-broker,DNS:localhost,IP:127.0.0.1
    
    # Validez: 3650 días
    # Algoritmo de clave: RSA 2048 bits
    # Algoritmo de firma: SHA256withRSA
    

    Configuración OAuth de Keycloak

    Realm: kafka-realm

    Clientes

    kafka-broker (confidencial)

    • ID de cliente: kafka-broker
    • Secreto de cliente: Generado automáticamente por setup-keycloak.sh
    • Propósito: Autenticación OAuth inter-broker
    • Mappers:
      • Mapper de audiencia: añade kafka-broker a la reclamación aud del JWT
      • Mapper de nombre de usuario: incluye preferred_username en el token

    kafka-producer (confidencial)

    • ID de cliente: kafka-producer
    • Secreto de cliente: Generado automáticamente
    • Propósito: Clientes productores externos
    • Grant: client_credentials
    • Mappers: Igual que kafka-broker

    kafka-consumer (confidencial)

    • ID de cliente: kafka-consumer
    • Secreto de cliente: Generado automáticamente
    • Propósito: Clientes consumidores externos
    • Grant: client_credentials
    • Mappers: Igual que kafka-broker

    Endpoint de token

    root@kitploit:~
    POST http://localhost:8080/realms/kafka-realm/protocol/openid-connect/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=client_credentials
    &client_id=kafka-producer
    &client_secret=<secreto>
    &scope=profile email
    

    Estructura del token JWT

    root@kitploit:~
    {
      "aud": ["kafka-broker", "account"],
      "iss": "http://localhost:8080/realms/kafka-realm",
      "azp": "kafka-producer",
      "preferred_username": "service-account-kafka-producer",
      "scope": "profile email"
    }
    

    Configuración de Kafka

    Modo KRaft (kraft-config.properties)

    root@kitploit:~
    # Identidad del nodo
    node.id=1
    process.roles=broker,controller
    controller.quorum.voters=1@kafka-broker:29093
    
    # Listeners
    listeners=SASL_SSL://0.0.0.0:9093,PLAINTEXT://0.0.0.0:19092,CONTROLLER://0.0.0.0:29093
    advertised.listeners=SASL_SSL://localhost:9093,PLAINTEXT://kafka-broker:19092
    listener.security.protocol.map=SASL_SSL:SASL_SSL,PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
    inter.broker.listener.name=PLAINTEXT
    controller.listener.names=CONTROLLER
    
    # Mecanismo SASL
    sasl.enabled.mechanisms=OAUTHBEARER
    
    # Handlers OAuth de Strimzi (por listener para SASL_SSL)
    listener.name.sasl_ssl.oauthbearer.sasl.login.callback.handler.class=io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler
    listener.name.sasl_ssl.oauthbearer.sasl.server.callback.handler.class=io.strimzi.kafka.oauth.server.JaasServerOauthValidatorCallbackHandler
    
    # Configuración OAuth mediante JAAS
    listener.name.sasl_ssl.oauthbearer.sasl.jaas.config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required \
      oauth.client.id="kafka-broker" \
      oauth.client.secret="<secreto>" \
      oauth.token.endpoint.uri="http://keycloak:8080/realms/kafka-realm/protocol/openid-connect/token" \
      oauth.valid.issuer.uri="http://localhost:8080/realms/kafka-realm" \
      oauth.jwks.endpoint.uri="http://keycloak:8080/realms/kafka-realm/protocol/openid-connect/certs" \
      oauth.username.claim="preferred_username";
    

    Parámetros clave de Strimzi OAuth

    • oauth.client.id: Identificador de cliente para la obtención de tokens
    • oauth.client.secret: Secreto de cliente para la obtención de tokens
    • oauth.token.endpoint.uri: Endpoint de token de Keycloak (el broker usa el nombre de host interno keycloak:8080)
    • oauth.valid.issuer.uri: Emisor JWT esperado (debe coincidir con la reclamación iss del token, usa localhost:8080 externo)
    • oauth.jwks.endpoint.uri: Endpoint JWKS para validación de firma JWT
    • oauth.username.claim: Reclamación JWT para extracción del principal

    Autorización

    root@kitploit:~
    authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
    super.users=User:kafka-broker;User:ANONYMOUS
    allow.everyone.if.no.acl.found=true
    

    Nota: Actualmente permisivo para pruebas. La producción debería usar ACLs.

    Configuración del cliente

    Productor Python (confluent-kafka)

    root@kitploit:~
    from confluent_kafka import Producer
    
    conf = {
        'bootstrap.servers': 'localhost:9093',
        'security.protocol': 'SASL_SSL',
        'sasl.mechanisms': 'OAUTHBEARER',
        'sasl.oauthbearer.method': 'oidc',
        'sasl.oauthbearer.client.id': 'kafka-producer',
        'sasl.oauthbearer.client.secret': '<secreto>',
        'sasl.oauthbearer.token.endpoint.url': 'http://localhost:8080/realms/kafka-realm/protocol/openid-connect/token',
        'ssl.ca.location': 'kafka-security/ca-cert',
        'ssl.endpoint.identification.algorithm': 'none',
    }
    
    producer = Producer(conf)
    producer.produce('topic', b'message')
    producer.flush()
    

    Consumidor Python (confluent-kafka)

    root@kitploit:~
    from confluent_kafka import Consumer
    
    conf = {
        'bootstrap.servers': 'localhost:9093',
        'group.id': 'test-group',
        'security.protocol': 'SASL_SSL',
        'sasl.mechanisms': 'OAUTHBEARER',
        'sasl.oauthbearer.method': 'oidc',
        'sasl.oauthbearer.client.id': 'kafka-consumer',
        'sasl.oauthbearer.client.secret': '<secreto>',
        'sasl.oauthbearer.token.endpoint.url': 'http://localhost:8080/realms/kafka-realm/protocol/openid-connect/token',
        'ssl.ca.location': 'kafka-security/ca-cert',
        'ssl.endpoint.identification.algorithm': 'none',
        'auto.offset.reset': 'earliest',
    }
    
    consumer = Consumer(conf)
    consumer.subscribe(['topic'])
    while True:
        msg = consumer.poll(1.0)
        if msg: print(msg.value())
    

    Por qué funciona librdkafka

    confluent-kafka-python utiliza librdkafka (biblioteca C) que implementa OAuth mediante sasl.oauthbearer.method=oidc. Esta implementación no verifica la propiedad del sistema org.apache.kafka.sasl.oauthbearer.allowed.urls que bloquea a los clientes Java nativos de Apache Kafka.

    Solución de problemas

    Verificar token OAuth

    root@kitploit:~
    TOKEN=$(curl -s -X POST http://localhost:8080/realms/kafka-realm/protocol/openid-connect/token \
      -d "grant_type=client_credentials" \
      -d "client_id=kafka-producer" \
      -d "client_secret=<secreto>" | jq -r .access_token)
    
    echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq .
    

    Reclamaciones esperadas:

    root@kitploit:~
    {
      "aud": ["kafka-broker", "account"],
      "iss": "http://localhost:8080/realms/kafka-realm",
      "azp": "kafka-producer",
      "preferred_username": "service-account-kafka-producer"
    }
    

    Verificar registros OAuth del broker

    root@kitploit:~
    docker logs kafka-broker 2>&1 | grep -E "Strimzi|JWTSignatureValidator|OAUTHBEARER"
    

    Esperado:

    root@kitploit:~
    [io.strimzi.kafka.oauth.validator.JWTSignatureValidator] JWKS keys change detected
    

    Verificar listeners del broker

    root@kitploit:~
    docker exec kafka-broker netstat -tlnp | grep java
    

    Esperado:

    root@kitploit:~
    tcp6  0.0.0.0:9093   LISTEN  (SASL_SSL)
    tcp6  0.0.0.0:19092  LISTEN  (PLAINTEXT)
    tcp6  0.0.0.0:29093  LISTEN  (CONTROLLER)
    

    Verificar metadatos KRaft

    root@kitploit:~
    docker exec kafka-broker cat /var/lib/kafka/data/meta.properties
    

    Esperado:

    root@kitploit:~
    version=1
    cluster.id=kafka-cluster-01
    node.id=1
    

    Problemas comunes

    Problema: {"status":"invalid_token"}

    • Causa: Fallo en la validación de firma JWT
    • Solución: Verificar que oauth.jwks.endpoint.uri sea accesible desde el contenedor del broker
    • Comprobación: docker exec kafka-broker curl http://keycloak:8080/realms/kafka-realm/protocol/openid-connect/certs

    Problema: Token audience mismatch

    • Causa: La reclamación aud del JWT no contiene kafka-broker
    • Solución: Ejecutar ./scripts/setup-keycloak.sh para añadir el mapper de audiencia
    • Verificación: Decodificar el token y comprobar que la reclamación aud incluye kafka-broker

    Problema: Token issuer mismatch

    • Causa: El iss del JWT no coincide con oauth.valid.issuer.uri
    • Solución: Asegurar que oauth.valid.issuer.uri=http://localhost:8080/realms/kafka-realm (nombre de host externo)
    • Nota: El broker usa http://keycloak:8080 para el endpoint de token pero valida contra el emisor http://localhost:8080

    Problema: Los clientes Java nativos de Kafka fallan con error de lista de URLs permitidas

    • Causa: Corrección de CVE-2025-27817 en Apache Kafka 4.1.0
    • Solución: Usar clientes basados en librdkafka (confluent-kafka-python) o Strimzi OAuth en el lado del broker (ya configurado)

    Ajuste de rendimiento

    Renovación de tokens

    Los tokens JWT de Keycloak tienen una caducidad de 5 minutos. Strimzi OAuth maneja la renovación automáticamente:

    • oauth.refresh.token: No utilizado (grant client_credentials)
    • Token almacenado en caché y renovado 30 segundos antes de la caducidad

    Caché JWKS

    root@kitploit:~
    sasl.oauthbearer.jwks.endpoint.refresh.ms=3600000  # 1 hora
    sasl.oauthbearer.jwks.endpoint.retry.backoff.ms=100
    sasl.oauthbearer.jwks.endpoint.retry.backoff.max.ms=10000
    

    Configuración de conexiones

    root@kitploit:~
    connections.max.idle.ms=600000
    connection.failed.authentication.delay.ms=1000
    

    Lista de verificación de producción

    • Reemplazar certificados autofirmados por certificados firmados por CA
    • Actualizar ssl.endpoint.identification.algorithm=https (eliminar none)
    • Configurar ACLs adecuadas (eliminar allow.everyone.if.no.acl.found=true)
    • Configurar ACLs:
      root@kitploit:~
      kafka-acls --bootstrap-server localhost:9093 \
        --command-config admin.properties \
        --add --allow-principal User:kafka-producer \
        --operation Write --topic '*'
      
    • Rotar secretos de clientes de Keycloak
    • Habilitar HTTPS en Keycloak
    • Actualizar oauth.token.endpoint.uri y oauth.jwks.endpoint.uri a URLs HTTPS
    • Configurar monitoreo de Kafka (JMX, Prometheus)
    • Configurar agregación de registros para pista de auditoría OAuth
    • Probar escenarios de conmutación por error
    • Documentar procedimientos de rotación de secretos
    • Habilitar federación de usuarios de Keycloak (LDAP/AD) si es necesario

    Estructura de directorios

    root@kitploit:~
    .
    ├── docker-compose.yml              # Orquestación
    ├── .env                            # Secretos (gitignored)
    ├── kafka-config/
    │   ├── kraft-config.properties     # Configuración del broker Kafka
    │   ├── producer.properties         # Configuración OAuth del productor (para herramientas CLI)
    │   └── consumer.properties         # Configuración OAuth del consumidor (para herramientas CLI)
    ├── kafka-security/
    │   ├── generate-certs.sh           # Generador de certificados SSL
    │   ├── ca-cert                     # Certificado de CA raíz
    │   ├── ca-key                      # Clave privada de CA raíz
    │   └── broker/
    │       ├── kafka.server.keystore.jks
    │       └── kafka.server.truststore.jks
    ├── scripts/
    │   └── setup-keycloak.sh           # Configuración de realm/clientes de Keycloak
    └── tests/
        └── quick_test.py               # Prueba de validación OAuth
    
    

    Notas técnicas

    Por qué la imagen de Strimzi Kafka en lugar de la imagen oficial de Apache Kafka

    La imagen de Strimzi Kafka (quay.io/strimzi/kafka:0.48.0-kafka-4.1.0) se utiliza en lugar de la imagen oficial de Apache Kafka porque:

    1. Soporte OAuth incluido: Incluye la biblioteca Strimzi OAuth 0.17.0 preinstalada (clases: io.strimzi.kafka.oauth.*)
    2. Evasión de CVE-2025-27817: La biblioteca Strimzi OAuth no implementa la restricción de lista de URLs permitidas que rompe el OAuth nativo de Kafka
    3. Listo para producción: Probado en entornos Kubernetes mediante Strimzi Operator
    4. Imagen única: No es necesario descargar y montar manualmente los archivos JAR de OAuth

    Desglose de la imagen:

    • Strimzi Kafka 0.48.0 = Versión/lanzamiento de la imagen Docker
    • Apache Kafka 4.1.0 = Versión del broker Kafka incluida
    • Strimzi OAuth 0.17.0 = Versión de la biblioteca OAuth incluida

    Dualidad de URL del emisor

    La configuración del broker tiene dos URLs:

    • oauth.token.endpoint.uri=http://keycloak:8080/... (red Docker interna)
    • oauth.valid.issuer.uri=http://localhost:8080/... (externa, coincide con la reclamación iss del JWT)

    Esto se debe a que:

    • El broker obtiene tokens usando el nombre DNS interno
    • Keycloak emite tokens con la URL del emisor externo (configurada en los ajustes del realm)
    • La validación JWT requiere coincidencia exacta del emisor

    Mapeo de principal

    El broker extrae el principal de la reclamación preferred_username del JWT:

    root@kitploit:~
    service-account-kafka-producer → User:service-account-kafka-producer
    

    Las ACLs hacen referencia a este principal para la autorización.

    Compatibilidad de versiones

    ComponenteVersiónNotas
    Apache Kafka4.1.0Modo KRaft (sin ZooKeeper)
    Imagen de Strimzi Kafka0.48.0Imagen Docker: quay.io/strimzi/kafka:0.48.0-kafka-4.1.0
    Biblioteca Strimzi OAuth0.17.0Preinstalada en la imagen de Strimzi Kafka 0.48.0
    Keycloak26.1.1Última LTS
    librdkafka2.12.0+Soporte OAuth OIDC
    confluent-kafka-python2.12.0+Coincide con la versión de librdkafka

    Referencias

    • Strimzi Kafka OAuth
    • Seguridad de Apache Kafka
    • Keycloak OIDC
    • CVE-2025-27817
    • Modo KRaft
    Descargar herramienta