Skip to content

Commit

Permalink
netty: add exporting SSL/TLS master key log feature (#7724)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
huangqiangxiong committed Jan 8, 2021
1 parent 2dba43c commit 9bc05fb
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
17 changes: 17 additions & 0 deletions netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
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;
Expand Down Expand Up @@ -396,6 +397,14 @@ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
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
Expand Down Expand Up @@ -572,6 +581,14 @@ protected void handlerAdded0(ChannelHandlerContext ctx) {
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
Expand Down
52 changes: 52 additions & 0 deletions netty/src/test/java/io/grpc/netty/ProtocolNegotiatorsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,20 @@
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;
Expand Down Expand Up @@ -1210,4 +1213,53 @@ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
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);
}
}
}
}

0 comments on commit 9bc05fb

Please sign in to comment.