in zuul-core/src/main/java/com/netflix/zuul/netty/server/ssl/SslHandshakeInfoHandler.java [80:206]
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof SslHandshakeCompletionEvent) {
try {
SslHandshakeCompletionEvent sslEvent = (SslHandshakeCompletionEvent) evt;
if (sslEvent.isSuccess()) {
CurrentPassport.fromChannel(ctx.channel()).add(PassportState.SERVER_CH_SSL_HANDSHAKE_COMPLETE);
SSLSession session = getSSLSession(ctx);
if (session == null) {
logger.warn("Error getting the SSL handshake info. SSLSession is null");
return;
}
ClientAuth clientAuth = whichClientAuthEnum(ctx);
Certificate serverCert = null;
X509Certificate peerCert = null;
if ((clientAuth == ClientAuth.REQUIRE || clientAuth == ClientAuth.OPTIONAL)
&& session.getPeerCertificates() != null
&& session.getPeerCertificates().length > 0) {
peerCert = (X509Certificate) session.getPeerCertificates()[0];
}
if (session.getLocalCertificates() != null && session.getLocalCertificates().length > 0) {
serverCert = session.getLocalCertificates()[0];
}
// if attribute is true, then true. If null or false then false
boolean tlsHandshakeUsingExternalPSK = Objects.equals(
ctx.channel()
.attr(ZuulPskServer.TLS_HANDSHAKE_USING_EXTERNAL_PSK)
.get(),
Boolean.TRUE);
ClientPSKIdentityInfo clientPSKIdentityInfo = ctx.channel()
.attr(TlsPskHandler.CLIENT_PSK_IDENTITY_ATTRIBUTE_KEY)
.get();
SslHandshakeInfo info = new SslHandshakeInfo(
isSSlFromIntermediary,
session.getProtocol(),
session.getCipherSuite(),
clientAuth,
serverCert,
peerCert,
tlsHandshakeUsingExternalPSK,
clientPSKIdentityInfo);
ctx.channel().attr(ATTR_SSL_INFO).set(info);
// Metrics.
incrementCounters(sslEvent, info);
logger.debug("Successful SSL Handshake: {}", info);
} else {
String clientIP = ctx.channel()
.attr(SourceAddressChannelHandler.ATTR_SOURCE_ADDRESS)
.get();
Throwable cause = sslEvent.cause();
PassportState passportState =
CurrentPassport.fromChannel(ctx.channel()).getState();
if (cause instanceof ClosedChannelException
&& (passportState == PassportState.SERVER_CH_INACTIVE
|| passportState == PassportState.SERVER_CH_IDLE_TIMEOUT)) {
// Either client closed the connection without/before having completed a handshake, or
// the connection idle timed-out before handshake.
// NOTE: we were seeing a lot of these in prod and can repro by just telnetting to port and then
// closing terminal
// without sending anything.
// So don't treat these as SSL handshake failures.
logger.debug(
"Client closed connection or it idle timed-out without doing an ssl handshake. ,"
+ " client_ip = {}, channel_info = {}",
clientIP,
ChannelUtils.channelInfoForLogging(ctx.channel()));
} else if (cause instanceof SSLException
&& cause.getMessage().contains("handshake timed out")) {
logger.debug(
"Client timed-out doing the ssl handshake. , client_ip = {}, channel_info = {}",
clientIP,
ChannelUtils.channelInfoForLogging(ctx.channel()));
} else if (cause instanceof SSLException
&& cause.getMessage().contains("failure when writing TLS control frames")) {
// This can happen if the ClientHello is sent followed by a RST packet, before we can respond.
logger.debug(
"Client terminated handshake early., client_ip = {}, channel_info = {}",
clientIP,
ChannelUtils.channelInfoForLogging(ctx.channel()));
} else {
if (logger.isDebugEnabled()) {
String msg = "Unsuccessful SSL Handshake: " + sslEvent
+ ", client_ip = " + clientIP
+ ", channel_info = " + ChannelUtils.channelInfoForLogging(ctx.channel())
+ ", error = " + cause;
if (cause instanceof ClosedChannelException) {
logger.debug(msg);
} else {
logger.debug(msg, cause);
}
}
incrementCounters(sslEvent, null);
}
}
} catch (Throwable e) {
logger.warn("Error getting the SSL handshake info.", e);
} finally {
// Now remove this handler from the pipeline as no longer needed once the ssl handshake has completed.
ctx.pipeline().remove(this);
}
} else if (evt instanceof SslCloseCompletionEvent) {
// TODO - increment a separate metric for this event?
} else if (evt instanceof SniCompletionEvent) {
logger.debug("SNI Parsing Complete: {}", evt);
SniCompletionEvent sniCompletionEvent = (SniCompletionEvent) evt;
if (sniCompletionEvent.isSuccess()) {
spectatorRegistry.counter("zuul.sni.parse.success").increment();
} else {
Throwable cause = sniCompletionEvent.cause();
spectatorRegistry
.counter("zuul.sni.parse.failure", "cause", cause != null ? cause.getMessage() : "UNKNOWN")
.increment();
}
}
super.userEventTriggered(ctx, evt);
}