[
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:
[email protected]
> 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)