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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new ef5567547ed branch-4.1: [fix](fe) Mask Kafka routine load sensitive 
properties #64786 (#65629)
ef5567547ed is described below

commit ef5567547ede5d9ac2cffe100c2e4f10556efb9f
Author: Refrain <[email protected]>
AuthorDate: Thu Jul 16 11:11:11 2026 +0800

    branch-4.1: [fix](fe) Mask Kafka routine load sensitive properties #64786 
(#65629)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #64786
    
    Problem Summary: Backport Kafka routine-load sensitive-property masking
    from #64786 to `branch-4.1`. The display paths for `SHOW ROUTINE LOAD`,
    `SHOW CREATE ROUTINE LOAD`, and `information_schema.routine_load_jobs`
    mask credentials while the internal Kafka client properties remain
    unchanged.
    
    Source merge commit: `c4ddfbaa66a6039a0383fd3fa1fb8c549bfa4381`.
    
    ### Conflict resolution
    
    - `branch-4.1` does not contain Kinesis routine load. Kept
    `KinesisRoutineLoadJob`, its unit test, and its regression suite absent
    instead of resurrecting a component unavailable on the target branch.
    - Preserved the branch's JMockit-based `KafkaRoutineLoadJobTest` and
    inserted only the source PR's sensitive-property masking test.
    - Replaced master's `DatasourcePrintableMap.SENSITIVE_KEY` with the
    equivalent `PrintableMap.SENSITIVE_KEY` registry available on
    branch-4.1.
    - Retained the branch's existing Kafka configuration constants for SASL
    JAAS and AWS access-key matching.
    - The Kafka regression test merged against the target suite without
    additional semantic changes.
    
    ### Release note
    
    Mask sensitive Kafka routine load custom properties in metadata display
    output.
    
    ### Check List (For Author)
    
    - Test: No need to test (one-time Agent conflict-resolution workflow;
    build and test execution were intentionally excluded)
        - `git diff HEAD^ HEAD --check`
        - Verified no conflict markers or unmerged index entries remain
    - Behavior changed: Yes (Kafka routine load credentials are masked in
    external metadata displays)
    - Does this need documentation: No
---
 .../load/routineload/KafkaRoutineLoadJob.java      | 33 +++++++--
 .../load/routineload/KafkaRoutineLoadJobTest.java  | 81 ++++++++++++++++++++++
 .../routine_load/test_show_routine_load.groovy     | 41 +++++++++++
 3 files changed, 151 insertions(+), 4 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/load/routineload/KafkaRoutineLoadJob.java
 
b/fe/fe-core/src/main/java/org/apache/doris/load/routineload/KafkaRoutineLoadJob.java
index 36e09f01e32..cb563e725db 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/load/routineload/KafkaRoutineLoadJob.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/load/routineload/KafkaRoutineLoadJob.java
@@ -34,6 +34,7 @@ import org.apache.doris.common.util.DebugPointUtil;
 import org.apache.doris.common.util.DebugUtil;
 import org.apache.doris.common.util.LogBuilder;
 import org.apache.doris.common.util.LogKey;
+import org.apache.doris.common.util.PrintableMap;
 import org.apache.doris.common.util.SmallFileMgr;
 import org.apache.doris.common.util.SmallFileMgr.SmallFile;
 import org.apache.doris.common.util.TimeUtils;
@@ -75,6 +76,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.TimeZone;
 import java.util.UUID;
@@ -95,6 +97,7 @@ public class KafkaRoutineLoadJob extends RoutineLoadJob {
     private static final String READ_COMMITTED_ZERO_ROWS_WITH_LAG_MESSAGE = 
"Kafka routine load consumed 0 rows "
             + "while lag is still positive under 
isolation.level=read_committed. If the upstream producer uses "
             + "Kafka transactions, some records may be in uncommitted 
transactions and are not visible yet.";
+    private static final String SENSITIVE_PROPERTY_MASK = "******";
 
     @SerializedName("bl")
     private String brokerList;
@@ -710,7 +713,31 @@ public class KafkaRoutineLoadJob extends RoutineLoadJob {
     @Override
     public String customPropertiesJsonToString() {
         Gson gson = new GsonBuilder().disableHtmlEscaping().create();
-        return gson.toJson(customProperties);
+        return gson.toJson(getMaskedCustomProperties(""));
+    }
+
+    private Map<String, String> getMaskedCustomProperties(String keyPrefix) {
+        Map<String, String> maskedProperties = new HashMap<>();
+        customProperties.forEach((key, value) -> {
+            String lowerKey = key.toLowerCase(Locale.ROOT);
+            boolean sensitive = 
KafkaConfiguration.SASL_JAAS_CONFIG.equalsIgnoreCase(key)
+                    || KafkaConfiguration.AWS_ACCESS_KEY.equalsIgnoreCase(key)
+                    || PrintableMap.SENSITIVE_KEY.contains(key)
+                    || lowerKey.endsWith(".password")
+                    || lowerKey.endsWith(".secret")
+                    || lowerKey.endsWith(".secret_key")
+                    || lowerKey.endsWith(".secret.key")
+                    || lowerKey.endsWith(".session_key")
+                    || lowerKey.endsWith(".session.token")
+                    || lowerKey.contains(".private.key.")
+                    || lowerKey.endsWith(".private.key")
+                    || lowerKey.endsWith(".private_key")
+                    || lowerKey.endsWith(".passphrase")
+                    || "ssl.keystore.key".equals(lowerKey)
+                    || "ssl.key.pem".equals(lowerKey);
+            maskedProperties.put(keyPrefix + key, sensitive ? 
SENSITIVE_PROPERTY_MASK : value);
+        });
+        return maskedProperties;
     }
 
     @Override
@@ -723,9 +750,7 @@ public class KafkaRoutineLoadJob extends RoutineLoadJob {
 
     @Override
     public Map<String, String> getCustomProperties() {
-        Map<String, String> ret = new HashMap<>();
-        customProperties.forEach((k, v) -> ret.put("property." + k, v));
-        return ret;
+        return getMaskedCustomProperties("property.");
     }
 
     @Override
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/load/routineload/KafkaRoutineLoadJobTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/load/routineload/KafkaRoutineLoadJobTest.java
index b6944df5539..2565f26a15b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/load/routineload/KafkaRoutineLoadJobTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/load/routineload/KafkaRoutineLoadJobTest.java
@@ -256,6 +256,87 @@ public class KafkaRoutineLoadJobTest {
         Assert.assertTrue(otherMsg.contains("some records may be in 
uncommitted transactions"));
     }
 
+    @Test
+    public void testDisplayCustomPropertiesMasksKafkaSecrets() {
+        KafkaRoutineLoadJob routineLoadJob = new KafkaRoutineLoadJob(1L, 
"kafka_routine_load_job", 1L,
+                1L, "127.0.0.1:9020", "topic1", UserIdentity.ADMIN);
+        Map<String, String> customProperties = Maps.newHashMap();
+        customProperties.put("security.protocol", "SASL_PLAINTEXT");
+        customProperties.put("sasl.username", "doris");
+        customProperties.put("sasl.password", "plain_secret");
+        customProperties.put("sasl.jaas.config", "username=\"doris\" 
password=\"jaas_secret\"");
+        customProperties.put("sasl.oauthbearer.client.secret", 
"oauth_client_secret");
+        
customProperties.put("sasl.oauthbearer.client.credentials.client.secret", 
"oauth_alias_secret");
+        customProperties.put("sasl.oauthbearer.assertion.private.key.pem", 
"oauth_private_key_pem");
+        
customProperties.put("sasl.oauthbearer.assertion.private.key.passphrase", 
"oauth_private_key_passphrase");
+        customProperties.put("ssl.keystore.password", "keystore_secret");
+        customProperties.put("ssl.keystore.key", "keystore_key_secret");
+        customProperties.put("ssl.key.pem", "key_pem_secret");
+        customProperties.put(KafkaConfiguration.AWS_ACCESS_KEY, 
"aws_access_key");
+        customProperties.put("aws.secret_key", "aws_secret");
+        customProperties.put("aws.session_key", "aws_session_secret");
+        customProperties.put("password", "bare_password_secret");
+        customProperties.put("secret_key", "bare_secret_key");
+        customProperties.put("session_token", "bare_session_token");
+        Deencapsulation.setField(routineLoadJob, "customProperties", 
customProperties);
+
+        String customPropertiesJson = 
routineLoadJob.customPropertiesJsonToString();
+        Map<String, String> showCreateCustomProperties = 
routineLoadJob.getCustomProperties();
+
+        Assert.assertFalse(customPropertiesJson.contains("plain_secret"));
+        Assert.assertFalse(customPropertiesJson.contains("jaas_secret"));
+        
Assert.assertFalse(customPropertiesJson.contains("oauth_client_secret"));
+        
Assert.assertFalse(customPropertiesJson.contains("oauth_alias_secret"));
+        
Assert.assertFalse(customPropertiesJson.contains("oauth_private_key_pem"));
+        
Assert.assertFalse(customPropertiesJson.contains("oauth_private_key_passphrase"));
+        Assert.assertFalse(customPropertiesJson.contains("keystore_secret"));
+        
Assert.assertFalse(customPropertiesJson.contains("keystore_key_secret"));
+        Assert.assertFalse(customPropertiesJson.contains("key_pem_secret"));
+        Assert.assertFalse(customPropertiesJson.contains("aws_access_key"));
+        Assert.assertFalse(customPropertiesJson.contains("aws_secret"));
+        
Assert.assertFalse(customPropertiesJson.contains("aws_session_secret"));
+        
Assert.assertFalse(customPropertiesJson.contains("bare_password_secret"));
+        Assert.assertFalse(customPropertiesJson.contains("bare_secret_key"));
+        
Assert.assertFalse(customPropertiesJson.contains("bare_session_token"));
+        
Assert.assertTrue(customPropertiesJson.contains("\"sasl.password\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"sasl.jaas.config\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"sasl.oauthbearer.client.secret\":\"******\""));
+        Assert.assertTrue(customPropertiesJson.contains(
+                
"\"sasl.oauthbearer.client.credentials.client.secret\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"sasl.oauthbearer.assertion.private.key.pem\":\"******\""));
+        Assert.assertTrue(customPropertiesJson.contains(
+                
"\"sasl.oauthbearer.assertion.private.key.passphrase\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"ssl.keystore.password\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"ssl.keystore.key\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"ssl.key.pem\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"aws.access_key\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"aws.secret_key\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"aws.session_key\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"password\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"secret_key\":\"******\""));
+        
Assert.assertTrue(customPropertiesJson.contains("\"session_token\":\"******\""));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.sasl.password"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.sasl.jaas.config"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.sasl.oauthbearer.client.secret"));
+        Assert.assertEquals("******",
+                
showCreateCustomProperties.get("property.sasl.oauthbearer.client.credentials.client.secret"));
+        Assert.assertEquals("******",
+                
showCreateCustomProperties.get("property.sasl.oauthbearer.assertion.private.key.pem"));
+        Assert.assertEquals("******",
+                
showCreateCustomProperties.get("property.sasl.oauthbearer.assertion.private.key.passphrase"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.ssl.keystore.password"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.ssl.keystore.key"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.ssl.key.pem"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.aws.access_key"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.aws.secret_key"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.aws.session_key"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.password"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.secret_key"));
+        Assert.assertEquals("******", 
showCreateCustomProperties.get("property.session_token"));
+        Assert.assertEquals("doris", 
showCreateCustomProperties.get("property.sasl.username"));
+        Assert.assertEquals("plain_secret", 
customProperties.get("sasl.password"));
+    }
+
     @Test
     public void testReadCommittedZeroRowsWithLagDelaysNextTask(
             @Injectable RoutineLoadManager routineLoadManager) throws 
UserException {
diff --git 
a/regression-test/suites/load_p0/routine_load/test_show_routine_load.groovy 
b/regression-test/suites/load_p0/routine_load/test_show_routine_load.groovy
index f89e89a9dda..fc330bb7b1d 100644
--- a/regression-test/suites/load_p0/routine_load/test_show_routine_load.groovy
+++ b/regression-test/suites/load_p0/routine_load/test_show_routine_load.groovy
@@ -204,6 +204,47 @@ suite("test_show_routine_load","p0") {
             sql "stop routine load for testShow"
         }
 
+        // test show routine load and information_schema mask sensitive custom 
properties
+        try {
+            def kafkaPassword = "doris_routine_load_password_should_be_hidden"
+            def kafkaJaasSecret = 
"doris_routine_load_jaas_secret_should_be_hidden"
+            sql """
+                CREATE ROUTINE LOAD testShowSensitiveProperties ON ${tableName}
+                COLUMNS TERMINATED BY ","
+                FROM KAFKA
+                (
+                    "kafka_broker_list" = "${externalEnvIp}:${kafka_port}",
+                    "kafka_topic" = "${kafkaCsvTpoics[0]}",
+                    "property.kafka_default_offsets" = "OFFSET_BEGINNING",
+                    "property.security.protocol" = "SASL_PLAINTEXT",
+                    "property.sasl.mechanism" = "PLAIN",
+                    "property.sasl.username" = "doris",
+                    "property.sasl.password" = "${kafkaPassword}",
+                    "property.sasl.jaas.config" = "password=${kafkaJaasSecret}"
+                );
+            """
+
+            def showResult = sql "show routine load for 
testShowSensitiveProperties"
+            def showCustomProperties = parseJson(showResult[0][13])
+            assertEquals("******", 
showCustomProperties["sasl.password"].toString())
+            assertEquals("******", 
showCustomProperties["sasl.jaas.config"].toString())
+            assertFalse(showResult[0][13].toString().contains(kafkaPassword))
+            assertFalse(showResult[0][13].toString().contains(kafkaJaasSecret))
+
+            def systemTableResult = sql """
+                SELECT CUSTOM_PROPERTIES
+                FROM information_schema.routine_load_jobs
+                WHERE JOB_NAME = 'testShowSensitiveProperties'
+            """
+            def systemTableCustomProperties = 
parseJson(systemTableResult[0][0])
+            assertEquals("******", 
systemTableCustomProperties["sasl.password"].toString())
+            assertEquals("******", 
systemTableCustomProperties["sasl.jaas.config"].toString())
+            
assertFalse(systemTableResult[0][0].toString().contains(kafkaPassword))
+            
assertFalse(systemTableResult[0][0].toString().contains(kafkaJaasSecret))
+        } finally {
+            sql "stop routine load for testShowSensitiveProperties"
+        }
+
         // test show routine load computegroup
         try {
             sql """


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

Reply via email to