This is an automated email from the ASF dual-hosted git repository.

feiwang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-kyuubi.git


The following commit(s) were added to refs/heads/master by this push:
     new 7bcb522d6 [KYUUBI #3658] Support SSL for Kyuubi thrift binary 
connection
7bcb522d6 is described below

commit 7bcb522d6e26bf1fb8403620da02d79e6af05d91
Author: Fei Wang <[email protected]>
AuthorDate: Mon Oct 24 21:39:31 2022 +0800

    [KYUUBI #3658] Support SSL for Kyuubi thrift binary connection
    
    ### _Why are the changes needed?_
    
    Support SSL for kyuubi thrift binary conenction.
    ### _How was this patch tested?_
    - [ ] Add some test cases that check the changes thoroughly including 
negative and positive cases if possible
    
    - [ ] Add screenshots for manual tests if appropriate
    Will add screenshot for IT.
    
    - [ ] [Run 
test](https://kyuubi.apache.org/docs/latest/develop_tools/testing.html#running-tests)
 locally before make a pull request
    
    Closes #3658 from turboFei/ssl_binary.
    
    Closes #3658
    
    ffd603ec [Fei Wang] comment
    ce652b7e [Fei Wang] revert UT
    52e24abb [Fei Wang] ut
    f6cae357 [Fei Wang] save
    c6b664f1 [Fei Wang] exclude passwords
    d34a4ca2 [Fei Wang] comments
    11bfa9ad [Fei Wang] refactor
    e2ca77ea [Fei Wang] align
    86aaf87c [Fei Wang] with alternative
    d17e0d11 [Fei Wang] style
    eb4ba52a [Fei Wang] Support SSL for Kyuubi thrift binary connection
    
    Authored-by: Fei Wang <[email protected]>
    Signed-off-by: Fei Wang <[email protected]>
---
 docs/deployment/settings.md                        | 10 ++-
 .../org/apache/kyuubi/config/KyuubiConf.scala      | 70 +++++++++++++++++++-
 .../kyuubi/service/TBinaryFrontendService.scala    | 76 +++++++++++++++++++++-
 .../kyuubi/server/KyuubiTHttpFrontendService.scala | 12 +++-
 4 files changed, 161 insertions(+), 7 deletions(-)

diff --git a/docs/deployment/settings.md b/docs/deployment/settings.md
index caaae3d9d..a9976bb93 100644
--- a/docs/deployment/settings.md
+++ b/docs/deployment/settings.md
@@ -297,9 +297,16 @@ kyuubi.frontend.protocols|THRIFT_BINARY|A comma separated 
list for all frontend
 kyuubi.frontend.proxy.http.client.ip.header|X-Real-IP|The http header to 
record the real client ip address. If your server is behind a load balancer or 
other proxy, the server will see this load balancer or proxy IP address as the 
client IP address, to get around this common issue, most load balancers or 
proxies offer the ability to record the real remote IP address in an HTTP 
header that will be added to the request for other devices to use. Note that, 
because the header value can be sp [...]
 kyuubi.frontend.rest.bind.host|&lt;undefined&gt;|Hostname or IP of the machine 
on which to run the REST frontend service.|string|1.4.0
 kyuubi.frontend.rest.bind.port|10099|Port of the machine on which to run the 
REST frontend service.|int|1.4.0
+kyuubi.frontend.ssl.keystore.algorithm|&lt;undefined&gt;|SSL certificate 
keystore algorithm.|string|1.7.0
+kyuubi.frontend.ssl.keystore.password|&lt;undefined&gt;|SSL certificate 
keystore password.|string|1.7.0
+kyuubi.frontend.ssl.keystore.path|&lt;undefined&gt;|SSL certificate keystore 
location.|string|1.7.0
+kyuubi.frontend.ssl.keystore.type|&lt;undefined&gt;|SSL certificate keystore 
type.|string|1.7.0
 kyuubi.frontend.thrift.backoff.slot.length|PT0.1S|Time to back off during 
login to the thrift frontend service.|duration|1.4.0
 kyuubi.frontend.thrift.binary.bind.host|&lt;undefined&gt;|Hostname or IP of 
the machine on which to run the thrift frontend service via binary 
protocol.|string|1.4.0
 kyuubi.frontend.thrift.binary.bind.port|10009|Port of the machine on which to 
run the thrift frontend service via binary protocol.|int|1.4.0
+kyuubi.frontend.thrift.binary.ssl.disallowed.protocols|SSLv2,SSLv3|SSL 
versions to disallow for Kyuubi thrift binary frontend.|seq|1.7.0
+kyuubi.frontend.thrift.binary.ssl.enabled|false|Set this to true for using SSL 
encryption in thrift binary frontend server.|boolean|1.7.0
+kyuubi.frontend.thrift.binary.ssl.include.ciphersuites||A comma separated list 
of include SSL cipher suite names for thrift binary frontend.|seq|1.7.0
 kyuubi.frontend.thrift.http.allow.user.substitution|true|Allow alternate user 
to be specified as part of open connection request when using HTTP transport 
mode.|boolean|1.6.0
 kyuubi.frontend.thrift.http.bind.host|&lt;undefined&gt;|Hostname or IP of the 
machine on which to run the thrift frontend service via http 
protocol.|string|1.6.0
 kyuubi.frontend.thrift.http.bind.port|10010|Port of the machine on which to 
run the thrift frontend service via http protocol.|int|1.6.0
@@ -313,9 +320,10 @@ kyuubi.frontend.thrift.http.max.idle.time|PT30M|Maximum 
idle time for a connecti
 kyuubi.frontend.thrift.http.path|cliservice|Path component of URL endpoint 
when in HTTP mode.|string|1.6.0
 kyuubi.frontend.thrift.http.request.header.size|6144|Request header size in 
bytes, when using HTTP transport mode. Jetty defaults used.|int|1.6.0
 kyuubi.frontend.thrift.http.response.header.size|6144|Response header size in 
bytes, when using HTTP transport mode. Jetty defaults used.|int|1.6.0
+kyuubi.frontend.thrift.http.ssl.exclude.ciphersuites||A comma separated list 
of exclude SSL cipher suite names for thrift http frontend.|seq|1.7.0
 kyuubi.frontend.thrift.http.ssl.keystore.password|&lt;undefined&gt;|SSL 
certificate keystore password.|string|1.6.0
 kyuubi.frontend.thrift.http.ssl.keystore.path|&lt;undefined&gt;|SSL 
certificate keystore location.|string|1.6.0
-kyuubi.frontend.thrift.http.ssl.protocol.blacklist|SSLv2,SSLv3|SSL Versions to 
disable when using HTTP transport mode.|string|1.6.0
+kyuubi.frontend.thrift.http.ssl.protocol.blacklist|SSLv2,SSLv3|SSL Versions to 
disable when using HTTP transport mode.|seq|1.6.0
 kyuubi.frontend.thrift.http.use.SSL|false|Set this to true for using SSL 
encryption in http mode.|boolean|1.6.0
 kyuubi.frontend.thrift.http.xsrf.filter.enabled|false|If enabled, Kyuubi will 
block any requests made to it over http if an X-XSRF-HEADER header is not 
present|boolean|1.6.0
 kyuubi.frontend.thrift.login.timeout|PT20S|Timeout for Thrift clients during 
login to the thrift frontend service.|duration|1.4.0
diff --git 
a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala 
b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
index ddefa455c..762b8b1ee 100644
--- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
+++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
@@ -379,6 +379,57 @@ object KyuubiConf {
       .version("1.4.0")
       .fallbackConf(FRONTEND_BIND_HOST)
 
+  val FRONTEND_THRIFT_BINARY_SSL_ENABLED: ConfigEntry[Boolean] =
+    buildConf("kyuubi.frontend.thrift.binary.ssl.enabled")
+      .doc("Set this to true for using SSL encryption in thrift binary 
frontend server.")
+      .version("1.7.0")
+      .booleanConf
+      .createWithDefault(false)
+
+  val FRONTEND_SSL_KEYSTORE_PATH: OptionalConfigEntry[String] =
+    buildConf("kyuubi.frontend.ssl.keystore.path")
+      .doc("SSL certificate keystore location.")
+      .version("1.7.0")
+      .stringConf
+      .createOptional
+
+  val FRONTEND_SSL_KEYSTORE_PASSWORD: OptionalConfigEntry[String] =
+    buildConf("kyuubi.frontend.ssl.keystore.password")
+      .doc("SSL certificate keystore password.")
+      .version("1.7.0")
+      .stringConf
+      .createOptional
+
+  val FRONTEND_SSL_KEYSTORE_TYPE: OptionalConfigEntry[String] =
+    buildConf("kyuubi.frontend.ssl.keystore.type")
+      .doc("SSL certificate keystore type.")
+      .version("1.7.0")
+      .stringConf
+      .createOptional
+
+  val FRONTEND_SSL_KEYSTORE_ALGORITHM: OptionalConfigEntry[String] =
+    buildConf("kyuubi.frontend.ssl.keystore.algorithm")
+      .doc("SSL certificate keystore algorithm.")
+      .version("1.7.0")
+      .stringConf
+      .createOptional
+
+  val FRONTEND_THRIFT_BINARY_SSL_DISALLOWED_PROTOCOLS: 
ConfigEntry[Seq[String]] =
+    buildConf("kyuubi.frontend.thrift.binary.ssl.disallowed.protocols")
+      .doc("SSL versions to disallow for Kyuubi thrift binary frontend.")
+      .version("1.7.0")
+      .stringConf
+      .toSequence()
+      .createWithDefault(Seq("SSLv2", "SSLv3"))
+
+  val FRONTEND_THRIFT_BINARY_SSL_INCLUDE_CIPHER_SUITES: 
ConfigEntry[Seq[String]] =
+    buildConf("kyuubi.frontend.thrift.binary.ssl.include.ciphersuites")
+      .doc("A comma separated list of include SSL cipher suite names for 
thrift binary frontend.")
+      .version("1.7.0")
+      .stringConf
+      .toSequence()
+      .createWithDefault(Nil)
+
   @deprecated("using kyuubi.frontend.thrift.binary.bind.port instead", "1.4.0")
   val FRONTEND_BIND_PORT: ConfigEntry[Int] = 
buildConf("kyuubi.frontend.bind.port")
     .doc("(deprecated) Port of the machine on which to run the thrift frontend 
service " +
@@ -584,6 +635,7 @@ object KyuubiConf {
     buildConf("kyuubi.frontend.thrift.http.ssl.keystore.path")
       .doc("SSL certificate keystore location.")
       .version("1.6.0")
+      .withAlternative("kyuubi.frontend.ssl.keystore.path")
       .stringConf
       .createOptional
 
@@ -591,15 +643,25 @@ object KyuubiConf {
     buildConf("kyuubi.frontend.thrift.http.ssl.keystore.password")
       .doc("SSL certificate keystore password.")
       .version("1.6.0")
+      .withAlternative("kyuubi.frontend.ssl.keystore.password")
       .stringConf
       .createOptional
 
-  val FRONTEND_THRIFT_HTTP_SSL_PROTOCOL_BLACKLIST: ConfigEntry[String] =
+  val FRONTEND_THRIFT_HTTP_SSL_PROTOCOL_BLACKLIST: ConfigEntry[Seq[String]] =
     buildConf("kyuubi.frontend.thrift.http.ssl.protocol.blacklist")
       .doc("SSL Versions to disable when using HTTP transport mode.")
       .version("1.6.0")
       .stringConf
-      .createWithDefault("SSLv2,SSLv3")
+      .toSequence()
+      .createWithDefault(Seq("SSLv2", "SSLv3"))
+
+  val FRONTEND_THRIFT_HTTP_SSL_EXCLUDE_CIPHER_SUITES: ConfigEntry[Seq[String]] 
=
+    buildConf("kyuubi.frontend.thrift.http.ssl.exclude.ciphersuites")
+      .doc("A comma separated list of exclude SSL cipher suite names for 
thrift http frontend.")
+      .version("1.7.0")
+      .stringConf
+      .toSequence()
+      .createWithDefault(Nil)
 
   val FRONTEND_THRIFT_HTTP_ALLOW_USER_SUBSTITUTION: ConfigEntry[Boolean] =
     buildConf("kyuubi.frontend.thrift.http.allow.user.substitution")
@@ -2007,7 +2069,9 @@ object KyuubiConf {
     SERVER_LIMIT_CONNECTIONS_PER_IPADDRESS,
     SERVER_LIMIT_CONNECTIONS_PER_USER_IPADDRESS,
     SERVER_LIMIT_CONNECTIONS_PER_USER,
-    SESSION_LOCAL_DIR_ALLOW_LIST)
+    SESSION_LOCAL_DIR_ALLOW_LIST,
+    FRONTEND_SSL_KEYSTORE_PASSWORD,
+    FRONTEND_THRIFT_HTTP_SSL_KEYSTORE_PASSWORD)
 
   /**
    * Holds information about keys that have been deprecated.
diff --git 
a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TBinaryFrontendService.scala
 
b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TBinaryFrontendService.scala
index c6e218754..561d8067a 100644
--- 
a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TBinaryFrontendService.scala
+++ 
b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TBinaryFrontendService.scala
@@ -17,12 +17,15 @@
 
 package org.apache.kyuubi.service
 
+import java.security.KeyStore
+import java.util.Locale
 import java.util.concurrent.{SynchronousQueue, ThreadPoolExecutor, TimeUnit}
+import javax.net.ssl.{KeyManagerFactory, SSLServerSocket}
 
 import org.apache.hive.service.rpc.thrift._
 import org.apache.thrift.protocol.TBinaryProtocol
 import org.apache.thrift.server.{TServer, TThreadPoolServer}
-import org.apache.thrift.transport.TServerSocket
+import org.apache.thrift.transport.{TServerSocket, TSSLTransportFactory}
 
 import org.apache.kyuubi.{KyuubiException, Logging}
 import org.apache.kyuubi.config.KyuubiConf
@@ -65,7 +68,35 @@ abstract class TBinaryFrontendService(name: String)
         new NamedThreadFactory(name + "Handler-Pool", false))
       val transFactory = authFactory.getTTransportFactory
       val tProcFactory = authFactory.getTProcessorFactory(this)
-      val tServerSocket = new TServerSocket(serverSocket)
+      val tServerSocket =
+        // only enable ssl for server side
+        if (isServer() && conf.get(FRONTEND_THRIFT_BINARY_SSL_ENABLED)) {
+          val keyStorePath = conf.get(FRONTEND_SSL_KEYSTORE_PATH)
+          val keyStorePassword = conf.get(FRONTEND_SSL_KEYSTORE_PASSWORD)
+          val keyStoreType = conf.get(FRONTEND_SSL_KEYSTORE_TYPE)
+          val keyStoreAlgorithm = conf.get(FRONTEND_SSL_KEYSTORE_ALGORITHM)
+          val disallowedSslProtocols = 
conf.get(FRONTEND_THRIFT_BINARY_SSL_DISALLOWED_PROTOCOLS)
+          val includeCipherSuites = 
conf.get(FRONTEND_THRIFT_BINARY_SSL_INCLUDE_CIPHER_SUITES)
+
+          if (keyStorePath.isEmpty) {
+            throw new IllegalArgumentException(
+              s"${FRONTEND_SSL_KEYSTORE_PATH.key} not configured for SSL 
connection")
+          }
+          if (keyStorePassword.isEmpty) {
+            throw new IllegalArgumentException(
+              s"${FRONTEND_SSL_KEYSTORE_PASSWORD.key} not configured for SSL 
connection")
+          }
+
+          getServerSSLSocket(
+            keyStorePath.get,
+            keyStorePassword.get,
+            keyStoreType,
+            keyStoreAlgorithm,
+            disallowedSslProtocols,
+            includeCipherSuites)
+        } else {
+          new TServerSocket(serverSocket)
+        }
       val maxMessageSize = conf.get(FRONTEND_THRIFT_MAX_MESSAGE_SIZE)
       val requestTimeout = conf.get(FRONTEND_THRIFT_LOGIN_TIMEOUT).toInt
       val beBackoffSlotLength = 
conf.get(FRONTEND_THRIFT_LOGIN_BACKOFF_SLOT_LENGTH).toInt
@@ -94,6 +125,47 @@ abstract class TBinaryFrontendService(name: String)
     super.initialize(conf)
   }
 
+  private def getServerSSLSocket(
+      keyStorePath: String,
+      keyStorePassword: String,
+      keyStoreType: Option[String],
+      keyStoreAlgorithm: Option[String],
+      disallowedSslProtocols: Seq[String],
+      includeCipherSuites: Seq[String]): TServerSocket = {
+    val params =
+      if (includeCipherSuites.nonEmpty) {
+        new TSSLTransportFactory.TSSLTransportParameters("TLS", 
includeCipherSuites.toArray)
+      } else {
+        new TSSLTransportFactory.TSSLTransportParameters()
+      }
+    params.setKeyStore(
+      keyStorePath,
+      keyStorePassword,
+      keyStoreAlgorithm.getOrElse(KeyManagerFactory.getDefaultAlgorithm),
+      keyStoreType.getOrElse(KeyStore.getDefaultType))
+
+    val tServerSocket =
+      TSSLTransportFactory.getServerSocket(portNum, 0, 
serverSocket.getInetAddress, params)
+
+    tServerSocket.getServerSocket match {
+      case sslServerSocket: SSLServerSocket =>
+        val lowerDisallowedSslProtocols = 
disallowedSslProtocols.map(_.toLowerCase(Locale.ROOT))
+        val enabledProtocols = sslServerSocket.getEnabledProtocols.flatMap { 
protocol =>
+          if 
(lowerDisallowedSslProtocols.contains(protocol.toLowerCase(Locale.ROOT))) {
+            debug(s"Disabling SSL Protocol: $protocol")
+            None
+          } else {
+            Some(protocol)
+          }
+        }
+        sslServerSocket.setEnabledProtocols(enabledProtocols)
+        info(s"SSL Server Socket enabled protocols: $enabledProtocols")
+
+      case _ =>
+    }
+    tServerSocket
+  }
+
   override def run(): Unit =
     try {
       if (isServer()) {
diff --git 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
index de2fc7814..4c0e2b875 100644
--- 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
+++ 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
@@ -132,14 +132,24 @@ final class KyuubiTHttpFrontendService(
           }
 
           val sslContextFactory = new SslContextFactory.Server
-          val excludedProtocols = 
conf.get(FRONTEND_THRIFT_HTTP_SSL_PROTOCOL_BLACKLIST).split(",")
+          val excludedProtocols = 
conf.get(FRONTEND_THRIFT_HTTP_SSL_PROTOCOL_BLACKLIST)
+          val excludeCipherSuites = 
conf.get(FRONTEND_THRIFT_HTTP_SSL_EXCLUDE_CIPHER_SUITES)
+          val keyStoreType = conf.get(FRONTEND_SSL_KEYSTORE_TYPE)
+          val keyStoreAlgorithm = conf.get(FRONTEND_SSL_KEYSTORE_ALGORITHM)
           info("Thrift HTTP Server SSL: adding excluded protocols: " +
             String.join(",", excludedProtocols: _*))
           sslContextFactory.addExcludeProtocols(excludedProtocols: _*)
           info("Thrift HTTP Server SSL: SslContextFactory.getExcludeProtocols 
= " +
             String.join(",", sslContextFactory.getExcludeProtocols: _*))
+          info("Thrift HTTP Server SSL: setting excluded cipher Suites: " +
+            String.join(",", excludeCipherSuites: _*))
+          sslContextFactory.setExcludeCipherSuites(excludeCipherSuites: _*)
+          info("Thrift HTTP Server SSL: 
SslContextFactory.getExcludeCipherSuites = " +
+            String.join(",", sslContextFactory.getExcludeCipherSuites: _*))
           sslContextFactory.setKeyStorePath(keyStorePath.get)
           sslContextFactory.setKeyStorePassword(keyStorePassword.get)
+          keyStoreType.foreach(sslContextFactory.setKeyStoreType)
+          
keyStoreAlgorithm.foreach(sslContextFactory.setKeyManagerFactoryAlgorithm)
           new ServerConnector(
             server.get,
             sslContextFactory,

Reply via email to