diff options
author | Bjørn Christian Seime <bjorncs@oath.com> | 2018-12-17 16:58:18 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@oath.com> | 2019-01-08 13:46:43 +0100 |
commit | 7839b72766082427e8bfe1190e03bd3069911764 (patch) | |
tree | 309ba0b79253bd28790ff3672281547f71f9abfa /jrt/src/com | |
parent | dd49544fc92bb977389ea43b661246df08b9ad44 (diff) |
Add metrics for jrt transport
Diffstat (limited to 'jrt/src/com')
-rw-r--r-- | jrt/src/com/yahoo/jrt/CryptoEngine.java | 2 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/MaybeTlsCryptoEngine.java | 8 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java | 13 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/NullCryptoEngine.java | 4 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/NullCryptoSocket.java | 16 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/TlsCryptoEngine.java | 5 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/TlsCryptoSocket.java | 104 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/Transport.java | 7 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/TransportMetrics.java | 79 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/XorCryptoEngine.java | 2 |
10 files changed, 180 insertions, 60 deletions
diff --git a/jrt/src/com/yahoo/jrt/CryptoEngine.java b/jrt/src/com/yahoo/jrt/CryptoEngine.java index cc59c29bc3b..0d1dfe8a22b 100644 --- a/jrt/src/com/yahoo/jrt/CryptoEngine.java +++ b/jrt/src/com/yahoo/jrt/CryptoEngine.java @@ -18,7 +18,7 @@ import java.nio.channels.SocketChannel; * encryption. **/ public interface CryptoEngine extends AutoCloseable { - CryptoSocket createCryptoSocket(SocketChannel channel, boolean isServer); + CryptoSocket createCryptoSocket(TransportMetrics metrics, SocketChannel channel, boolean isServer); static CryptoEngine createDefault() { if (!TransportSecurityUtils.isTransportSecurityEnabled()) { return new NullCryptoEngine(); diff --git a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoEngine.java b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoEngine.java index d888690c39c..a0d56281744 100644 --- a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoEngine.java +++ b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoEngine.java @@ -21,13 +21,13 @@ public class MaybeTlsCryptoEngine implements CryptoEngine { } @Override - public CryptoSocket createCryptoSocket(SocketChannel channel, boolean isServer) { + public CryptoSocket createCryptoSocket(TransportMetrics metrics, SocketChannel channel, boolean isServer) { if (isServer) { - return new MaybeTlsCryptoSocket(channel, tlsEngine); + return new MaybeTlsCryptoSocket(metrics, channel, tlsEngine, isServer); } else if (useTlsWhenClient) { - return tlsEngine.createCryptoSocket(channel, false); + return tlsEngine.createCryptoSocket(metrics, channel, false); } else { - return new NullCryptoSocket(channel); + return new NullCryptoSocket(metrics, channel, isServer); } } diff --git a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java index 7c09b8b47ca..ba34bed11c0 100644 --- a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java +++ b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java @@ -56,11 +56,13 @@ public class MaybeTlsCryptoSocket implements CryptoSocket { private class MyCryptoSocket extends NullCryptoSocket { + private final TransportMetrics metrics; private TlsCryptoEngine factory; private Buffer buffer; - MyCryptoSocket(SocketChannel channel, TlsCryptoEngine factory) { - super(channel); + MyCryptoSocket(TransportMetrics metrics, SocketChannel channel, TlsCryptoEngine factory, boolean isServer) { + super(metrics, channel, isServer); + this.metrics = metrics; this.factory = factory; this.buffer = new Buffer(4096); } @@ -79,11 +81,12 @@ public class MaybeTlsCryptoSocket implements CryptoSocket { data[i] = src.get(i); } if (looksLikeTlsToMe(data)) { - TlsCryptoSocket tlsSocket = factory.createCryptoSocket(channel(), true); + TlsCryptoSocket tlsSocket = factory.createCryptoSocket(metrics, channel(), true); tlsSocket.injectReadData(buffer); socket = tlsSocket; return socket.handshake(); } else { + metrics.incrementServerUnencryptedConnectionsEstablished(); factory = null; } } @@ -114,8 +117,8 @@ public class MaybeTlsCryptoSocket implements CryptoSocket { } } - public MaybeTlsCryptoSocket(SocketChannel channel, TlsCryptoEngine factory) { - this.socket = new MyCryptoSocket(channel, factory); + public MaybeTlsCryptoSocket(TransportMetrics metrics, SocketChannel channel, TlsCryptoEngine factory, boolean isServer) { + this.socket = new MyCryptoSocket(metrics, channel, factory, isServer); } @Override public SocketChannel channel() { return socket.channel(); } diff --git a/jrt/src/com/yahoo/jrt/NullCryptoEngine.java b/jrt/src/com/yahoo/jrt/NullCryptoEngine.java index 601ca6aff4f..7a7773ed855 100644 --- a/jrt/src/com/yahoo/jrt/NullCryptoEngine.java +++ b/jrt/src/com/yahoo/jrt/NullCryptoEngine.java @@ -9,7 +9,7 @@ import java.nio.channels.SocketChannel; * CryptoEngine implementation that performs no encryption. **/ public class NullCryptoEngine implements CryptoEngine { - @Override public CryptoSocket createCryptoSocket(SocketChannel channel, boolean isServer) { - return new NullCryptoSocket(channel); + @Override public CryptoSocket createCryptoSocket(TransportMetrics metrics, SocketChannel channel, boolean isServer) { + return new NullCryptoSocket(metrics, channel, isServer); } } diff --git a/jrt/src/com/yahoo/jrt/NullCryptoSocket.java b/jrt/src/com/yahoo/jrt/NullCryptoSocket.java index 0397ba8f9fd..1473f288306 100644 --- a/jrt/src/com/yahoo/jrt/NullCryptoSocket.java +++ b/jrt/src/com/yahoo/jrt/NullCryptoSocket.java @@ -11,10 +11,22 @@ import java.nio.channels.SocketChannel; * A CryptoSocket with no encryption **/ public class NullCryptoSocket implements CryptoSocket { + private final boolean isServer; private SocketChannel channel; - public NullCryptoSocket(SocketChannel channel) { this.channel = channel; } + private TransportMetrics metrics; + public NullCryptoSocket(TransportMetrics metrics, SocketChannel channel, boolean isServer) { this.metrics = metrics; this.channel = channel; this.isServer = isServer; } @Override public SocketChannel channel() { return channel; } - @Override public HandshakeResult handshake() throws IOException { return HandshakeResult.DONE; } + @Override public HandshakeResult handshake() throws IOException { + if (metrics != null) { + if (isServer) { + metrics.incrementServerUnencryptedConnectionsEstablished(); + } else { + metrics.incrementClientUnencryptedConnectionsEstablished(); + } + metrics = null; + } + return HandshakeResult.DONE; + } @Override public int getMinimumReadBufferSize() { return 1; } @Override public int read(ByteBuffer dst) throws IOException { return channel.read(dst); } @Override public int drain(ByteBuffer dst) throws IOException { return 0; } diff --git a/jrt/src/com/yahoo/jrt/TlsCryptoEngine.java b/jrt/src/com/yahoo/jrt/TlsCryptoEngine.java index 56fae08a7ef..7e5e6fd9dc4 100644 --- a/jrt/src/com/yahoo/jrt/TlsCryptoEngine.java +++ b/jrt/src/com/yahoo/jrt/TlsCryptoEngine.java @@ -20,15 +20,16 @@ public class TlsCryptoEngine implements CryptoEngine { } @Override - public TlsCryptoSocket createCryptoSocket(SocketChannel channel, boolean isServer) { + public TlsCryptoSocket createCryptoSocket(TransportMetrics metrics, SocketChannel channel, boolean isServer) { SSLEngine sslEngine = tlsContext.createSslEngine(); sslEngine.setNeedClientAuth(true); sslEngine.setUseClientMode(!isServer); - return new TlsCryptoSocket(channel, sslEngine); + return new TlsCryptoSocket(metrics, channel, sslEngine); } @Override public void close() { tlsContext.close(); } + } diff --git a/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java b/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java index b4a8df52e8e..184b8824877 100644 --- a/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java +++ b/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java @@ -7,6 +7,7 @@ import com.yahoo.security.tls.authz.PeerAuthorizerTrustManager; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import java.io.IOException; import java.nio.ByteBuffer; @@ -30,6 +31,7 @@ public class TlsCryptoSocket implements CryptoSocket { private enum HandshakeState { NOT_STARTED, NEED_READ, NEED_WRITE, COMPLETED } + private final TransportMetrics metrics; private final SocketChannel channel; private final SSLEngine sslEngine; private final Buffer wrapBuffer; @@ -40,7 +42,8 @@ public class TlsCryptoSocket implements CryptoSocket { private HandshakeState handshakeState; private AuthorizationResult authorizationResult; - public TlsCryptoSocket(SocketChannel channel, SSLEngine sslEngine) { + public TlsCryptoSocket(TransportMetrics metrics, SocketChannel channel, SSLEngine sslEngine) { + this.metrics = metrics; this.channel = channel; this.sslEngine = sslEngine; SSLSession nullSession = sslEngine.getSession(); @@ -71,52 +74,69 @@ public class TlsCryptoSocket implements CryptoSocket { } private HandshakeState processHandshakeState(HandshakeState state) throws IOException { - switch (state) { - case NOT_STARTED: - log.fine(() -> "Initiating handshake"); - sslEngine.beginHandshake(); - break; - case NEED_WRITE: - channelWrite(); - break; - case NEED_READ: - channelRead(); - break; - case COMPLETED: - return HandshakeState.COMPLETED; - default: - throw unhandledStateException(state); - } - - while (true) { - log.fine(() -> "SSLEngine.getHandshakeStatus(): " + sslEngine.getHandshakeStatus()); - switch (sslEngine.getHandshakeStatus()) { - case NOT_HANDSHAKING: - if (wrapBuffer.bytes() > 0) return HandshakeState.NEED_WRITE; - sslEngine.setEnableSessionCreation(false); // disable renegotiation - handshakeDummyBuffer = null; - SSLSession session = sslEngine.getSession(); - sessionApplicationBufferSize = session.getApplicationBufferSize(); - sessionPacketBufferSize = session.getPacketBufferSize(); - log.fine(() -> String.format("Handshake complete: protocol=%s, cipherSuite=%s", session.getProtocol(), session.getCipherSuite())); - return HandshakeState.COMPLETED; - case NEED_TASK: - sslEngine.getDelegatedTask().run(); - if (authorizationResult != null) { - PeerAuthorizerTrustManager.getAuthorizationResult(sslEngine) // only available during handshake - .ifPresent(result -> this.authorizationResult = result); - } + try { + switch (state) { + case NOT_STARTED: + log.fine(() -> "Initiating handshake"); + sslEngine.beginHandshake(); break; - case NEED_UNWRAP: - if (wrapBuffer.bytes() > 0) return HandshakeState.NEED_WRITE; - if (!handshakeUnwrap()) return HandshakeState.NEED_READ; + case NEED_WRITE: + channelWrite(); break; - case NEED_WRAP: - if (!handshakeWrap()) return HandshakeState.NEED_WRITE; + case NEED_READ: + channelRead(); break; + case COMPLETED: + return HandshakeState.COMPLETED; default: - throw new IllegalStateException("Unexpected handshake status: " + sslEngine.getHandshakeStatus()); + throw unhandledStateException(state); + } + while (true) { + log.fine(() -> "SSLEngine.getHandshakeStatus(): " + sslEngine.getHandshakeStatus()); + switch (sslEngine.getHandshakeStatus()) { + case NOT_HANDSHAKING: + if (wrapBuffer.bytes() > 0) return HandshakeState.NEED_WRITE; + sslEngine.setEnableSessionCreation(false); // disable renegotiation + handshakeDummyBuffer = null; + SSLSession session = sslEngine.getSession(); + sessionApplicationBufferSize = session.getApplicationBufferSize(); + sessionPacketBufferSize = session.getPacketBufferSize(); + log.fine(() -> String.format("Handshake complete: protocol=%s, cipherSuite=%s", session.getProtocol(), session.getCipherSuite())); + if (sslEngine.getUseClientMode()) { + metrics.incrementClientTlsConnectionsEstablished(); + } else { + metrics.incrementServerTlsConnectionsEstablished(); + } + return HandshakeState.COMPLETED; + case NEED_TASK: + sslEngine.getDelegatedTask().run(); + if (authorizationResult != null) { + PeerAuthorizerTrustManager.getAuthorizationResult(sslEngine) // only available during handshake + .ifPresent(result -> { + if (!result.succeeded()) { + metrics.incrementPeerAuthorizationFailures(); + } + authorizationResult = result; + }); + } + break; + case NEED_UNWRAP: + if (wrapBuffer.bytes() > 0) return HandshakeState.NEED_WRITE; + if (!handshakeUnwrap()) return HandshakeState.NEED_READ; + break; + case NEED_WRAP: + if (!handshakeWrap()) return HandshakeState.NEED_WRITE; + break; + default: + throw new IllegalStateException("Unexpected handshake status: " + sslEngine.getHandshakeStatus()); + } + } + } catch (SSLHandshakeException e) { + // sslEngine.getDelegatedTask().run() and handshakeWrap() may throw SSLHandshakeException, potentially handshakeUnwrap() and sslEngine.beginHandshake() as well. + if (authorizationResult == null || authorizationResult.succeeded()) { // don't include handshake failures due from PeerAuthorizerTrustManager + metrics.incrementTlsCertificateVerificationFailures(); } + throw e; } } diff --git a/jrt/src/com/yahoo/jrt/Transport.java b/jrt/src/com/yahoo/jrt/Transport.java index ecad5bfb8a1..717c39d403b 100644 --- a/jrt/src/com/yahoo/jrt/Transport.java +++ b/jrt/src/com/yahoo/jrt/Transport.java @@ -77,6 +77,7 @@ public class Transport { private Scheduler scheduler; private int state; private Selector selector; + private final TransportMetrics metrics = new TransportMetrics(); private void handleAddConnection(Connection conn) { if (conn.isClosed()) { @@ -196,7 +197,7 @@ public class Transport { * @param isServer flag indicating which end of the connection we are **/ CryptoSocket createCryptoSocket(SocketChannel channel, boolean isServer) { - return cryptoEngine.createCryptoSocket(channel, isServer); + return cryptoEngine.createCryptoSocket(metrics, channel, isServer); } /** @@ -409,4 +410,8 @@ public class Transport { } catch (InterruptedException e) {} } } + + public TransportMetrics metrics() { + return metrics; + } } diff --git a/jrt/src/com/yahoo/jrt/TransportMetrics.java b/jrt/src/com/yahoo/jrt/TransportMetrics.java new file mode 100644 index 00000000000..e0afbc495e7 --- /dev/null +++ b/jrt/src/com/yahoo/jrt/TransportMetrics.java @@ -0,0 +1,79 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jrt; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Metric values produced by {@link Transport}. + * + * @author bjorncs + */ +public class TransportMetrics { + + private final AtomicLong tlsCertificateVerificationFailures = new AtomicLong(0); + private final AtomicLong peerAuthorizationFailures = new AtomicLong(0); + private final AtomicLong serverTlsConnectionsEstablished = new AtomicLong(0); + private final AtomicLong clientTlsConnectionsEstablished = new AtomicLong(0); + private final AtomicLong serverUnencryptedConnectionsEstablished = new AtomicLong(0); + private final AtomicLong clientUnencryptedConnectionsEstablished = new AtomicLong(0); + + public long tlsCertificateVerificationFailures() { + return tlsCertificateVerificationFailures.get(); + } + + public long peerAuthorizationFailures() { + return peerAuthorizationFailures.get(); + } + + public long serverTlsConnectionsEstablished() { + return serverTlsConnectionsEstablished.get(); + } + + public long clientTlsConnectionsEstablished() { + return clientTlsConnectionsEstablished.get(); + } + + public long serverUnencryptedConnectionsEstablished() { + return serverUnencryptedConnectionsEstablished.get(); + } + + public long clientUnencryptedConnectionsEstablished() { + return clientUnencryptedConnectionsEstablished.get(); + } + + void incrementTlsCertificateVerificationFailures() { + tlsCertificateVerificationFailures.incrementAndGet(); + } + + void incrementPeerAuthorizationFailures() { + peerAuthorizationFailures.incrementAndGet(); + } + + void incrementServerTlsConnectionsEstablished() { + serverTlsConnectionsEstablished.incrementAndGet(); + } + + void incrementClientTlsConnectionsEstablished() { + clientTlsConnectionsEstablished.incrementAndGet(); + } + + void incrementServerUnencryptedConnectionsEstablished() { + serverUnencryptedConnectionsEstablished.incrementAndGet(); + } + + void incrementClientUnencryptedConnectionsEstablished() { + clientUnencryptedConnectionsEstablished.incrementAndGet(); + } + + @Override + public String toString() { + return "TransportMetrics{" + + "tlsCertificateVerificationFailures=" + tlsCertificateVerificationFailures + + ", peerAuthorizationFailures=" + peerAuthorizationFailures + + ", serverTlsConnectionsEstablished=" + serverTlsConnectionsEstablished + + ", clientTlsConnectionsEstablished=" + clientTlsConnectionsEstablished + + ", serverUnencryptedConnectionsEstablished=" + serverUnencryptedConnectionsEstablished + + ", clientUnencryptedConnectionsEstablished=" + clientUnencryptedConnectionsEstablished + + '}'; + } +} diff --git a/jrt/src/com/yahoo/jrt/XorCryptoEngine.java b/jrt/src/com/yahoo/jrt/XorCryptoEngine.java index 4ba6d00faa4..6912a58e394 100644 --- a/jrt/src/com/yahoo/jrt/XorCryptoEngine.java +++ b/jrt/src/com/yahoo/jrt/XorCryptoEngine.java @@ -11,7 +11,7 @@ import java.nio.channels.SocketChannel; * from TLS. **/ public class XorCryptoEngine implements CryptoEngine { - @Override public CryptoSocket createCryptoSocket(SocketChannel channel, boolean isServer) { + @Override public CryptoSocket createCryptoSocket(TransportMetrics metrics, SocketChannel channel, boolean isServer) { return new XorCryptoSocket(channel, isServer); } } |