netty: add exporting SSL/TLS master key log feature (#7724)

Enable this feature by setting the system property
   -Dio.netty.ssl.masterKeyHandler=true
or
   System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, "true");
The keys will be written to the log named "io.netty.wireshark" in
the warnning level. To export the keys to a file, you can configure
log factory like: (with log4j.xml for example)
<appender name="key-file" class="org.apache.log4j.RollingFileAppender">
	<param name="file" value="d:/keyfile.txt"/>
	<layout class="org.apache.log4j.PatternLayout">
		<param name="ConversionPattern" value="%m%n"/>
	</layout>
</appender>
<category name="io.netty.wireshark">
	<priority value="DEBUG" />
	<appender-ref ref="key-file" />
</category>

Wireshark can analyze the messages gRPC over TLS with this
key log file.

close #7199
This commit is contained in:
huangqiangxiong 2021-01-09 05:11:24 +08:00 committed by GitHub
parent 2dba43c727
commit 9bc05fba67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 0 deletions

View File

@ -64,6 +64,7 @@ import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.handler.ssl.SslMasterKeyHandler;
import io.netty.handler.ssl.SslProvider;
import io.netty.util.AsciiString;
import io.netty.util.Attribute;
@ -396,6 +397,14 @@ final class ProtocolNegotiators {
ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
? new SslHandler(sslEngine, false, this.executor)
: new SslHandler(sslEngine, false));
// Support exporting the key and session identifier to the log named
// "io.netty.wireshark" when the system property named "io.netty.ssl.masterKeyHandler"
// is "true". This feature is used to analyze gRPC traffic with Wireshark.
if (Boolean.getBoolean(SslMasterKeyHandler.SYSTEM_PROP_KEY)) {
ctx.pipeline().addBefore(ctx.name(), null,
SslMasterKeyHandler.newWireSharkSslMasterKeyHandler());
}
}
@Override
@ -572,6 +581,14 @@ final class ProtocolNegotiators {
ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
? new SslHandler(sslEngine, false, this.executor)
: new SslHandler(sslEngine, false));
// Support exporting the key and session identifier to the log named
// "io.netty.wireshark" when the system property named "io.netty.ssl.masterKeyHandler"
// is "true". This feature is used to analyze gRPC traffic with Wireshark.
if (Boolean.getBoolean(SslMasterKeyHandler.SYSTEM_PROP_KEY)) {
ctx.pipeline().addBefore(ctx.name(), null,
SslMasterKeyHandler.newWireSharkSslMasterKeyHandler());
}
}
@Override

View File

@ -94,17 +94,20 @@ import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.handler.ssl.SslMasterKeyHandler;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Filter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
@ -1210,4 +1213,53 @@ public class ProtocolNegotiatorsTest {
ctx.pipeline().fireUserEventTriggered(ProtocolNegotiationEvent.DEFAULT);
}
}
@Test
public void clientTlsHandler_serverTlsHandler_sslMasterKeyLog() throws Exception {
final List<LogRecord> logs = new ArrayList<>();
Handler handler = new Handler() {
@Override public void publish(LogRecord record) {
logs.add(record);
}
@Override public void flush() {}
@Override public void close() {}
};
Logger logger = Logger.getLogger("io.netty.wireshark");
logger.addHandler(handler);
String oldPropValue = System.getProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY);
try {
// The master key log feature should be disabled
// when the "io.netty.ssl.masterKeyHandler" property is missing.
System.clearProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY);
clientTlsHandler_firesNegotiation();
assertThat(logs).isEmpty();
// The master key log feature should be disabled
// when the value of "io.netty.ssl.masterKeyHandler" property is not "true".
System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, "false");
clientTlsHandler_firesNegotiation();
assertThat(logs).isEmpty();
// The master key log feature should be enabled
// when the value of "io.netty.ssl.masterKeyHandler" property is "true".
System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, "true");
clientTlsHandler_firesNegotiation();
// writing key twice because both client and server will enable key log feature
assertThat(logs.size()).isEqualTo(2);
assertThat(logs.get(0).getMessage()).containsMatch("^RSA Session-ID:.+ Master-Key:");
assertThat(logs.get(1).getMessage()).containsMatch("^RSA Session-ID:.+ Master-Key:");
} finally {
logger.removeHandler(handler);
if (oldPropValue != null) {
System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, oldPropValue);
} else {
System.clearProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY);
}
}
}
}