diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ConfigRedactor.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ConfigRedactor.java
new file mode 100644
index 0000000000..0ba756c7d4
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ConfigRedactor.java
@@ -0,0 +1,84 @@
+/**
+ * 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.hadoop.conf;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import static org.apache.hadoop.fs.CommonConfigurationKeys.*;
+
+/**
+ * Tool for redacting sensitive information when displaying config parameters.
+ *
+ *
Some config parameters contain sensitive information (for example, cloud
+ * storage keys). When these properties are displayed in plaintext, we should
+ * redactor their values as appropriate.
+ */
+public class ConfigRedactor {
+
+ private static final String REDACTED_TEXT = "";
+
+ private List compiledPatterns;
+
+ public ConfigRedactor(Configuration conf) {
+ String sensitiveRegexList = conf.get(
+ HADOOP_SECURITY_SENSITIVE_CONFIG_KEYS,
+ HADOOP_SECURITY_SENSITIVE_CONFIG_KEYS_DEFAULT);
+ List sensitiveRegexes = Arrays.asList(sensitiveRegexList.split(","));
+ compiledPatterns = new ArrayList();
+ for (String regex : sensitiveRegexes) {
+ Pattern p = Pattern.compile(regex);
+ compiledPatterns.add(p);
+ }
+ }
+
+ /**
+ * Given a key / value pair, decides whether or not to redact and returns
+ * either the original value or text indicating it has been redacted.
+ *
+ * @param key
+ * @param value
+ * @return Original value, or text indicating it has been redacted
+ */
+ public String redact(String key, String value) {
+ if (configIsSensitive(key)) {
+ return REDACTED_TEXT;
+ }
+ return value;
+ }
+
+ /**
+ * Matches given config key against patterns and determines whether or not
+ * it should be considered sensitive enough to redact in logs and other
+ * plaintext displays.
+ *
+ * @param key
+ * @return True if parameter is considered sensitive
+ */
+ private boolean configIsSensitive(String key) {
+ for (Pattern regex : compiledPatterns) {
+ if (regex.matcher(key).find()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java
index 681ca2bf77..bdd006dfa7 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java
@@ -117,17 +117,24 @@ public void run() {
final Collection changes =
parent.getChangedProperties(newConf, oldConf);
Map> results = Maps.newHashMap();
+ ConfigRedactor oldRedactor = new ConfigRedactor(oldConf);
+ ConfigRedactor newRedactor = new ConfigRedactor(newConf);
for (PropertyChange change : changes) {
String errorMessage = null;
+ String oldValRedacted = oldRedactor.redact(change.prop, change.oldVal);
+ String newValRedacted = newRedactor.redact(change.prop, change.newVal);
if (!parent.isPropertyReconfigurable(change.prop)) {
LOG.info(String.format(
"Property %s is not configurable: old value: %s, new value: %s",
- change.prop, change.oldVal, change.newVal));
+ change.prop,
+ oldValRedacted,
+ newValRedacted));
continue;
}
LOG.info("Change property: " + change.prop + " from \""
- + ((change.oldVal == null) ? "" : change.oldVal)
- + "\" to \"" + ((change.newVal == null) ? "" : change.newVal)
+ + ((change.oldVal == null) ? "" : oldValRedacted)
+ + "\" to \""
+ + ((change.newVal == null) ? "" : newValRedacted)
+ "\".");
try {
String effectiveValue =
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java
index e746f2b0ab..0a3afb781f 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java
@@ -753,5 +753,19 @@ public class CommonConfigurationKeysPublic {
*/
public static final String HADOOP_SECURITY_CREDENTIAL_PASSWORD_FILE_KEY =
"hadoop.security.credstore.java-keystore-provider.password-file";
+
+ /**
+ * @see
+ *
+ * core-default.xml
+ */
+ public static final String HADOOP_SECURITY_SENSITIVE_CONFIG_KEYS =
+ "hadoop.security.sensitive-config-keys";
+ public static final String HADOOP_SECURITY_SENSITIVE_CONFIG_KEYS_DEFAULT =
+ "password$" + "," +
+ "fs.s3.*[Ss]ecret.?[Kk]ey" + "," +
+ "fs.azure\\.account.key.*" + "," +
+ "dfs.webhdfs.oauth2.[a-z]+.token" + "," +
+ HADOOP_SECURITY_SENSITIVE_CONFIG_KEYS;
}
diff --git a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
index f9c3f728ab..e78795c3f9 100644
--- a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
+++ b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
@@ -502,6 +502,16 @@
+
+ hadoop.security.sensitive-config-keys
+ password$,fs.s3.*[Ss]ecret.?[Kk]ey,fs.azure.account.key.*,dfs.webhdfs.oauth2.[a-z]+.token,hadoop.security.sensitive-config-keys
+ A comma-separated list of regular expressions to match against
+ configuration keys that should be redacted where appropriate, for
+ example, when logging modified properties during a reconfiguration,
+ private credentials should not be logged.
+
+
+
hadoop.workaround.non.threadsafe.getpwuid
true
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestConfigRedactor.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestConfigRedactor.java
new file mode 100644
index 0000000000..81f8f71795
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestConfigRedactor.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.hadoop.conf;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests the tool (and the default expression) for deciding which config
+ * redact.
+ */
+public class TestConfigRedactor {
+ private static final String REDACTED_TEXT = "";
+
+ private static final String ORIGINAL_VALUE = "Hello, World!";
+
+ @Test
+ public void redact() throws Exception {
+ Configuration conf = new Configuration();
+ ConfigRedactor redactor = new ConfigRedactor(conf);
+ String processedText;
+
+ List sensitiveKeys = Arrays.asList(
+ "fs.s3a.secret.key",
+ "fs.s3n.awsSecretKey",
+ "fs.azure.account.key.abcdefg.blob.core.windows.net",
+ "dfs.webhdfs.oauth2.access.token",
+ "dfs.webhdfs.oauth2.refresh.token",
+ "ssl.server.keystore.keypassword",
+ "ssl.server.keystore.password",
+ "hadoop.security.sensitive-config-keys"
+ );
+ for (String key : sensitiveKeys) {
+ processedText = redactor.redact(key, ORIGINAL_VALUE);
+ Assert.assertEquals(
+ "Config parameter wasn't redacted and should be: " + key,
+ REDACTED_TEXT, processedText);
+ }
+
+ List normalKeys = Arrays.asList(
+ "fs.defaultFS",
+ "dfs.replication",
+ "ssl.server.keystore.location",
+ "hadoop.security.credstore.java-keystore-provider.password-file"
+ );
+ for (String key : normalKeys) {
+ processedText = redactor.redact(key, ORIGINAL_VALUE);
+ Assert.assertEquals(
+ "Config parameter was redacted and shouldn't be: " + key,
+ ORIGINAL_VALUE, processedText);
+ }
+ }
+}