From 65fccded8fc7dac3e24c0d8f991e5caef554e2f4 Mon Sep 17 00:00:00 2001 From: tlrx Date: Wed, 18 Jul 2012 20:59:27 +0200 Subject: [PATCH] Add SSL support to Netty transport layer --- config/elasticsearch.yml | 14 ++ .../transport/SSLTransportException.java | 34 +++ .../transport/netty/NettyTransport.java | 115 ++++++---- .../netty/ssl/SSLChannelPipelineFactory.java | 131 +++++++++++ .../SecureClientChannelPipelineFactory.java | 53 +++++ .../ssl/SecureMessageChannelHandler.java | 49 ++++ .../SecureServerChannelPipelineFactory.java | 58 +++++ .../netty/ssl/NettySSLEchoBenchmark.java | 216 ++++++++++++++++++ src/test/resources/certificates/esnode1.cert | 11 + src/test/resources/certificates/esnode1.jks | Bin 0 -> 2374 bytes src/test/resources/certificates/esnode2.cert | 11 + src/test/resources/certificates/esnode2.jks | Bin 0 -> 2920 bytes src/test/resources/certificates/esnode3.cert | 11 + src/test/resources/certificates/esnode3.jks | Bin 0 -> 2373 bytes src/test/resources/certificates/generate.sh | 58 +++++ 15 files changed, 720 insertions(+), 41 deletions(-) create mode 100644 src/main/java/org/elasticsearch/transport/SSLTransportException.java create mode 100644 src/main/java/org/elasticsearch/transport/netty/ssl/SSLChannelPipelineFactory.java create mode 100644 src/main/java/org/elasticsearch/transport/netty/ssl/SecureClientChannelPipelineFactory.java create mode 100644 src/main/java/org/elasticsearch/transport/netty/ssl/SecureMessageChannelHandler.java create mode 100644 src/main/java/org/elasticsearch/transport/netty/ssl/SecureServerChannelPipelineFactory.java create mode 100644 src/test/java/org/elasticsearch/benchmark/transport/netty/ssl/NettySSLEchoBenchmark.java create mode 100644 src/test/resources/certificates/esnode1.cert create mode 100644 src/test/resources/certificates/esnode1.jks create mode 100644 src/test/resources/certificates/esnode2.cert create mode 100644 src/test/resources/certificates/esnode2.jks create mode 100644 src/test/resources/certificates/esnode3.cert create mode 100644 src/test/resources/certificates/esnode3.jks create mode 100755 src/test/resources/certificates/generate.sh diff --git a/config/elasticsearch.yml b/config/elasticsearch.yml index 6ef5d5ea7b099..1f3e16e3f5725 100644 --- a/config/elasticsearch.yml +++ b/config/elasticsearch.yml @@ -217,6 +217,20 @@ # # transport.tcp.compress: true +# Enable SSL/TLS encryption for all communication between nodes (disabled by default): +# +# transport.tcp.ssl: true + +# Settings for SSL/TLS encryption, used when transport.tcp.ssl is set to true +# +# transport.tcp.ssl.keystore: /path/to/the/keystore +# transport.tcp.ssl.keystore_password: password +# transport.tcp.ssl.keystore_algorithm: SunX509 +# +# transport.tcp.ssl.truststore: /path/to/the/truststore +# transport.tcp.ssl.truststore_password: password +# transport.tcp.ssl.truststore_algorithm: PKIX + # Set a custom port to listen for HTTP traffic: # # http.port: 9200 diff --git a/src/main/java/org/elasticsearch/transport/SSLTransportException.java b/src/main/java/org/elasticsearch/transport/SSLTransportException.java new file mode 100644 index 0000000000000..c22b9df514ca9 --- /dev/null +++ b/src/main/java/org/elasticsearch/transport/SSLTransportException.java @@ -0,0 +1,34 @@ +/* + * Licensed to ElasticSearch and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. ElasticSearch licenses this + * file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.transport; + +/** + * @author Tanguy Leroux - tlrx.dev@gmail.com + */ +public class SSLTransportException extends TransportException { + + public SSLTransportException(String message) { + super(message); + } + + public SSLTransportException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/elasticsearch/transport/netty/NettyTransport.java b/src/main/java/org/elasticsearch/transport/netty/NettyTransport.java index 46b040fdbf2b6..fcef6fdad3ca0 100644 --- a/src/main/java/org/elasticsearch/transport/netty/NettyTransport.java +++ b/src/main/java/org/elasticsearch/transport/netty/NettyTransport.java @@ -43,6 +43,7 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.*; +import org.elasticsearch.transport.netty.ssl.*; import org.elasticsearch.transport.support.TransportStreams; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap; @@ -107,6 +108,14 @@ public class NettyTransport extends AbstractLifecycleComponent implem final Boolean tcpKeepAlive; final Boolean reuseAddress; + + final boolean ssl; + final String sslKeyStore; + final String sslKeyStorePassword; + final String sslKeyStoreAlgorithm; + final String sslTrustStore; + final String sslTrustStorePassword; + final String sslTrustStoreAlgorithm; final ByteSizeValue tcpSendBufferSize; final ByteSizeValue tcpReceiveBufferSize; @@ -177,8 +186,16 @@ public NettyTransport(Settings settings, ThreadPool threadPool, NetworkService n this.maxCumulationBufferCapacity = componentSettings.getAsBytesSize("max_cumulation_buffer_capacity", null); this.maxCompositeBufferComponents = componentSettings.getAsInt("max_composite_buffer_components", -1); - logger.debug("using worker_count[{}], port[{}], bind_host[{}], publish_host[{}], compress[{}], connect_timeout[{}], connections_per_node[{}/{}/{}]", - workerCount, port, bindHost, publishHost, compress, connectTimeout, connectionsPerNodeLow, connectionsPerNodeMed, connectionsPerNodeHigh); + this.ssl = settings.getAsBoolean("transport.tcp.ssl", false); + this.sslKeyStore = settings.get("transport.tcp.ssl.keystore", System.getProperty("javax.net.ssl.keyStore")); + this.sslKeyStorePassword = settings.get("transport.tcp.ssl.keystore_password", System.getProperty("javax.net.ssl.keyStorePassword")); + this.sslKeyStoreAlgorithm = settings.get("transport.tcp.ssl.keystore_algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm")); + this.sslTrustStore = settings.get("transport.tcp.ssl.truststore", System.getProperty("javax.net.ssl.trustStore")); + this.sslTrustStorePassword = settings.get("transport.tcp.ssl.truststore_password", System.getProperty("javax.net.ssl.trustStorePassword")); + this.sslTrustStoreAlgorithm = settings.get("ransport.tcp.ssl.truststore_algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm")); + + logger.debug("using worker_count[{}], port[{}], bind_host[{}], publish_host[{}], compress[{}], connect_timeout[{}], connections_per_node[{}/{}/{}], ssl[{}]", + workerCount, port, bindHost, publishHost, compress, connectTimeout, connectionsPerNodeLow, connectionsPerNodeMed, connectionsPerNodeHigh, ssl); } public Settings settings() { @@ -208,26 +225,34 @@ protected void doStart() throws ElasticSearchException { Executors.newCachedThreadPool(daemonThreadFactory(settings, "transport_client_worker")), workerCount)); } - ChannelPipelineFactory clientPipelineFactory = new ChannelPipelineFactory() { - @Override - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline pipeline = Channels.pipeline(); - SizeHeaderFrameDecoder sizeHeader = new SizeHeaderFrameDecoder(); - if (maxCumulationBufferCapacity != null) { - if (maxCumulationBufferCapacity.bytes() > Integer.MAX_VALUE) { - sizeHeader.setMaxCumulationBufferCapacity(Integer.MAX_VALUE); - } else { - sizeHeader.setMaxCumulationBufferCapacity((int) maxCumulationBufferCapacity.bytes()); + ChannelPipelineFactory clientPipelineFactory = null; + if (ssl) { + clientPipelineFactory = new SecureClientChannelPipelineFactory(new SecureMessageChannelHandler(NettyTransport.this, logger), + sslKeyStore, sslKeyStorePassword, sslKeyStoreAlgorithm, + sslTrustStore, sslTrustStorePassword, sslTrustStoreAlgorithm, + maxCumulationBufferCapacity, maxCompositeBufferComponents); + } else { + clientPipelineFactory = new ChannelPipelineFactory() { + @Override + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = Channels.pipeline(); + SizeHeaderFrameDecoder sizeHeader = new SizeHeaderFrameDecoder(); + if (maxCumulationBufferCapacity != null) { + if (maxCumulationBufferCapacity.bytes() > Integer.MAX_VALUE) { + sizeHeader.setMaxCumulationBufferCapacity(Integer.MAX_VALUE); + } else { + sizeHeader.setMaxCumulationBufferCapacity((int) maxCumulationBufferCapacity.bytes()); + } } + if (maxCompositeBufferComponents != -1) { + sizeHeader.setMaxCumulationBufferComponents(maxCompositeBufferComponents); + } + pipeline.addLast("size", sizeHeader); + pipeline.addLast("dispatcher", new MessageChannelHandler(NettyTransport.this, logger)); + return pipeline; } - if (maxCompositeBufferComponents != -1) { - sizeHeader.setMaxCumulationBufferComponents(maxCompositeBufferComponents); - } - pipeline.addLast("size", sizeHeader); - pipeline.addLast("dispatcher", new MessageChannelHandler(NettyTransport.this, logger)); - return pipeline; - } - }; + }; + } clientBootstrap.setPipelineFactory(clientPipelineFactory); clientBootstrap.setOption("connectTimeoutMillis", connectTimeout.millis()); if (tcpNoDelay != null) { @@ -262,27 +287,35 @@ public ChannelPipeline getPipeline() throws Exception { Executors.newCachedThreadPool(daemonThreadFactory(settings, "transport_server_worker")), workerCount)); } - ChannelPipelineFactory serverPipelineFactory = new ChannelPipelineFactory() { - @Override - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline pipeline = Channels.pipeline(); - pipeline.addLast("openChannels", serverOpenChannels); - SizeHeaderFrameDecoder sizeHeader = new SizeHeaderFrameDecoder(); - if (maxCumulationBufferCapacity != null) { - if (maxCumulationBufferCapacity.bytes() > Integer.MAX_VALUE) { - sizeHeader.setMaxCumulationBufferCapacity(Integer.MAX_VALUE); - } else { - sizeHeader.setMaxCumulationBufferCapacity((int) maxCumulationBufferCapacity.bytes()); - } - } - if (maxCompositeBufferComponents != -1) { - sizeHeader.setMaxCumulationBufferComponents(maxCompositeBufferComponents); - } - pipeline.addLast("size", sizeHeader); - pipeline.addLast("dispatcher", new MessageChannelHandler(NettyTransport.this, logger)); - return pipeline; - } - }; + ChannelPipelineFactory serverPipelineFactory = null; + if (ssl) { + serverPipelineFactory = serverPipelineFactory = new SecureServerChannelPipelineFactory(new SecureMessageChannelHandler(NettyTransport.this, logger), serverOpenChannels, + sslKeyStore, sslKeyStorePassword, sslKeyStoreAlgorithm, + sslTrustStore, sslTrustStorePassword, sslTrustStoreAlgorithm, + maxCumulationBufferCapacity, maxCompositeBufferComponents); + } else { + serverPipelineFactory = new ChannelPipelineFactory() { + @Override + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("openChannels", serverOpenChannels); + SizeHeaderFrameDecoder sizeHeader = new SizeHeaderFrameDecoder(); + if (maxCumulationBufferCapacity != null) { + if (maxCumulationBufferCapacity.bytes() > Integer.MAX_VALUE) { + sizeHeader.setMaxCumulationBufferCapacity(Integer.MAX_VALUE); + } else { + sizeHeader.setMaxCumulationBufferCapacity((int) maxCumulationBufferCapacity.bytes()); + } + } + if (maxCompositeBufferComponents != -1) { + sizeHeader.setMaxCumulationBufferComponents(maxCompositeBufferComponents); + } + pipeline.addLast("size", sizeHeader); + pipeline.addLast("dispatcher", new MessageChannelHandler(NettyTransport.this, logger)); + return pipeline; + } + }; + } serverBootstrap.setPipelineFactory(serverPipelineFactory); if (tcpNoDelay != null) { serverBootstrap.setOption("child.tcpNoDelay", tcpNoDelay); diff --git a/src/main/java/org/elasticsearch/transport/netty/ssl/SSLChannelPipelineFactory.java b/src/main/java/org/elasticsearch/transport/netty/ssl/SSLChannelPipelineFactory.java new file mode 100644 index 0000000000000..8aa7eaf505669 --- /dev/null +++ b/src/main/java/org/elasticsearch/transport/netty/ssl/SSLChannelPipelineFactory.java @@ -0,0 +1,131 @@ +package org.elasticsearch.transport.netty.ssl; + +import java.io.FileInputStream; +import java.security.KeyStore; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.jboss.netty.channel.ChannelPipelineFactory; + +/** + * ChannelPipelineFactory used for Client/Server SSL channel pipelines + * + * @author Tanguy Leroux + * + */ +public abstract class SSLChannelPipelineFactory implements ChannelPipelineFactory { + + private static final ESLogger logger = Loggers.getLogger(SSLChannelPipelineFactory.class); + + private SSLContext sslContext; + + SecureMessageChannelHandler messageChannelHandler; + + private String keyStore; + + private String keyStorePassword; + + private String keyStoreAlgorithm; + + private String trustStore; + + private String trustStorePassword; + + private String trustStoreAlgorithm; + + ByteSizeValue maxCumulationBufferCapacity; + + int maxCompositeBufferComponents; + + public SSLChannelPipelineFactory(SecureMessageChannelHandler channelHandler, + String sslKeyStore, String sslKeyStorePassword, String sslKeyStoreAlgorithm, + String sslTrustStore, String sslTrustStorePassword, String sslTrustStoreAlgorithm, + ByteSizeValue mCumulationBufferCapacity, int mCompositeBufferComponents) { + + messageChannelHandler = channelHandler; + maxCumulationBufferCapacity = mCumulationBufferCapacity; + maxCompositeBufferComponents = mCompositeBufferComponents; + + keyStore = sslKeyStore; + keyStorePassword = sslKeyStorePassword; + if (sslKeyStoreAlgorithm != null) { + keyStoreAlgorithm = sslKeyStoreAlgorithm; + } else { + keyStoreAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + } + + trustStore = sslTrustStore; + trustStorePassword = sslTrustStorePassword; + if (sslTrustStoreAlgorithm != null) { + trustStoreAlgorithm = sslTrustStoreAlgorithm; + } else { + trustStoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + } + + logger.debug("using keyStore[{}], keyAlgorithm[{}], trustStore[{}], trustAlgorithm[{}]", keyStore, keyStoreAlgorithm, trustStore, trustStoreAlgorithm); + + KeyStore ks = null; + KeyManagerFactory kmf = null; + FileInputStream in = null; + try { + // Load KeyStore + ks = KeyStore.getInstance("jks"); + in = new FileInputStream(keyStore); + ks.load(in, keyStorePassword.toCharArray()); + + // Initialize KeyManagerFactory + kmf = KeyManagerFactory.getInstance(keyStoreAlgorithm); + kmf.init(ks, keyStorePassword.toCharArray()); + } catch (Exception e) { + throw new Error("Failed to initialize a KeyManagerFactory", e); + } finally { + try { + in.close(); + } catch (Exception e2) { + } + } + + TrustManager[] trustManagers = null; + try { + // Load TrustStore + in = new FileInputStream(trustStore); + ks.load(in, trustStorePassword.toCharArray()); + + // Initialize a trust manager factory with the trusted store + TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm); + trustFactory.init(ks); + + // Retrieve the trust managers from the factory + trustManagers = trustFactory.getTrustManagers(); + } catch (Exception e) { + throw new Error("Failed to initialize a TrustManagerFactory", e); + } finally { + try { + in.close(); + } catch (Exception e2) { + } + } + + // Initialize sslContext + try { + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), trustManagers, null); + } catch (Exception e) { + throw new Error("Failed to initialize the SSLContext", e); + } + } + + public SSLContext getSslContext() { + return sslContext; + } + + public void setSslContext(SSLContext sslContext) { + this.sslContext = sslContext; + } +} diff --git a/src/main/java/org/elasticsearch/transport/netty/ssl/SecureClientChannelPipelineFactory.java b/src/main/java/org/elasticsearch/transport/netty/ssl/SecureClientChannelPipelineFactory.java new file mode 100644 index 0000000000000..4d2a8e8d63a2e --- /dev/null +++ b/src/main/java/org/elasticsearch/transport/netty/ssl/SecureClientChannelPipelineFactory.java @@ -0,0 +1,53 @@ +package org.elasticsearch.transport.netty.ssl; + +import javax.net.ssl.SSLEngine; + +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.transport.netty.SizeHeaderFrameDecoder; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.handler.ssl.SslHandler; + +/** + * ChannelPipelineFactory used for Client SSL channel pipelines + * + * @author Tanguy Leroux + * + */ +public class SecureClientChannelPipelineFactory extends SSLChannelPipelineFactory { + + public SecureClientChannelPipelineFactory(SecureMessageChannelHandler channelHandler, + String sslKeyStore, String sslKeyStorePassword, String sslKeyStoreAlgorithm, + String sslTrustStore, String sslTrustStorePassword, String sslTrustStoreAlgorithm, + ByteSizeValue maxCumulationBufferCapacity, int maxCompositeBufferComponents) { + super(channelHandler, + sslKeyStore, sslKeyStorePassword, sslKeyStoreAlgorithm, + sslTrustStore, sslTrustStorePassword, sslTrustStoreAlgorithm, + maxCumulationBufferCapacity, maxCompositeBufferComponents); + } + + @Override + public ChannelPipeline getPipeline() throws Exception { + SSLEngine engine = getSslContext().createSSLEngine(); + engine.setUseClientMode(true); + + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("ssl", new SslHandler(engine)); + SizeHeaderFrameDecoder sizeHeader = new SizeHeaderFrameDecoder(); + if (maxCumulationBufferCapacity != null) { + if (maxCumulationBufferCapacity.bytes() > Integer.MAX_VALUE) { + sizeHeader.setMaxCumulationBufferCapacity(Integer.MAX_VALUE); + } else { + sizeHeader.setMaxCumulationBufferCapacity((int) maxCumulationBufferCapacity.bytes()); + } + } + if (maxCompositeBufferComponents != -1) { + sizeHeader.setMaxCumulationBufferComponents(maxCompositeBufferComponents); + } + pipeline.addLast("size", sizeHeader); + pipeline.addLast("dispatcher", messageChannelHandler); + + + return pipeline; + } +} diff --git a/src/main/java/org/elasticsearch/transport/netty/ssl/SecureMessageChannelHandler.java b/src/main/java/org/elasticsearch/transport/netty/ssl/SecureMessageChannelHandler.java new file mode 100644 index 0000000000000..59bcf2a4a3e31 --- /dev/null +++ b/src/main/java/org/elasticsearch/transport/netty/ssl/SecureMessageChannelHandler.java @@ -0,0 +1,49 @@ +package org.elasticsearch.transport.netty.ssl; + +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.transport.SSLTransportException; +import org.elasticsearch.transport.netty.MessageChannelHandler; +import org.elasticsearch.transport.netty.NettyTransport; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.handler.ssl.SslHandler; + +/** + * SSL message channel handler + * + * @author Tanguy Leroux + * + */ +public class SecureMessageChannelHandler extends MessageChannelHandler { + + private static final ESLogger logger = Loggers.getLogger(SecureMessageChannelHandler.class); + + public SecureMessageChannelHandler(NettyTransport transport, ESLogger logger) { + super(transport, logger); + } + + @Override + public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception { + SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class); + sslHandler.handshake(); + + // Get notified when SSL handshake is done. + final ChannelFuture handshakeFuture = sslHandler.handshake(); + handshakeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + logger.debug("SSL / TLS handshake completed for the channel."); + ctx.sendUpstream(e); + } else { + logger.error("SSL / TLS handshake failed, closing the channel"); + future.getChannel().close(); + throw new SSLTransportException("SSL / TLS handshake failed, closing the channel", future.getCause()); + } + } + }); + } +} diff --git a/src/main/java/org/elasticsearch/transport/netty/ssl/SecureServerChannelPipelineFactory.java b/src/main/java/org/elasticsearch/transport/netty/ssl/SecureServerChannelPipelineFactory.java new file mode 100644 index 0000000000000..bc1cc791c59f1 --- /dev/null +++ b/src/main/java/org/elasticsearch/transport/netty/ssl/SecureServerChannelPipelineFactory.java @@ -0,0 +1,58 @@ +package org.elasticsearch.transport.netty.ssl; + +import javax.net.ssl.SSLEngine; + +import org.elasticsearch.common.netty.OpenChannelsHandler; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.transport.netty.SizeHeaderFrameDecoder; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.handler.ssl.SslHandler; + +/** + * ChannelPipelineFactory used for Server SSL channel pipelines + * + * @author Tanguy Leroux + * + */ +public class SecureServerChannelPipelineFactory extends SSLChannelPipelineFactory { + + private OpenChannelsHandler serverOpenChannels; + + public SecureServerChannelPipelineFactory(SecureMessageChannelHandler channelHandler, OpenChannelsHandler openChannels, + String sslKeyStore, String sslKeyStorePassword, String sslKeyStoreAlgorithm, + String sslTrustStore, String sslTrustStorePassword, String sslTrustStoreAlgorithm, + ByteSizeValue maxCumulationBufferCapacity, int maxCompositeBufferComponents) { + + super(channelHandler, sslKeyStore, sslKeyStorePassword, sslKeyStoreAlgorithm, + sslTrustStore, sslTrustStorePassword, sslTrustStoreAlgorithm, + maxCumulationBufferCapacity, maxCompositeBufferComponents); + this.serverOpenChannels = openChannels; + } + + @Override + public ChannelPipeline getPipeline() throws Exception { + SSLEngine engine = getSslContext().createSSLEngine(); + engine.setUseClientMode(false); + engine.setNeedClientAuth(true); + + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("ssl", new SslHandler(engine)); + pipeline.addLast("openChannels", serverOpenChannels); + SizeHeaderFrameDecoder sizeHeader = new SizeHeaderFrameDecoder(); + if (maxCumulationBufferCapacity != null) { + if (maxCumulationBufferCapacity.bytes() > Integer.MAX_VALUE) { + sizeHeader.setMaxCumulationBufferCapacity(Integer.MAX_VALUE); + } else { + sizeHeader.setMaxCumulationBufferCapacity((int) maxCumulationBufferCapacity.bytes()); + } + } + if (maxCompositeBufferComponents != -1) { + sizeHeader.setMaxCumulationBufferComponents(maxCompositeBufferComponents); + } + pipeline.addLast("size", sizeHeader); + pipeline.addLast("dispatcher", messageChannelHandler); + return pipeline; + + } +} diff --git a/src/test/java/org/elasticsearch/benchmark/transport/netty/ssl/NettySSLEchoBenchmark.java b/src/test/java/org/elasticsearch/benchmark/transport/netty/ssl/NettySSLEchoBenchmark.java new file mode 100644 index 0000000000000..4af0473b5ce16 --- /dev/null +++ b/src/test/java/org/elasticsearch/benchmark/transport/netty/ssl/NettySSLEchoBenchmark.java @@ -0,0 +1,216 @@ +/* + * Licensed to ElasticSearch and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. ElasticSearch licenses this + * file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.benchmark.transport.netty.ssl; + +import org.elasticsearch.transport.SSLTransportException; +import org.elasticsearch.transport.netty.ssl.SSLChannelPipelineFactory; +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.*; +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.jboss.netty.handler.ssl.SslHandler; + +import java.net.InetSocketAddress; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; + +import javax.net.ssl.SSLEngine; + +public class NettySSLEchoBenchmark { + + private static final String CERTIFICATES_DIR = "./src/test/resources/certificates/"; + + public static void main(String[] args) { + final int payloadSize = 100; + int CYCLE_SIZE = 50000; + final long NUMBER_OF_ITERATIONS = 500000; + + ChannelBuffer message = ChannelBuffers.buffer(100); + for (int i = 0; i < message.capacity(); i++) { + message.writeByte((byte) i); + } + + // Configure the server. + ServerBootstrap serverBootstrap = new ServerBootstrap( + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool())); + + // Set up the pipeline factory. + serverBootstrap.setPipelineFactory(new SSLChannelPipelineFactory(null, + CERTIFICATES_DIR + "esnode1.jks", "esnode1", null, + CERTIFICATES_DIR + "esnode1.jks", "esnode1", null, + null, 0) { + public ChannelPipeline getPipeline() throws Exception { + SSLEngine engine = getSslContext().createSSLEngine(); + engine.setUseClientMode(false); + engine.setNeedClientAuth(true); + return Channels.pipeline(new SslHandler(engine), new EchoServerHandler()); + } + }); + + // Bind and start to accept incoming connections. + serverBootstrap.bind(new InetSocketAddress(9000)); + + ClientBootstrap clientBootstrap = new ClientBootstrap( + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool())); + +// ClientBootstrap clientBootstrap = new ClientBootstrap( +// new OioClientSocketChannelFactory(Executors.newCachedThreadPool())); + + // Set up the pipeline factory. + final EchoClientHandler clientHandler = new EchoClientHandler(); + clientBootstrap.setPipelineFactory(new SSLChannelPipelineFactory(null, + CERTIFICATES_DIR + "esnode2.jks", "esnode2", null, + CERTIFICATES_DIR + "esnode2.jks", "esnode2", null, + null, 0) { + public ChannelPipeline getPipeline() throws Exception { + SSLEngine engine = getSslContext().createSSLEngine(); + engine.setUseClientMode(true); + return Channels.pipeline(new SslHandler(engine), clientHandler); + } + }); + + // Start the connection attempt. + ChannelFuture future = clientBootstrap.connect(new InetSocketAddress("localhost", 9000)); + future.awaitUninterruptibly(); + Channel clientChannel = future.getChannel(); + + System.out.println("Warming up..."); + for (long i = 0; i < 10000; i++) { + clientHandler.latch = new CountDownLatch(1); + clientChannel.write(message); + try { + clientHandler.latch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + System.out.println("Warmed up"); + + + long start = System.currentTimeMillis(); + long cycleStart = System.currentTimeMillis(); + for (long i = 1; i < NUMBER_OF_ITERATIONS; i++) { + clientHandler.latch = new CountDownLatch(1); + clientChannel.write(message); + try { + clientHandler.latch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if ((i % CYCLE_SIZE) == 0) { + long cycleEnd = System.currentTimeMillis(); + System.out.println("Ran 50000, TPS " + (CYCLE_SIZE / ((double) (cycleEnd - cycleStart) / 1000))); + cycleStart = cycleEnd; + } + } + long end = System.currentTimeMillis(); + long seconds = (end - start) / 1000; + System.out.println("Ran [" + NUMBER_OF_ITERATIONS + "] iterations, payload [" + payloadSize + "]: took [" + seconds + "], TPS: " + ((double) NUMBER_OF_ITERATIONS) / seconds); + + clientChannel.close().awaitUninterruptibly(); + clientBootstrap.releaseExternalResources(); + serverBootstrap.releaseExternalResources(); + } + + public static class EchoClientHandler extends SimpleChannelUpstreamHandler { + + public volatile CountDownLatch latch; + + public EchoClientHandler() { + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { + latch.countDown(); + } + + @Override + public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception { + SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class); + sslHandler.handshake(); + + // Get notified when SSL handshake is done. + final ChannelFuture handshakeFuture = sslHandler.handshake(); + handshakeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + System.out.println("Handshake completed on client side"); + ctx.sendUpstream(e); + } else { + System.out.println("Handshake failed on client side"); + future.getChannel().close(); + throw new SSLTransportException("SSL / TLS handshake failed, closing the channel", future.getCause()); + } + } + }); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + e.getCause().printStackTrace(); + e.getChannel().close(); + } + } + + + public static class EchoServerHandler extends SimpleChannelUpstreamHandler { + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { + e.getChannel().write(e.getMessage()); + } + + public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception { + SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class); + sslHandler.handshake(); + + // Get notified when SSL handshake is done. + final ChannelFuture handshakeFuture = sslHandler.handshake(); + handshakeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + System.out.println("Handshake completed on server side"); + ctx.sendUpstream(e); + } else { + System.out.println("Handshake failed on server side"); + future.getChannel().close(); + throw new SSLTransportException("SSL / TLS handshake failed, closing the channel", future.getCause()); + } + } + }); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + // Close the connection when an exception is raised. + e.getCause().printStackTrace(); + e.getChannel().close(); + } + } +} \ No newline at end of file diff --git a/src/test/resources/certificates/esnode1.cert b/src/test/resources/certificates/esnode1.cert new file mode 100644 index 0000000000000..47a32701e2d61 --- /dev/null +++ b/src/test/resources/certificates/esnode1.cert @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIB/TCCAWagAwIBAgIEUAZ4+TANBgkqhkiG9w0BAQUFADBDMQwwCgYDVQQKEwNvcmcxFjAUBgNV +BAsTDWVsYXN0aWNzZWFyY2gxGzAZBgNVBAMTEkVsYXN0aWNzZWFyY2ggTm9kZTAeFw0xMjA3MTgw +ODUxMDVaFw0xMjEwMTYwODUxMDVaMEMxDDAKBgNVBAoTA29yZzEWMBQGA1UECxMNZWxhc3RpY3Nl +YXJjaDEbMBkGA1UEAxMSRWxhc3RpY3NlYXJjaCBOb2RlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCVWUbN1yPz8Gy9j49vuatDROzjgkhFvHQikK4uH4WvZdVDt57uf8wzVkk+pWzQki6Xcchv +wINrjh7lqmQV2LLedhE4R5lE6NuPHDHovncx2VweL+Vi4KuTzPdg7GxgbKXERZ/LLpdQfQGAvkqn +EtWugWJXkY5fj63NCVX9VpwJDwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADYkAloVoHZ+ijZhwKGz +99uimzHy52ThJhVVFfMFFz5vCo8Neo63+RskxsBbmvWluTajNvxFfgY/6rfeqZen1JmP7kGdVBQ/ +Y9zvc6EZSkpOfegTZR6y0OrFiTGaQL0pgo8UUUjiB1yvYgFfz16E/sYHfWIDlojFY1jiWeaGq9bO +-----END CERTIFICATE----- diff --git a/src/test/resources/certificates/esnode1.jks b/src/test/resources/certificates/esnode1.jks new file mode 100644 index 0000000000000000000000000000000000000000..c91e71ceb13ff0ae8178e9602a601dd8662c4d08 GIT binary patch literal 2374 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/certificates/generate.sh b/src/test/resources/certificates/generate.sh new file mode 100755 index 0000000000000..6c1ab2657e935 --- /dev/null +++ b/src/test/resources/certificates/generate.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Number of nodes key pair to create +nb=3 + +prefix="esnode" + +die () { + echo >&2 "$@" + exit 1 +} + +[ "$#" -eq 1 ] || die "1 argument required, $# provided" +echo $1 | grep -E -q '^[0-9]+$' || die "Numeric argument required, $1 provided" + +nb=$1 + +rm ./$prefix*.jks +rm ./$prefix*.cert + +# Create key pair and certificate for every node +nb_nodes=1 +while [ $nb_nodes -le $nb ] +do + node_name=$prefix$nb_nodes + + echo "Create" $node_name "key pair:" + keytool -genkeypair -alias $node_name -keystore $node_name.jks -keyalg RSA -storepass $node_name -keypass $node_name -dname "cn=Elasticsearch Node, ou=elasticsearch, o=org" + + echo "Generate" $node_name "certificate:" + keytool -export -alias $node_name -keystore $node_name.jks -rfc -file $node_name.cert -storepass $node_name + + nb_nodes=$(( $nb_nodes + 1 )) +done + +# Import certificates in nodes +current_node=1 +while [ $current_node -le $nb ] +do + node_name=$prefix$current_node + import_node=1 + + while [ $import_node -le $nb ] + do + import_node_name=$prefix$import_node + if [ "$import_node" -ne "$current_node" ] + then + echo "Importing" $node_name "certificate into" $import_node_name "keystore" + keytool -import -trustcacerts -alias $node_name -file $node_name.cert -keystore $import_node_name.jks -storepass $import_node_name -noprompt + fi + import_node=$(( $import_node + 1 )) + done + + current_node=$(( $current_node + 1 )) +done + +exit; +