[ 
https://issues.apache.org/jira/browse/KAFKA-5117?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16687038#comment-16687038
 ] 

ASF GitHub Bot commented on KAFKA-5117:
---------------------------------------

qiao-meng-zefr closed pull request #4269: KAFKA-5117: Add password masking for 
kafka connect REST endpoint
URL: https://github.com/apache/kafka/pull/4269
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/AbstractHerder.java
 
b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/AbstractHerder.java
index fbe0ae2afb2..9eb2def0c64 100644
--- 
a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/AbstractHerder.java
+++ 
b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/AbstractHerder.java
@@ -184,6 +184,22 @@ public void resumeConnector(String connector) {
         configBackingStore.putTargetState(connector, TargetState.STARTED);
     }
 
+    @Override
+    public Map<String, String> maskCredentials(String connName, Map<String, 
String> config) {
+        Map<String, String> newConfig = new LinkedHashMap<>();
+        for (Map.Entry<String, String> entry : config.entrySet()) {
+            // Password.toString() will return the hidden value
+            String value = null;
+            if (entry.getValue() != null) {
+                value = entry.getValue().toString();
+            }
+
+            newConfig.put(entry.getKey(), value);
+        }
+
+        return newConfig;
+    }
+
     @Override
     public Plugins plugins() {
         return worker.getPlugins();
diff --git 
a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/Herder.java 
b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/Herder.java
index 5dfb808f764..774dbaf8ca1 100644
--- a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/Herder.java
+++ b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/Herder.java
@@ -169,6 +169,14 @@
      */
     void resumeConnector(String connector);
 
+    /**
+     * Goes through config parameters and replace password field value with 
"[hidden"]
+     * @param connName name of the connector
+     * @param config configuration of the connector
+     * @return new map of the configurations, with password omitted from 
clear-text
+     */
+    Map<String, String> maskCredentials(String connName, Map<String, String> 
config);
+
     /**
      * Returns a handle to the plugin factory used by this herder and its 
worker.
      *
diff --git 
a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResource.java
 
b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResource.java
index 2c031245c06..6e19cc729a8 100644
--- 
a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResource.java
+++ 
b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResource.java
@@ -50,6 +50,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.ArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -109,7 +110,8 @@ public ConnectorInfo getConnector(final 
@PathParam("connector") String connector
                                       final @QueryParam("forward") Boolean 
forward) throws Throwable {
         FutureCallback<ConnectorInfo> cb = new FutureCallback<>();
         herder.connectorInfo(connector, cb);
-        return completeOrForwardRequest(cb, "/connectors/" + connector, "GET", 
null, forward);
+        ConnectorInfo connectorInfo = completeOrForwardRequest(cb, 
"/connectors/" + connector, "GET", null, forward);
+        return new ConnectorInfo(connectorInfo.name(), 
herder.maskCredentials(connector, connectorInfo.config()), 
connectorInfo.tasks(), connectorInfo.type());
     }
 
     @GET
@@ -118,7 +120,8 @@ public ConnectorInfo getConnector(final 
@PathParam("connector") String connector
                                                   final @QueryParam("forward") 
Boolean forward) throws Throwable {
         FutureCallback<Map<String, String>> cb = new FutureCallback<>();
         herder.connectorConfig(connector, cb);
-        return completeOrForwardRequest(cb, "/connectors/" + connector + 
"/config", "GET", null, forward);
+        Map<String, String> config = completeOrForwardRequest(cb, 
"/connectors/" + connector + "/config", "GET", null, forward);
+        return herder.maskCredentials(connector, config);
     }
 
     @GET
@@ -177,8 +180,14 @@ public Response resumeConnector(@PathParam("connector") 
String connector) {
                                          final @QueryParam("forward") Boolean 
forward) throws Throwable {
         FutureCallback<List<TaskInfo>> cb = new FutureCallback<>();
         herder.taskConfigs(connector, cb);
-        return completeOrForwardRequest(cb, "/connectors/" + connector + 
"/tasks", "GET", null, new TypeReference<List<TaskInfo>>() {
-        }, forward);
+        List<TaskInfo> taskInfoList = completeOrForwardRequest(cb, 
"/connectors/" + connector + "/tasks", "GET", null,
+                new TypeReference<List<TaskInfo>>() {
+                }, forward);
+        List<TaskInfo> maskedTaskInfoList = new ArrayList<>();
+        for (TaskInfo taskInfo : taskInfoList) {
+            maskedTaskInfoList.add(new TaskInfo(taskInfo.id(), 
herder.maskCredentials(connector, taskInfo.config())));
+        }
+        return maskedTaskInfoList;
     }
 
     @POST
diff --git 
a/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResourceTest.java
 
b/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResourceTest.java
index 89a221835ef..614eff9d585 100644
--- 
a/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResourceTest.java
+++ 
b/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/resources/ConnectorsResourceTest.java
@@ -268,7 +268,10 @@ public void testDeleteConnectorNotFound() throws Throwable 
{
 
     @Test
     public void testGetConnector() throws Throwable {
+        EasyMock.expect(herder.maskCredentials(CONNECTOR_NAME, 
CONNECTOR_CONFIG)).andReturn(CONNECTOR_CONFIG);
+
         final Capture<Callback<ConnectorInfo>> cb = Capture.newInstance();
+
         herder.connectorInfo(EasyMock.eq(CONNECTOR_NAME), 
EasyMock.capture(cb));
         expectAndCallbackResult(cb, new ConnectorInfo(CONNECTOR_NAME, 
CONNECTOR_CONFIG, CONNECTOR_TASK_NAMES,
             ConnectorType.SOURCE));
@@ -276,6 +279,7 @@ public void testGetConnector() throws Throwable {
         PowerMock.replayAll();
 
         ConnectorInfo connInfo = 
connectorsResource.getConnector(CONNECTOR_NAME, FORWARD);
+
         assertEquals(new ConnectorInfo(CONNECTOR_NAME, CONNECTOR_CONFIG, 
CONNECTOR_TASK_NAMES, ConnectorType.SOURCE),
             connInfo);
 
@@ -284,13 +288,15 @@ public void testGetConnector() throws Throwable {
 
     @Test
     public void testGetConnectorConfig() throws Throwable {
+        EasyMock.expect(herder.maskCredentials(CONNECTOR_NAME, 
CONNECTOR_CONFIG)).andReturn(CONNECTOR_CONFIG);
+
         final Capture<Callback<Map<String, String>>> cb = 
Capture.newInstance();
         herder.connectorConfig(EasyMock.eq(CONNECTOR_NAME), 
EasyMock.capture(cb));
         expectAndCallbackResult(cb, CONNECTOR_CONFIG);
 
         PowerMock.replayAll();
-
         Map<String, String> connConfig = 
connectorsResource.getConnectorConfig(CONNECTOR_NAME, FORWARD);
+
         assertEquals(CONNECTOR_CONFIG, connConfig);
 
         PowerMock.verifyAll();
@@ -298,6 +304,8 @@ public void testGetConnectorConfig() throws Throwable {
 
     @Test(expected = NotFoundException.class)
     public void testGetConnectorConfigConnectorNotFound() throws Throwable {
+        EasyMock.expect(herder.maskCredentials(CONNECTOR_NAME, 
CONNECTOR_CONFIG)).andReturn(CONNECTOR_CONFIG);
+
         final Capture<Callback<Map<String, String>>> cb = 
Capture.newInstance();
         herder.connectorConfig(EasyMock.eq(CONNECTOR_NAME), 
EasyMock.capture(cb));
         expectAndCallbackException(cb, new NotFoundException("not found"));
@@ -375,6 +383,10 @@ public void testCreateConnectorConfigNameMismatch() throws 
Throwable {
 
     @Test
     public void testGetConnectorTaskConfigs() throws Throwable {
+        for (int i = 0; i < TASK_CONFIGS.size(); i++) {
+            EasyMock.expect(herder.maskCredentials(CONNECTOR_NAME, 
TASK_CONFIGS.get(i))).andReturn(TASK_CONFIGS.get(i));
+        }
+
         final Capture<Callback<List<TaskInfo>>> cb = Capture.newInstance();
         herder.taskConfigs(EasyMock.eq(CONNECTOR_NAME), EasyMock.capture(cb));
         expectAndCallbackResult(cb, TASK_INFOS);


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


> Kafka Connect REST endpoints reveal Password typed values
> ---------------------------------------------------------
>
>                 Key: KAFKA-5117
>                 URL: https://issues.apache.org/jira/browse/KAFKA-5117
>             Project: Kafka
>          Issue Type: Bug
>          Components: KafkaConnect
>    Affects Versions: 0.10.2.0
>            Reporter: Thomas Holmes
>            Assignee: Ewen Cheslack-Postava
>            Priority: Major
>              Labels: needs-kip
>             Fix For: 2.0.0
>
>
> A Kafka Connect connector can specify ConfigDef keys as type of Password. 
> This type was added to prevent logging the values (instead "[hidden]" is 
> logged).
> This change does not apply to the values returned by executing a GET on 
> {{connectors/\{connector-name\}}} and 
> {{connectors/\{connector-name\}/config}}. This creates an easily accessible 
> way for an attacker who has infiltrated your network to gain access to 
> potential secrets that should not be available.
> I have started on a code change that addresses this issue by parsing the 
> config values through the ConfigDef for the connector and returning their 
> output instead (which leads to the masking of Password typed configs as 
> [hidden]).



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to