This is an automated email from the ASF dual-hosted git repository.

amaechler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a3e4c3331e Support IAM auth for Kafka supervisors and lookups (#20471)
8a3e4c3331e is described below

commit 8a3e4c3331e2d0dc853751b111b493726ac4ecf9
Author: Andreas Maechler <[email protected]>
AuthorDate: Tue Oct 20 08:58:27 2026 -0600

    Support IAM auth for Kafka supervisors and lookups (#20471)
    
    Kafka supervisors and Kafka lookups could not authenticate to an
    IAM-enabled Amazon MSK cluster, because neither extension shipped the
    aws-msk-iam-auth plugin that registers the AWS_MSK_IAM SASL mechanism.
    
    Add aws-msk-iam-auth 2.3.9 to both extensions at runtime scope and
    exclude all of its transitives, so only the plugin jar ships. The AWS
    SDK v2 it needs is already in lib/ through druid-server and
    druid-aws-common, and extensions resolve it parent-first, so bundling
    another copy would put two SDK versions in one JVM. The plugin cannot
    live in core itself: its callback handlers implement Kafka client
    interfaces, and kafka-clients is bundled per extension.
    
    No code change is needed in either extension. Both pass consumer
    properties through unfiltered and set the thread context classloader to
    the extension classloader around consumer construction, which is where
    Kafka loads the login module and callback handler.
    
    The docs list the IAM actions a consumer needs and recommend
    awsAddDefaultProviders="false" with assume-role, so that a failed
    assumption does not fall back to the ambient identity.
    
    The tests build a consumer through each extension's own factory with
    the documented properties. Login happens in the constructor, so no
    broker is needed, and a missing plugin or SDK artifact fails the test.
---
 docs/ingestion/kafka-ingestion.md                  | 26 ++++++++
 docs/querying/kafka-extraction-namespace.md        |  4 ++
 extensions-core/kafka-extraction-namespace/pom.xml | 12 ++++
 .../druid/query/lookup/AwsMskIamAuthTest.java      | 72 +++++++++++++++++++++
 extensions-core/kafka-indexing-service/pom.xml     | 12 ++++
 .../druid/indexing/kafka/AwsMskIamAuthTest.java    | 73 ++++++++++++++++++++++
 licenses.yaml                                      | 24 +++++++
 pom.xml                                            |  6 ++
 8 files changed, 229 insertions(+)

diff --git a/docs/ingestion/kafka-ingestion.md 
b/docs/ingestion/kafka-ingestion.md
index 3b07df3495b..693377675f4 100644
--- a/docs/ingestion/kafka-ingestion.md
+++ b/docs/ingestion/kafka-ingestion.md
@@ -198,6 +198,32 @@ When you define the consumer properties in the supervisor 
spec, use the dynamic
 
 When connecting to Kafka, Druid replaces the environment variables with their 
corresponding values.
 
+##### Amazon MSK with IAM authentication
+
+The `druid-kafka-indexing-service` extension includes the [Amazon MSK Library 
for AWS Identity and Access 
Management](https://github.com/aws/aws-msk-iam-auth), so supervisors can read 
from an MSK cluster that uses [IAM access 
control](https://docs.aws.amazon.com/msk/latest/developerguide/iam-access-control.html)
 with nothing extra to install. Set these consumer properties, pointing 
`bootstrap.servers` at the cluster's [IAM bootstrap 
brokers](https://docs.aws.amazon.com/msk/latest/develop [...]
+
+```json
+"consumerProperties": {
+  "bootstrap.servers": "b-1.example.c1.kafka.us-east-1.amazonaws.com:9098",
+  "security.protocol": "SASL_SSL",
+  "sasl.mechanism": "AWS_MSK_IAM",
+  "sasl.jaas.config": "software.amazon.msk.auth.iam.IAMLoginModule required;",
+  "sasl.client.callback.handler.class": 
"software.amazon.msk.auth.iam.IAMClientCallbackHandler"
+}
+```
+
+Grant the IAM identity that Druid uses the actions AWS lists for [consuming 
data](https://docs.aws.amazon.com/msk/latest/developerguide/iam-access-control-use-cases.html):
 `kafka-cluster:Connect`, `kafka-cluster:DescribeTopic`, 
`kafka-cluster:ReadData`, `kafka-cluster:DescribeGroup`, and 
`kafka-cluster:AlterGroup`. [Scope each 
action](https://docs.aws.amazon.com/msk/latest/developerguide/kafka-actions.html)
 to the cluster, topic, or group it applies to.
+
+Druid finds credentials through the default AWS credentials provider chain. 
The Overlord and the Peons or Indexers that run Kafka tasks pick up an EC2 
instance profile, a Kubernetes service account role, or environment credentials 
without extra configuration.
+
+To assume a role, add `awsRoleArn` and `awsStsRegion` to `sasl.jaas.config`. 
Also add `awsAddDefaultProviders="false"`. Without it, a failed role assumption 
falls back to the default credentials, and Druid connects as that identity 
instead:
+
+```json
+"sasl.jaas.config": "software.amazon.msk.auth.iam.IAMLoginModule required 
awsRoleArn=\"arn:aws:iam::123456789012:role/msk-consumer\" 
awsStsRegion=\"us-east-1\" awsAddDefaultProviders=\"false\";"
+```
+
+Kafka lookups take the same properties in `kafkaProperties`. See [Kafka 
lookups](../querying/kafka-extraction-namespace.md#amazon-msk-with-iam-authentication).
+
 #### Idle configuration
 
 :::info
diff --git a/docs/querying/kafka-extraction-namespace.md 
b/docs/querying/kafka-extraction-namespace.md
index f5c91e7847d..c2abd040a96 100644
--- a/docs/querying/kafka-extraction-namespace.md
+++ b/docs/querying/kafka-extraction-namespace.md
@@ -75,6 +75,10 @@ This input topic would be consumed from the beginning, and 
result in a lookup na
 
 Now when a query uses this extraction namespace, the country codes can be 
mapped to the full country name at query time.
 
+## Amazon MSK with IAM authentication
+
+The `druid-kafka-extraction-namespace` extension includes the [Amazon MSK 
Library for AWS Identity and Access 
Management](https://github.com/aws/aws-msk-iam-auth), so a lookup can read from 
an MSK cluster that uses IAM access control. Put the properties from [Kafka 
ingestion](../ingestion/kafka-ingestion.md#amazon-msk-with-iam-authentication) 
in `kafkaProperties`. Every service that loads the lookup needs IAM credentials 
with the permissions listed there.
+
 ## Tombstones and Deleting Records
 
 The Kafka lookup extractor treats `null` Kafka messages as tombstones. This 
means that a record on the input topic with a `null` message payload on Kafka 
will remove the associated key from the lookup map, effectively deleting it.
diff --git a/extensions-core/kafka-extraction-namespace/pom.xml 
b/extensions-core/kafka-extraction-namespace/pom.xml
index bfb7ccb5c44..08cb3c73261 100644
--- a/extensions-core/kafka-extraction-namespace/pom.xml
+++ b/extensions-core/kafka-extraction-namespace/pom.xml
@@ -67,6 +67,18 @@
         </exclusion>
       </exclusions>
     </dependency>
+    <!-- Provides the AWS_MSK_IAM SASL mechanism; its AWS SDK comes from 
druid-aws-common, so no transitives ship. -->
+    <dependency>
+      <groupId>software.amazon.msk</groupId>
+      <artifactId>aws-msk-iam-auth</artifactId>
+      <scope>runtime</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>*</groupId>
+          <artifactId>*</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
     <dependency>
       <groupId>com.google.code.findbugs</groupId>
       <artifactId>jsr305</artifactId>
diff --git 
a/extensions-core/kafka-extraction-namespace/src/test/java/org/apache/druid/query/lookup/AwsMskIamAuthTest.java
 
b/extensions-core/kafka-extraction-namespace/src/test/java/org/apache/druid/query/lookup/AwsMskIamAuthTest.java
new file mode 100644
index 00000000000..8bf9fe4d3b7
--- /dev/null
+++ 
b/extensions-core/kafka-extraction-namespace/src/test/java/org/apache/druid/query/lookup/AwsMskIamAuthTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.query.lookup;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.security.sasl.Sasl;
+import java.util.Map;
+
+/**
+ * Checks that a lookup configured for {@code AWS_MSK_IAM} as documented logs 
in using only the AWS SDK on Druid's
+ * core classpath. Login happens in the consumer constructor, so no broker is 
needed.
+ */
+public class AwsMskIamAuthTest
+{
+  private static final String LOGIN_MODULE = 
"software.amazon.msk.auth.iam.IAMLoginModule";
+
+  @Test
+  public void testDefaultCredentials() throws Exception
+  {
+    createAndCloseConsumer(LOGIN_MODULE + " required;");
+
+    // Construction never connects, so separately check the mechanism the SASL 
handshake would look up.
+    Assertions.assertNotNull(
+        Sasl.createSaslClient(new String[]{"AWS_MSK_IAM"}, null, "kafka", 
"localhost", Map.of(), callbacks -> {}),
+        "AWS_MSK_IAM SASL mechanism is not registered"
+    );
+  }
+
+  /**
+   * Assume-role builds an {@code StsClient} at login, which reaches more of 
the SDK than the default chain.
+   */
+  @Test
+  public void testAssumeRole()
+  {
+    createAndCloseConsumer(
+        LOGIN_MODULE + " required 
awsRoleArn=\"arn:aws:iam::123456789012:role/msk-consumer\" 
awsStsRegion=\"us-east-1\""
+        + " awsAddDefaultProviders=\"false\";"
+    );
+  }
+
+  private static void createAndCloseConsumer(final String jaasConfig)
+  {
+    final Map<String, String> kafkaProperties = Map.of(
+        "bootstrap.servers", "localhost:9098",
+        "security.protocol", "SASL_SSL",
+        "sasl.mechanism", "AWS_MSK_IAM",
+        "sasl.jaas.config", jaasConfig,
+        "sasl.client.callback.handler.class", 
"software.amazon.msk.auth.iam.IAMClientCallbackHandler"
+    );
+
+    new KafkaLookupExtractorFactory(null, "lookup-topic", 
kafkaProperties).getConsumer().close();
+  }
+}
diff --git a/extensions-core/kafka-indexing-service/pom.xml 
b/extensions-core/kafka-indexing-service/pom.xml
index 03ddb2d7007..6b0b6a51641 100644
--- a/extensions-core/kafka-indexing-service/pom.xml
+++ b/extensions-core/kafka-indexing-service/pom.xml
@@ -67,6 +67,18 @@
         </exclusion>
       </exclusions>
     </dependency>
+    <!-- Provides the AWS_MSK_IAM SASL mechanism; its AWS SDK comes from 
druid-aws-common, so no transitives ship. -->
+    <dependency>
+      <groupId>software.amazon.msk</groupId>
+      <artifactId>aws-msk-iam-auth</artifactId>
+      <scope>runtime</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>*</groupId>
+          <artifactId>*</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
     <dependency>
       <groupId>com.google.code.findbugs</groupId>
       <artifactId>jsr305</artifactId>
diff --git 
a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/AwsMskIamAuthTest.java
 
b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/AwsMskIamAuthTest.java
new file mode 100644
index 00000000000..1b1d5ce02f1
--- /dev/null
+++ 
b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/AwsMskIamAuthTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.indexing.kafka;
+
+import org.apache.druid.jackson.DefaultObjectMapper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.security.sasl.Sasl;
+import java.util.Map;
+
+/**
+ * Checks that a consumer configured for {@code AWS_MSK_IAM} as documented 
logs in using only the AWS SDK on Druid's
+ * core classpath. Login happens in the consumer constructor, so no broker is 
needed.
+ */
+public class AwsMskIamAuthTest
+{
+  private static final String LOGIN_MODULE = 
"software.amazon.msk.auth.iam.IAMLoginModule";
+
+  @Test
+  public void testDefaultCredentials() throws Exception
+  {
+    createAndCloseConsumer(LOGIN_MODULE + " required;");
+
+    // Construction never connects, so separately check the mechanism the SASL 
handshake would look up.
+    Assertions.assertNotNull(
+        Sasl.createSaslClient(new String[]{"AWS_MSK_IAM"}, null, "kafka", 
"localhost", Map.of(), callbacks -> {}),
+        "AWS_MSK_IAM SASL mechanism is not registered"
+    );
+  }
+
+  /**
+   * Assume-role builds an {@code StsClient} at login, which reaches more of 
the SDK than the default chain.
+   */
+  @Test
+  public void testAssumeRole()
+  {
+    createAndCloseConsumer(
+        LOGIN_MODULE + " required 
awsRoleArn=\"arn:aws:iam::123456789012:role/msk-consumer\" 
awsStsRegion=\"us-east-1\""
+        + " awsAddDefaultProviders=\"false\";"
+    );
+  }
+
+  private static void createAndCloseConsumer(final String jaasConfig)
+  {
+    final Map<String, Object> consumerProperties = Map.of(
+        "bootstrap.servers", "localhost:9098",
+        "security.protocol", "SASL_SSL",
+        "sasl.mechanism", "AWS_MSK_IAM",
+        "sasl.jaas.config", jaasConfig,
+        "sasl.client.callback.handler.class", 
"software.amazon.msk.auth.iam.IAMClientCallbackHandler"
+    );
+
+    KafkaRecordSupplier.getKafkaConsumer(new DefaultObjectMapper(), 
consumerProperties, null).close();
+  }
+}
diff --git a/licenses.yaml b/licenses.yaml
index defdc07d745..837a55eeea5 100644
--- a/licenses.yaml
+++ b/licenses.yaml
@@ -3322,6 +3322,18 @@ notices:
 
 ---
 
+name: Amazon MSK Library for AWS Identity and Access Management
+license_category: binary
+module: extensions/druid-kafka-indexing-service
+license_name: Apache License version 2.0
+version: 2.3.9
+libraries:
+  - software.amazon.msk: aws-msk-iam-auth
+notice: |
+  Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+---
+
 
 name: Apache Parquet
 license_category: binary
@@ -4484,6 +4496,18 @@ notices:
 
 ---
 
+name: Amazon MSK Library for AWS Identity and Access Management
+license_category: binary
+module: extensions/druid-kafka-extraction-namespace
+license_name: Apache License version 2.0
+version: 2.3.9
+libraries:
+  - software.amazon.msk: aws-msk-iam-auth
+notice: |
+  Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+---
+
 name: Microsoft Azure SDK For Key Vault Core
 license_category: binary
 module: extensions/druid-azure-extensions
diff --git a/pom.xml b/pom.xml
index 725c05b60bf..86a74929a04 100644
--- a/pom.xml
+++ b/pom.xml
@@ -128,6 +128,7 @@
              It should be removed once those extensions are upgraded or 
dropped (see #19109). -->
         <aws.sdk.v1.version>1.12.797</aws.sdk.v1.version>
         <aws.sdk.v2.version>2.55.6</aws.sdk.v2.version>
+        <aws.msk.iam.auth.version>2.3.9</aws.msk.iam.auth.version>
         <caffeine.version>3.3.0</caffeine.version>
         <jacoco.version>0.8.15</jacoco.version>
         <testcontainers.version>2.0.5</testcontainers.version>
@@ -416,6 +417,11 @@
                 <artifactId>aws-crt-client</artifactId>
                 <version>${aws.sdk.v2.version}</version>
             </dependency>
+            <dependency>
+                <groupId>software.amazon.msk</groupId>
+                <artifactId>aws-msk-iam-auth</artifactId>
+                <version>${aws.msk.iam.auth.version}</version>
+            </dependency>
             <dependency>
                 <groupId>net.minidev</groupId>
                 <artifactId>json-smart</artifactId>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to