Return-Path: X-Original-To: apmail-cassandra-commits-archive@www.apache.org Delivered-To: apmail-cassandra-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B2A4F182E7 for ; Wed, 28 Oct 2015 13:24:18 +0000 (UTC) Received: (qmail 76794 invoked by uid 500); 28 Oct 2015 13:24:18 -0000 Delivered-To: apmail-cassandra-commits-archive@cassandra.apache.org Received: (qmail 76759 invoked by uid 500); 28 Oct 2015 13:24:17 -0000 Mailing-List: contact commits-help@cassandra.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cassandra.apache.org Delivered-To: mailing list commits@cassandra.apache.org Received: (qmail 76733 invoked by uid 99); 28 Oct 2015 13:24:16 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 28 Oct 2015 13:24:16 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 28DA9DFCCE; Wed, 28 Oct 2015 13:24:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: snazy@apache.org To: commits@cassandra.apache.org Date: Wed, 28 Oct 2015 13:24:13 -0000 Message-Id: <8f9c884232964218ad1d6f33e0d89cae@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] cassandra git commit: Support encrypted and plain traffic on the same port Repository: cassandra Updated Branches: refs/heads/cassandra-2.2 5a356a706 -> 734a3bfa7 Support encrypted and plain traffic on the same port patch by Norman Maurer; reviewed by Robert Stupp for CASSANDRA-10559 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/32a6f205 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/32a6f205 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/32a6f205 Branch: refs/heads/cassandra-2.2 Commit: 32a6f2059aa3888fc0223cabcdff2ea2c9b97b21 Parents: cedcf07 Author: Norman Maurer Authored: Wed Oct 28 14:17:18 2015 +0100 Committer: Robert Stupp Committed: Wed Oct 28 14:17:18 2015 +0100 ---------------------------------------------------------------------- CHANGES.txt | 1 + NEWS.txt | 2 + conf/cassandra.yaml | 2 + .../cassandra/config/EncryptionOptions.java | 1 + .../org/apache/cassandra/transport/Server.java | 76 ++++++++++++++++++-- 5 files changed, 75 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/32a6f205/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 5b46eac..998dd22 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 2.1.12 + * Support encrypted and plain traffic on the same port (CASSANDRA-10559) * Do STCS in DTCS windows (CASSANDRA-10276) * Don't try to get ancestors from half-renamed sstables (CASSANDRA-10501) * Avoid repetition of JVM_OPTS in debian package (CASSANDRA-10251) http://git-wip-us.apache.org/repos/asf/cassandra/blob/32a6f205/NEWS.txt ---------------------------------------------------------------------- diff --git a/NEWS.txt b/NEWS.txt index 67a545b..c6ea6c0 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -18,6 +18,8 @@ using the provided 'sstableupgrade' tool. New features ------------ + - Native protocol server now allows both SSL and non-SSL connections on + the same port. - Switching racks is no longer an allowed operation on a node which has data. Instead, the node will need to be decommissioned and rebootstrapped. If moving from the SimpleSnitch, make sure the rack containing all current http://git-wip-us.apache.org/repos/asf/cassandra/blob/32a6f205/conf/cassandra.yaml ---------------------------------------------------------------------- diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index e0ef878..0d0282b 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -781,6 +781,8 @@ server_encryption_options: # enable or disable client/server encryption. client_encryption_options: enabled: false + # If enabled and optional is set to true encrypted and unencrypted connections are handled. + optional: false keystore: conf/.keystore keystore_password: cassandra # require_client_auth: false http://git-wip-us.apache.org/repos/asf/cassandra/blob/32a6f205/src/java/org/apache/cassandra/config/EncryptionOptions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/config/EncryptionOptions.java b/src/java/org/apache/cassandra/config/EncryptionOptions.java index 945a15b..31f8b4a 100644 --- a/src/java/org/apache/cassandra/config/EncryptionOptions.java +++ b/src/java/org/apache/cassandra/config/EncryptionOptions.java @@ -36,6 +36,7 @@ public abstract class EncryptionOptions public static class ClientEncryptionOptions extends EncryptionOptions { public boolean enabled = false; + public boolean optional = false; } public static class ServerEncryptionOptions extends EncryptionOptions http://git-wip-us.apache.org/repos/asf/cassandra/blob/32a6f205/src/java/org/apache/cassandra/transport/Server.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/transport/Server.java b/src/java/org/apache/cassandra/transport/Server.java index c21a669..02f17b0 100644 --- a/src/java/org/apache/cassandra/transport/Server.java +++ b/src/java/org/apache/cassandra/transport/Server.java @@ -22,6 +22,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.EnumMap; +import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; @@ -29,9 +30,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; +import io.netty.buffer.ByteBuf; import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollServerSocketChannel; +import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.util.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -170,8 +173,16 @@ public class Server implements CassandraDaemon.Server final EncryptionOptions.ClientEncryptionOptions clientEnc = DatabaseDescriptor.getClientEncryptionOptions(); if (clientEnc.enabled) { - logger.info("Enabling encrypted CQL connections between client and server"); - bootstrap.childHandler(new SecureInitializer(this, clientEnc)); + if (clientEnc.optional) + { + logger.info("Enabling optionally encrypted CQL connections between client and server"); + bootstrap.childHandler(new OptionalSecureInitializer(this, clientEnc)); + } + else + { + logger.info("Enabling encrypted CQL connections between client and server"); + bootstrap.childHandler(new SecureInitializer(this, clientEnc)); + } } else { @@ -309,12 +320,12 @@ public class Server implements CassandraDaemon.Server } } - private static class SecureInitializer extends Initializer + protected abstract static class AbstractSecureIntializer extends Initializer { private final SSLContext sslContext; private final EncryptionOptions encryptionOptions; - public SecureInitializer(Server server, EncryptionOptions encryptionOptions) + protected AbstractSecureIntializer(Server server, EncryptionOptions encryptionOptions) { super(server); this.encryptionOptions = encryptionOptions; @@ -328,14 +339,65 @@ public class Server implements CassandraDaemon.Server } } - protected void initChannel(Channel channel) throws Exception - { + protected final SslHandler createSslHandler() { SSLEngine sslEngine = sslContext.createSSLEngine(); sslEngine.setUseClientMode(false); sslEngine.setEnabledCipherSuites(encryptionOptions.cipher_suites); sslEngine.setNeedClientAuth(encryptionOptions.require_client_auth); sslEngine.setEnabledProtocols(SSLFactory.ACCEPTED_PROTOCOLS); - SslHandler sslHandler = new SslHandler(sslEngine); + return new SslHandler(sslEngine); + } + } + + private static class OptionalSecureInitializer extends AbstractSecureIntializer + { + public OptionalSecureInitializer(Server server, EncryptionOptions encryptionOptions) + { + super(server, encryptionOptions); + } + + protected void initChannel(final Channel channel) throws Exception + { + super.initChannel(channel); + channel.pipeline().addFirst("sslDetectionHandler", new ByteToMessageDecoder() + { + @Override + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception + { + if (byteBuf.readableBytes() < 5) + { + // To detect if SSL must be used we need to have at least 5 bytes, so return here and try again + // once more bytes a ready. + return; + } + if (SslHandler.isEncrypted(byteBuf)) + { + // Connection uses SSL/TLS, replace the detection handler with a SslHandler and so use + // encryption. + SslHandler sslHandler = createSslHandler(); + channelHandlerContext.pipeline().replace(this, "ssl", sslHandler); + } + else + { + // Connection use no TLS/SSL encryption, just remove the detection handler and continue without + // SslHandler in the pipeline. + channelHandlerContext.pipeline().remove(this); + } + } + }); + } + } + + private static class SecureInitializer extends AbstractSecureIntializer + { + public SecureInitializer(Server server, EncryptionOptions encryptionOptions) + { + super(server, encryptionOptions); + } + + protected void initChannel(Channel channel) throws Exception + { + SslHandler sslHandler = createSslHandler(); super.initChannel(channel); channel.pipeline().addFirst("ssl", sslHandler); }