Skip to content

Commit ac0d082

Browse files
committed
Add log redactor method when logging ZK config properties
1 parent 97e1789 commit ac0d082

File tree

4 files changed

+121
-5
lines changed

4 files changed

+121
-5
lines changed

zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKConfig.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
import java.nio.file.Path;
2626
import java.nio.file.Paths;
2727
import java.util.HashMap;
28+
import java.util.Locale;
2829
import java.util.Map;
2930
import java.util.Map.Entry;
3031
import java.util.Properties;
32+
3133
import org.apache.zookeeper.Environment;
3234
import org.apache.zookeeper.server.util.VerifyingFileFactory;
3335
import org.slf4j.Logger;
@@ -104,7 +106,11 @@ public ZKConfig(File configFile) throws ConfigException {
104106
public ZKConfig(Path configPath) throws ConfigException {
105107
this();
106108
addConfiguration(configPath);
107-
LOG.info("ZK Config {}", this.properties);
109+
Map<String, String> p = new HashMap<>();
110+
for (Entry<String, String> entry : properties.entrySet()) {
111+
p.put(entry.getKey(), logRedactor(entry.getKey(), entry.getValue()));
112+
}
113+
LOG.info("ZK Config {}", p);
108114
}
109115

110116
private void init() {
@@ -206,7 +212,7 @@ public void setProperty(String key, String value) {
206212
}
207213
String oldValue = properties.put(key, value);
208214
if (null != oldValue && !oldValue.equals(value)) {
209-
LOG.debug("key {}'s value {} is replaced with new value {}", key, oldValue, value);
215+
LOG.debug("key {}'s value {} is replaced with new value {}", key, logRedactor(key, oldValue), logRedactor(key, value));
210216
}
211217
}
212218

@@ -337,4 +343,13 @@ public int getInt(String key, int defaultValue) {
337343
return defaultValue;
338344
}
339345

346+
private String logRedactor(String key, String value) {
347+
if (key == null) {
348+
return value;
349+
}
350+
if (key.toLowerCase(Locale.ROOT).endsWith("password")) {
351+
return "***";
352+
}
353+
return value;
354+
}
340355
}

zookeeper-server/src/test/java/org/apache/zookeeper/common/ZKConfigTest.java

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,35 @@
1919
package org.apache.zookeeper.common;
2020

2121
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertFalse;
23+
import static org.junit.jupiter.api.Assertions.assertNotNull;
24+
import static org.junit.jupiter.api.Assertions.assertTrue;
25+
26+
import org.apache.zookeeper.test.LoggerTestTool;
27+
import org.junit.jupiter.api.AfterAll;
2228
import org.junit.jupiter.api.AfterEach;
29+
import org.junit.jupiter.api.BeforeAll;
2330
import org.junit.jupiter.api.Test;
2431
import org.junit.jupiter.api.Timeout;
32+
import ch.qos.logback.classic.Level;
33+
34+
import java.io.File;
35+
import java.io.IOException;
2536

2637
public class ZKConfigTest {
2738

28-
X509Util x509Util = new ClientX509Util();
39+
private final X509Util x509Util = new ClientX509Util();
40+
private static LoggerTestTool loggerTestTool;
41+
42+
@BeforeAll
43+
public static void setUpBeforeClass() {
44+
loggerTestTool = new LoggerTestTool(ZKConfig.class, Level.DEBUG);
45+
}
46+
47+
@AfterAll
48+
public static void tearDownAfterClass() throws Exception {
49+
loggerTestTool.close();
50+
}
2951

3052
@AfterEach
3153
public void tearDown() throws Exception {
@@ -90,4 +112,52 @@ public void testBooleanRetrievalFromPropertyWithWhitespacesAtBeginningAndEnd() {
90112
boolean result = conf.getBoolean(x509Util.getSslProtocolProperty(), defaultValue);
91113
assertEquals(value, result);
92114
}
115+
116+
@Test
117+
public void testLogRedactorFromConfigFile() throws ConfigException, IOException {
118+
// Arrange
119+
File configFile = new File("./src/test/resources/zookeeper-client.config");
120+
121+
// Act
122+
new ZKConfig(configFile.toPath());
123+
124+
// Assert
125+
String logLine = loggerTestTool.readLogLine("ZK Config");
126+
assertNotNull(logLine, "Unable to find ZK Config line in the logs");
127+
assertFalse(logLine.contains("FileSecret456!"), "Logs should not contain any secrets");
128+
assertFalse(logLine.contains("AnotherFileSecret789!"), "Logs should not contain any secrets");
129+
assertTrue(logLine.contains("/home/zookeeper/top_secret.txt")); // what we shouldn't redact
130+
}
131+
132+
@Test
133+
public void testLogRedactorInDebugLogs() throws IOException {
134+
// Arrange
135+
ZKConfig conf = new ZKConfig();
136+
137+
// Act
138+
conf.setProperty("zookeeper.some.secret.password", "0ldP4ssw0rd");
139+
conf.setProperty("zookeeper.some.secret.password", "N3Ws3cr3t");
140+
141+
// Assert
142+
String logLine = loggerTestTool.readLogLine("replaced with new value");
143+
assertNotNull(logLine, "Unable to find relevant line in the logs");
144+
assertFalse(logLine.contains("0ldP4ssw0rd"), "Logs should not contain any secrets");
145+
assertFalse(logLine.contains("N3Ws3cr3t"), "Logs should not contain any secrets");
146+
}
147+
148+
@Test
149+
public void testDontRedactorInDebugLogs() throws IOException {
150+
// Arrange
151+
ZKConfig conf = new ZKConfig();
152+
153+
// Act
154+
conf.setProperty("zookeeper.some.secret.passwordPath", "/home/zookeeper/old_secret.txt"); // what we shouldn't redact
155+
conf.setProperty("zookeeper.some.secret.passwordPath", "/home/zookeeper/new_secret.txt"); // what we shouldn't redact
156+
157+
// Assert
158+
String logLine = loggerTestTool.readLogLine("replaced with new value");
159+
assertNotNull(logLine, "Unable to find relevant line in the logs");
160+
assertTrue(logLine.contains("/home/zookeeper/new_secret.txt"));
161+
}
162+
93163
}

zookeeper-server/src/test/java/org/apache/zookeeper/test/LoggerTestTool.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,27 @@
2727
import ch.qos.logback.core.OutputStreamAppender;
2828
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
2929
import java.io.ByteArrayOutputStream;
30+
import java.io.IOException;
31+
import java.io.LineNumberReader;
32+
import java.io.StringReader;
33+
3034
import org.slf4j.LoggerFactory;
3135

3236
public class LoggerTestTool implements AutoCloseable {
3337
private final ByteArrayOutputStream os;
3438
private Appender<ILoggingEvent> appender;
3539
private Logger qlogger;
40+
private Level logLevel = Level.INFO;
3641

3742
public LoggerTestTool(Class<?> cls) {
3843
os = createLoggingStream(cls);
3944
}
4045

46+
public LoggerTestTool(Class<?> cls, Level logLevel) {
47+
this.logLevel = logLevel;
48+
this.os = createLoggingStream(cls);
49+
}
50+
4151
public LoggerTestTool(String cls) {
4252
os = createLoggingStream(cls);
4353
}
@@ -51,7 +61,7 @@ private ByteArrayOutputStream createLoggingStream(Class<?> cls) {
5161
appender = getConsoleAppender(os);
5262
qlogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(cls);
5363
qlogger.addAppender(appender);
54-
qlogger.setLevel(Level.INFO);
64+
qlogger.setLevel(logLevel);
5565
appender.start();
5666
return os;
5767
}
@@ -61,14 +71,15 @@ private ByteArrayOutputStream createLoggingStream(String cls) {
6171
appender = getConsoleAppender(os);
6272
qlogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(cls);
6373
qlogger.addAppender(appender);
64-
qlogger.setLevel(Level.INFO);
74+
qlogger.setLevel(logLevel);
6575
appender.start();
6676
return os;
6777
}
6878

6979
private OutputStreamAppender<ILoggingEvent> getConsoleAppender(ByteArrayOutputStream os) {
7080
Logger rootLogger =
7181
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
82+
rootLogger.setLevel(logLevel);
7283
Layout<ILoggingEvent> layout = ((LayoutWrappingEncoder<ILoggingEvent>)
7384
((OutputStreamAppender<ILoggingEvent>) rootLogger.getAppender("CONSOLE")).getEncoder()).getLayout();
7485

@@ -80,6 +91,21 @@ private OutputStreamAppender<ILoggingEvent> getConsoleAppender(ByteArrayOutputSt
8091
return appender;
8192
}
8293

94+
public String readLogLine(String search) throws IOException {
95+
try {
96+
LineNumberReader r = new LineNumberReader(new StringReader(os.toString()));
97+
String line;
98+
while ((line = r.readLine()) != null) {
99+
if (line.contains(search)) {
100+
return line;
101+
}
102+
}
103+
return null;
104+
} finally {
105+
os.reset();
106+
}
107+
}
108+
83109
@Override
84110
public void close() throws Exception {
85111
qlogger.detachAppender(appender);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Sample secrets
2+
zookeeper.ssl.keyStore.password=FileSecret456!
3+
zookeeper.ssl.keyStore.passwordPath=/home/zookeeper/top_secret.txt
4+
ssl.quorum.keyStore.password=AnotherFileSecret789!
5+

0 commit comments

Comments
 (0)