You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kafka.apache.org by mi...@apache.org on 2026/08/07 09:46:04 UTC

(kafka) branch trunk updated: KAFKA-20816: Add support for CIDR into Authorizer.authorizeByResourceType (#22883)

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

mimaison pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 9c943129ebb KAFKA-20816: Add support for CIDR into Authorizer.authorizeByResourceType (#22883)
9c943129ebb is described below

commit 9c943129ebb07a3a6b5d19f02a1c286c1fe8061b
Author: Maros Orsak <ma...@gmail.com>
AuthorDate: Fri Aug 7 11:45:57 2026 +0200

    KAFKA-20816: Add support for CIDR into Authorizer.authorizeByResourceType (#22883)
    
    This PR fixes a problem if someone sets up CIDR-based ACLs and an
    idempotent producer connects from an IP within the CIDR range, the
    `authorizeByResourceType` check would fail to match the CIDR ACL, and
    the producer would get `CLUSTER_AUTHORIZATION_FAILED`. It is a narrow
    edge case but its bug.
    
    Reviewers: Mickael Maison <mi...@gmail.com>, Manikumar Reddy
    <ma...@gmail.com>
    
    ---------
    
    Signed-off-by: see-quick <ma...@gmail.com>
---
 build.gradle                                       |  9 +++----
 checkstyle/import-control-metadata.xml             |  1 -
 checkstyle/import-control.xml                      |  7 ++++++
 .../apache/kafka/server/authorizer/Authorizer.java |  4 +++-
 .../server/authorizer/internals}/CidrUtils.java    |  2 +-
 .../authorizer/internals}/CidrUtilsTest.java       |  6 ++---
 .../apache/kafka/controller/AclControlManager.java |  2 +-
 .../authorizer/StandardAuthorizerData.java         |  1 +
 .../authorizer/StandardAuthorizerTest.java         | 28 ++++++++++++++++++++++
 9 files changed, 49 insertions(+), 11 deletions(-)

diff --git a/build.gradle b/build.gradle
index 64831c79b0b..10183c8a955 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1498,10 +1498,6 @@ project(':metadata') {
     implementation project(':server-common')
     implementation project(':clients')
     implementation project(':raft')
-    // commons-io is a transitive dependency not needed by SubnetUtils/SubnetUtils6
-    implementation(libs.commonsNet) {
-      exclude group: 'commons-io', module: 'commons-io'
-    }
     implementation libs.jacksonDatabind
     implementation libs.metrics
     implementation libs.slf4jApi
@@ -2024,6 +2020,10 @@ project(':clients') {
     implementation libs.opentelemetryProto
     implementation libs.protobuf
     implementation libs.slf4jApi
+    // commons-io is a transitive dependency not needed by SubnetUtils/SubnetUtils6
+    implementation(libs.commonsNet) {
+      exclude group: 'commons-io', module: 'commons-io'
+    }
 
     // libraries which should be added as runtime dependencies in generated pom.xml should be defined here:
     shadowed libs.zstd
@@ -2090,6 +2090,7 @@ project(':clients') {
     // KIP-714: move shaded dependencies to a shaded location
     relocate('io.opentelemetry.proto', 'org.apache.kafka.shaded.io.opentelemetry.proto')
     relocate('com.google.protobuf', 'org.apache.kafka.shaded.com.google.protobuf')
+    relocate('org.apache.commons.net', 'org.apache.kafka.shaded.org.apache.commons.net')
 
     // dependencies excluded from the final jar, since they are declared as runtime dependencies
     dependencies {
diff --git a/checkstyle/import-control-metadata.xml b/checkstyle/import-control-metadata.xml
index 1ee3207de44..cdc73dcf65d 100644
--- a/checkstyle/import-control-metadata.xml
+++ b/checkstyle/import-control-metadata.xml
@@ -168,7 +168,6 @@
             <allow pkg="org.apache.kafka.common.metrics" />
             <allow pkg="org.apache.kafka.common.metrics.internals" />
             <allow pkg="org.apache.kafka.common.metrics.stats" />
-            <allow pkg="org.apache.commons.net.util" />
         </subpackage>
         <subpackage name="bootstrap">
             <allow pkg="org.apache.kafka.snapshot" />
diff --git a/checkstyle/import-control.xml b/checkstyle/import-control.xml
index 36dde497ff7..d0d85a607f9 100644
--- a/checkstyle/import-control.xml
+++ b/checkstyle/import-control.xml
@@ -257,6 +257,13 @@
     <!-- This is required to make AlterConfigPolicyTest work. -->
     <allow pkg="org.apache.kafka.server.policy" />
 
+    <subpackage name="authorizer">
+      <allow pkg="org.apache.kafka.server.authorizer" />
+      <subpackage name="internals">
+        <allow pkg="org.apache.commons.net.util" />
+      </subpackage>
+    </subpackage>
+
     <subpackage name="telemetry">
       <allow class="org.apache.kafka.server.authorizer.AuthorizableRequestContext" />
     </subpackage>
diff --git a/clients/src/main/java/org/apache/kafka/server/authorizer/Authorizer.java b/clients/src/main/java/org/apache/kafka/server/authorizer/Authorizer.java
index db61421de94..34a6a44d0b5 100644
--- a/clients/src/main/java/org/apache/kafka/server/authorizer/Authorizer.java
+++ b/clients/src/main/java/org/apache/kafka/server/authorizer/Authorizer.java
@@ -31,6 +31,7 @@ import org.apache.kafka.common.resource.ResourcePatternFilter;
 import org.apache.kafka.common.resource.ResourceType;
 import org.apache.kafka.common.security.auth.KafkaPrincipal;
 import org.apache.kafka.common.utils.internals.SecurityUtils;
+import org.apache.kafka.server.authorizer.internals.CidrUtils;
 
 import java.io.Closeable;
 import java.util.Collections;
@@ -221,7 +222,8 @@ public interface Authorizer extends Configurable, Closeable {
         String hostAddr = requestContext.clientAddress().getHostAddress();
 
         for (AclBinding binding : acls(aclFilter)) {
-            if (!binding.entry().host().equals(hostAddr) && !binding.entry().host().equals("*"))
+            String aclHost = binding.entry().host();
+            if (!aclHost.equals(hostAddr) && !aclHost.equals("*") && !CidrUtils.isInRange(hostAddr, aclHost))
                 continue;
 
             if (!SecurityUtils.parseKafkaPrincipal(binding.entry().principal()).equals(principal)
diff --git a/metadata/src/main/java/org/apache/kafka/metadata/authorizer/CidrUtils.java b/clients/src/main/java/org/apache/kafka/server/authorizer/internals/CidrUtils.java
similarity index 97%
rename from metadata/src/main/java/org/apache/kafka/metadata/authorizer/CidrUtils.java
rename to clients/src/main/java/org/apache/kafka/server/authorizer/internals/CidrUtils.java
index 318b2c9bff0..e909a777742 100644
--- a/metadata/src/main/java/org/apache/kafka/metadata/authorizer/CidrUtils.java
+++ b/clients/src/main/java/org/apache/kafka/server/authorizer/internals/CidrUtils.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.kafka.metadata.authorizer;
+package org.apache.kafka.server.authorizer.internals;
 
 import org.apache.commons.net.util.SubnetUtils;
 import org.apache.commons.net.util.SubnetUtils6;
diff --git a/metadata/src/test/java/org/apache/kafka/metadata/authorizer/CidrUtilsTest.java b/clients/src/test/java/org/apache/kafka/server/authorizer/internals/CidrUtilsTest.java
similarity index 95%
rename from metadata/src/test/java/org/apache/kafka/metadata/authorizer/CidrUtilsTest.java
rename to clients/src/test/java/org/apache/kafka/server/authorizer/internals/CidrUtilsTest.java
index f648944f0ba..46af4304c82 100644
--- a/metadata/src/test/java/org/apache/kafka/metadata/authorizer/CidrUtilsTest.java
+++ b/clients/src/test/java/org/apache/kafka/server/authorizer/internals/CidrUtilsTest.java
@@ -15,15 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.kafka.metadata.authorizer;
+package org.apache.kafka.server.authorizer.internals;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
 import java.net.InetAddress;
 
-import static org.apache.kafka.metadata.authorizer.CidrUtils.isInRange;
-import static org.apache.kafka.metadata.authorizer.CidrUtils.validate;
+import static org.apache.kafka.server.authorizer.internals.CidrUtils.isInRange;
+import static org.apache.kafka.server.authorizer.internals.CidrUtils.validate;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
diff --git a/metadata/src/main/java/org/apache/kafka/controller/AclControlManager.java b/metadata/src/main/java/org/apache/kafka/controller/AclControlManager.java
index 22808ced18d..3553ba6f7a3 100644
--- a/metadata/src/main/java/org/apache/kafka/controller/AclControlManager.java
+++ b/metadata/src/main/java/org/apache/kafka/controller/AclControlManager.java
@@ -28,12 +28,12 @@ import org.apache.kafka.common.metadata.AccessControlEntryRecord;
 import org.apache.kafka.common.metadata.RemoveAccessControlEntryRecord;
 import org.apache.kafka.common.requests.ApiError;
 import org.apache.kafka.common.utils.internals.LogContext;
-import org.apache.kafka.metadata.authorizer.CidrUtils;
 import org.apache.kafka.metadata.authorizer.StandardAcl;
 import org.apache.kafka.metadata.authorizer.StandardAclWithId;
 import org.apache.kafka.server.authorizer.AclCreateResult;
 import org.apache.kafka.server.authorizer.AclDeleteResult;
 import org.apache.kafka.server.authorizer.AclDeleteResult.AclBindingDeleteResult;
+import org.apache.kafka.server.authorizer.internals.CidrUtils;
 import org.apache.kafka.server.common.ApiMessageAndVersion;
 import org.apache.kafka.server.common.MetadataVersion;
 import org.apache.kafka.server.mutable.BoundedList;
diff --git a/metadata/src/main/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerData.java b/metadata/src/main/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerData.java
index f56c39dc379..bb17549de83 100644
--- a/metadata/src/main/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerData.java
+++ b/metadata/src/main/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerData.java
@@ -32,6 +32,7 @@ import org.apache.kafka.common.utils.internals.SecurityUtils;
 import org.apache.kafka.server.authorizer.Action;
 import org.apache.kafka.server.authorizer.AuthorizableRequestContext;
 import org.apache.kafka.server.authorizer.AuthorizationResult;
+import org.apache.kafka.server.authorizer.internals.CidrUtils;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/metadata/src/test/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerTest.java b/metadata/src/test/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerTest.java
index 35e49bb85f5..c49f08f627b 100644
--- a/metadata/src/test/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerTest.java
+++ b/metadata/src/test/java/org/apache/kafka/metadata/authorizer/StandardAuthorizerTest.java
@@ -875,4 +875,32 @@ public class StandardAuthorizerTest {
             authorizer.authorize(aliceInBroadOnly, List.of(newAction(READ, TOPIC, "foo"))),
             "Client in only the broad ALLOW /8 should be allowed");
     }
+
+    @Test
+    public void testAuthorizeByResourceTypeWithCidrHost() throws Exception {
+        StandardAuthorizer authorizer = createAndInitializeStandardAuthorizer();
+
+        StandardAclWithId cidrAcl = withId(new StandardAcl(
+            TOPIC, "test-topic", LITERAL, "User:bob",
+            "192.168.1.0/24", WRITE, ALLOW));
+        authorizer.addAcl(cidrAcl.id(), cidrAcl.acl());
+
+        // Client within the CIDR range should be allowed
+        AuthorizableRequestContext bobInRange = new MockAuthorizableRequestContext.Builder()
+            .setPrincipal(new KafkaPrincipal(USER_TYPE, "bob"))
+            .setClientAddress(InetAddress.getByName("192.168.1.50"))
+            .build();
+        assertEquals(ALLOWED,
+            authorizer.authorizeByResourceType(bobInRange, WRITE, TOPIC),
+            "Client within CIDR range should be allowed by authorizeByResourceType");
+
+        // Client outside the CIDR range should be denied
+        AuthorizableRequestContext bobOutOfRange = new MockAuthorizableRequestContext.Builder()
+            .setPrincipal(new KafkaPrincipal(USER_TYPE, "bob"))
+            .setClientAddress(InetAddress.getByName("10.0.0.1"))
+            .build();
+        assertEquals(DENIED,
+            authorizer.authorizeByResourceType(bobOutOfRange, WRITE, TOPIC),
+            "Client outside CIDR range should be denied by authorizeByResourceType");
+    }
 }