Skip to content

Commit e4bf807

Browse files
Add exporting SSL/TLS master key log feature
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
1 parent 821ec65 commit e4bf807

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import io.netty.handler.ssl.SslContextBuilder;
6565
import io.netty.handler.ssl.SslHandler;
6666
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
67+
import io.netty.handler.ssl.SslMasterKeyHandler;
6768
import io.netty.handler.ssl.SslProvider;
6869
import io.netty.util.AsciiString;
6970
import io.netty.util.Attribute;
@@ -396,6 +397,14 @@ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
396397
ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
397398
? new SslHandler(sslEngine, false, this.executor)
398399
: new SslHandler(sslEngine, false));
400+
401+
// Support exporting the key and session identifier to the log named
402+
// "io.netty.wireshark" when the system property named "io.netty.ssl.masterKeyHandler"
403+
// is "true". This feature is used to analyze gRPC traffic with Wireshark.
404+
if (Boolean.getBoolean(SslMasterKeyHandler.SYSTEM_PROP_KEY)) {
405+
ctx.pipeline().addBefore(ctx.name(), null,
406+
SslMasterKeyHandler.newWireSharkSslMasterKeyHandler());
407+
}
399408
}
400409

401410
@Override
@@ -572,6 +581,14 @@ protected void handlerAdded0(ChannelHandlerContext ctx) {
572581
ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
573582
? new SslHandler(sslEngine, false, this.executor)
574583
: new SslHandler(sslEngine, false));
584+
585+
// Support exporting the key and session identifier to the log named
586+
// "io.netty.wireshark" when the system property named "io.netty.ssl.masterKeyHandler"
587+
// is "true". This feature is used to analyze gRPC traffic with Wireshark.
588+
if (Boolean.getBoolean(SslMasterKeyHandler.SYSTEM_PROP_KEY)) {
589+
ctx.pipeline().addBefore(ctx.name(), null,
590+
SslMasterKeyHandler.newWireSharkSslMasterKeyHandler());
591+
}
575592
}
576593

577594
@Override

netty/src/test/java/io/grpc/netty/ProtocolNegotiatorsTest.java

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626
import static org.junit.Assert.assertNull;
2727
import static org.junit.Assert.assertTrue;
2828
import static org.mockito.ArgumentMatchers.any;
29+
import static org.mockito.ArgumentMatchers.argThat;
2930
import static org.mockito.Mockito.mock;
31+
import static org.mockito.Mockito.never;
3032
import static org.mockito.Mockito.times;
33+
import static org.mockito.Mockito.verify;
3134

3235
import io.grpc.Attributes;
3336
import io.grpc.CallCredentials;
@@ -94,8 +97,11 @@
9497
import io.netty.handler.ssl.SslContextBuilder;
9598
import io.netty.handler.ssl.SslHandler;
9699
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
100+
import io.netty.handler.ssl.SslMasterKeyHandler;
97101
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
98102
import io.netty.handler.ssl.util.SelfSignedCertificate;
103+
import io.netty.util.internal.logging.InternalLogger;
104+
import io.netty.util.internal.logging.InternalLoggerFactory;
99105
import java.io.File;
100106
import java.net.InetSocketAddress;
101107
import java.net.SocketAddress;
@@ -124,6 +130,7 @@
124130
import org.junit.runner.RunWith;
125131
import org.junit.runners.JUnit4;
126132
import org.mockito.ArgumentCaptor;
133+
import org.mockito.ArgumentMatcher;
127134
import org.mockito.ArgumentMatchers;
128135
import org.mockito.Mockito;
129136

@@ -160,6 +167,10 @@ public static void loadCerts() throws Exception {
160167
private SSLEngine engine;
161168
private ChannelHandlerContext channelHandlerCtx;
162169

170+
private InternalLogger mockLogger4KeyLog;
171+
private InternalLogger mockLogger4Others;
172+
private InternalLoggerFactory oldLoggerFactory;
173+
163174
@Before
164175
public void setUp() throws Exception {
165176
File serverCert = TestUtils.loadCert("server1.pem");
@@ -168,6 +179,11 @@ public void setUp() throws Exception {
168179
.ciphers(TestUtils.preferredTestCiphers(), SupportedCipherSuiteFilter.INSTANCE).build();
169180
engine = SSLContext.getDefault().createSSLEngine();
170181
engine.setUseClientMode(true);
182+
183+
oldLoggerFactory = InternalLoggerFactory.getDefaultFactory();
184+
mockLogger4KeyLog = mock(InternalLogger.class);
185+
mockLogger4Others = mock(InternalLogger.class);
186+
InternalLoggerFactory.setDefaultFactory(new FakeLoggerFactory());
171187
}
172188

173189
@After
@@ -179,6 +195,10 @@ public void tearDown() {
179195
chan.close();
180196
}
181197
group.shutdownGracefully();
198+
199+
Mockito.reset(mockLogger4KeyLog);
200+
Mockito.reset(mockLogger4Others);
201+
InternalLoggerFactory.setDefaultFactory(oldLoggerFactory);
182202
}
183203

184204
@Test
@@ -1190,4 +1210,81 @@ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
11901210
ctx.pipeline().fireUserEventTriggered(ProtocolNegotiationEvent.DEFAULT);
11911211
}
11921212
}
1213+
1214+
@Test
1215+
public void clientTlsHandler_serverTlsHandler_sslMasterKeyLog() throws Exception {
1216+
// The master key log feature should be disabled
1217+
// when the "io.netty.ssl.masterKeyHandler" property is missing.
1218+
System.clearProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY);
1219+
clientTlsHandler_firesNegotiation();
1220+
verify(mockLogger4KeyLog, never()).warn(argThat(new ArgumentMatcher<String>() {
1221+
@Override
1222+
public boolean matches(String arg) {
1223+
return arg.contains(" Master-Key:");
1224+
}
1225+
}), argThat(new ArgumentMatcher<String>() {
1226+
@Override
1227+
public boolean matches(String arg) {
1228+
return true;
1229+
}
1230+
}), argThat(new ArgumentMatcher<String>() {
1231+
@Override
1232+
public boolean matches(String arg) {
1233+
return true;
1234+
}
1235+
}));
1236+
1237+
// The master key log feature should be disabled
1238+
// when the value of "io.netty.ssl.masterKeyHandler" property is not "true".
1239+
System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, "false");
1240+
clientTlsHandler_firesNegotiation();
1241+
verify(mockLogger4KeyLog, never()).warn(argThat(new ArgumentMatcher<String>() {
1242+
@Override
1243+
public boolean matches(String arg) {
1244+
return arg.contains(" Master-Key:");
1245+
}
1246+
}), argThat(new ArgumentMatcher<String>() {
1247+
@Override
1248+
public boolean matches(String arg) {
1249+
return true;
1250+
}
1251+
}), argThat(new ArgumentMatcher<String>() {
1252+
@Override
1253+
public boolean matches(String arg) {
1254+
return true;
1255+
}
1256+
}));
1257+
1258+
// The master key log feature should be enabled
1259+
// when the value of "io.netty.ssl.masterKeyHandler" property is "true".
1260+
System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, "true");
1261+
clientTlsHandler_firesNegotiation();
1262+
// writing key twice because both client and server will enable key log feature
1263+
verify(mockLogger4KeyLog, times(2)).warn(argThat(new ArgumentMatcher<String>() {
1264+
@Override
1265+
public boolean matches(String arg) {
1266+
// writing key in one line each time, like:
1267+
// "RSA Session-ID:9da5b... Master-Key:5d74b...\n"
1268+
return arg.contains(" Master-Key:");
1269+
}
1270+
}), argThat(new ArgumentMatcher<String>() {
1271+
@Override
1272+
public boolean matches(String arg) {
1273+
return true;
1274+
}
1275+
}), argThat(new ArgumentMatcher<String>() {
1276+
@Override
1277+
public boolean matches(String arg) {
1278+
return true;
1279+
}
1280+
}));
1281+
System.clearProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY);
1282+
}
1283+
1284+
private class FakeLoggerFactory extends InternalLoggerFactory {
1285+
@Override
1286+
protected InternalLogger newInstance(String name) {
1287+
return name.equals("io.netty.wireshark") ? mockLogger4KeyLog : mockLogger4Others;
1288+
}
1289+
}
11931290
}

0 commit comments

Comments
 (0)