diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md index b3a9ff337db..663043e61d5 100644 --- a/docs/configuration/settings.md +++ b/docs/configuration/settings.md @@ -246,6 +246,7 @@ You can configure the Kyuubi properties in `$KYUUBI_HOME/conf/kyuubi-defaults.co | kyuubi.frontend.mysql.worker.keepalive.time | PT1M | Time(ms) that an idle async thread of the command execution thread pool will wait for a new task to arrive before terminating in MySQL frontend service | duration | 1.4.0 | | kyuubi.frontend.protocols | THRIFT_BINARY,REST | A comma-separated list for all frontend protocols | seq | 1.4.0 | | 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 specified to any IP address, so it will not be used for authentication. | string | 1.6.0 | +| kyuubi.frontend.rest.authentication | NONE | A comma-separated list of client authentication types. It fallback to `kyuubi.authentication` if not configure. | seq | 1.9.0 | | kyuubi.frontend.rest.bind.host | <undefined> | 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.rest.max.worker.threads | 999 | Maximum number of threads in the frontend worker thread pool for the rest frontend service | int | 1.6.2 | 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 9f09e9bb636..c943823133b 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 @@ -797,6 +797,14 @@ object KyuubiConf { .checkValues(AuthTypes) .createWithDefault(Seq(AuthTypes.NONE.toString)) + val FRONTEND_REST_AUTHENTICATION_METHOD: ConfigEntry[Seq[String]] = + buildConf("kyuubi.frontend.rest.authentication") + .doc("A comma-separated list of client authentication types." + + " It fallback to `kyuubi.authentication` if not configure.") + .version("1.9.0") + .serverOnly + .fallbackConf(AUTHENTICATION_METHOD) + val AUTHENTICATION_CUSTOM_CLASS: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.custom.class") .doc("User-defined authentication implementation of " + diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala index 83aee66fef0..b9211d17e6e 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala @@ -72,7 +72,7 @@ class KyuubiRestFrontendService(override val serverable: Serverable) private lazy val port: Int = conf.get(FRONTEND_REST_BIND_PORT) private[kyuubi] lazy val securityEnabled = { - val authTypes = conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName) + val authTypes = conf.get(FRONTEND_REST_AUTHENTICATION_METHOD).map(AuthTypes.withName) AuthUtils.kerberosEnabled(authTypes) || !AuthUtils.effectivePlainAuthType(authTypes).contains(AuthTypes.NONE) } @@ -103,7 +103,9 @@ class KyuubiRestFrontendService(override val serverable: Serverable) private def startInternal(): Unit = { val contextHandler = ApiRootResource.getServletHandler(this) - val holder = new FilterHolder(new AuthenticationFilter(conf)) + val holder = new FilterHolder(new AuthenticationFilter( + conf, + conf.get(FRONTEND_REST_AUTHENTICATION_METHOD).map(AuthTypes.withName))) contextHandler.addFilter(holder, "/v1/*", EnumSet.allOf(classOf[DispatcherType])) val authenticationFactory = new KyuubiHttpAuthenticationFactory(conf) server.addHandler(authenticationFactory.httpHandlerWrapperFactory.wrapHandler(contextHandler)) diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala index 980f35d70b5..8b22a717dee 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala @@ -30,11 +30,11 @@ import org.apache.hadoop.hive.shims.Utils import org.apache.kyuubi.Logging import org.apache.kyuubi.config.KyuubiConf -import org.apache.kyuubi.config.KyuubiConf.FRONTEND_PROXY_HTTP_CLIENT_IP_HEADER +import org.apache.kyuubi.config.KyuubiConf.{AUTHENTICATION_METHOD, FRONTEND_PROXY_HTTP_CLIENT_IP_HEADER} import org.apache.kyuubi.server.http.authentication.AuthenticationFilter import org.apache.kyuubi.server.http.util.{CookieSigner, HttpAuthUtils, SessionManager} import org.apache.kyuubi.server.http.util.HttpAuthUtils.AUTHORIZATION_HEADER -import org.apache.kyuubi.service.authentication.KyuubiAuthenticationFactory +import org.apache.kyuubi.service.authentication.{AuthTypes, KyuubiAuthenticationFactory} import org.apache.kyuubi.shaded.thrift.TProcessor import org.apache.kyuubi.shaded.thrift.protocol.TProtocolFactory import org.apache.kyuubi.shaded.thrift.server.TServlet @@ -56,7 +56,8 @@ class ThriftHttpServlet( private var isCookieSecure = false private var isHttpOnlyCookie = false private val X_FORWARDED_FOR_HEADER = "X-Forwarded-For" - private val authenticationFilter = new AuthenticationFilter(conf) + private val authenticationFilter = + new AuthenticationFilter(conf, conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName)) override def init(): Unit = { isCookieAuthEnabled = conf.get(KyuubiConf.FRONTEND_THRIFT_HTTP_COOKIE_AUTH_ENABLED) diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala index 15b387607ea..1339a75334d 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala @@ -26,12 +26,13 @@ import scala.collection.mutable import org.apache.kyuubi.Logging import org.apache.kyuubi.config.KyuubiConf -import org.apache.kyuubi.config.KyuubiConf.{AUTHENTICATION_METHOD, FRONTEND_PROXY_HTTP_CLIENT_IP_HEADER} +import org.apache.kyuubi.config.KyuubiConf.FRONTEND_PROXY_HTTP_CLIENT_IP_HEADER import org.apache.kyuubi.server.http.util.HttpAuthUtils.AUTHORIZATION_HEADER import org.apache.kyuubi.service.authentication.{AuthTypes, InternalSecurityAccessor} import org.apache.kyuubi.service.authentication.AuthTypes.{KERBEROS, NOSASL} -class AuthenticationFilter(conf: KyuubiConf) extends Filter with Logging { +class AuthenticationFilter(conf: KyuubiConf, authTypes: Seq[AuthTypes.Value]) extends Filter + with Logging { import AuthenticationFilter._ import AuthSchemes._ @@ -55,7 +56,6 @@ class AuthenticationFilter(conf: KyuubiConf) extends Filter with Logging { } private[kyuubi] def initAuthHandlers(): Unit = { - val authTypes = conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName) val spnegoKerberosEnabled = authTypes.contains(KERBEROS) val basicAuthTypeOpt = { if (authTypes == Set(NOSASL)) { diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala index ca95fda3d9a..250ee5d6462 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala @@ -25,14 +25,14 @@ import org.eclipse.jetty.server.{Handler, Request} import org.eclipse.jetty.server.handler.HandlerWrapper import org.apache.kyuubi.config.KyuubiConf -import org.apache.kyuubi.config.KyuubiConf.{AUTHENTICATION_METHOD, ENGINE_SECURITY_ENABLED} +import org.apache.kyuubi.config.KyuubiConf.{ENGINE_SECURITY_ENABLED, FRONTEND_REST_AUTHENTICATION_METHOD} import org.apache.kyuubi.metrics.MetricsConstants.{REST_CONN_FAIL, REST_CONN_OPEN, REST_CONN_TOTAL} import org.apache.kyuubi.metrics.MetricsSystem import org.apache.kyuubi.service.authentication.{AuthTypes, InternalSecurityAccessor} import org.apache.kyuubi.service.authentication.AuthTypes.KERBEROS class KyuubiHttpAuthenticationFactory(conf: KyuubiConf) { - private val authTypes = conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName) + private val authTypes = conf.get(FRONTEND_REST_AUTHENTICATION_METHOD).map(AuthTypes.withName) private val kerberosEnabled = authTypes.contains(KERBEROS) private val ugi = UserGroupInformation.getCurrentUser diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/RestClientTestHelper.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/RestClientTestHelper.scala index 8344cdef01d..f063a0e1ff1 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/RestClientTestHelper.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/RestClientTestHelper.scala @@ -48,7 +48,9 @@ trait RestClientTestHelper extends RestFrontendTestHelper with KerberizedTestHel UserGroupInformation.setConfiguration(config) assert(UserGroupInformation.isSecurityEnabled) - val conf = KyuubiConf().set(KyuubiConf.AUTHENTICATION_METHOD, Seq("KERBEROS", "LDAP", "CUSTOM")) + val conf = KyuubiConf().set( + KyuubiConf.FRONTEND_REST_AUTHENTICATION_METHOD, + Seq("KERBEROS", "LDAP", "CUSTOM")) .set(KyuubiConf.SERVER_KEYTAB.key, testKeytab) .set(KyuubiConf.SERVER_PRINCIPAL, testPrincipal) .set(KyuubiConf.SERVER_SPNEGO_KEYTAB, testKeytab) diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala index 260264b6797..965e4ccd669 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala @@ -182,3 +182,33 @@ class KyuubiRestAuthenticationSuite extends RestClientTestHelper { assert(HttpServletResponse.SC_UNAUTHORIZED == response.getStatus) } } + +class NoneKyuubiRestAuthenticationSuite extends RestClientTestHelper { + + override protected val otherConfigs: Map[String, String] = { + Map(KyuubiConf.FRONTEND_REST_AUTHENTICATION_METHOD.key -> "NONE") + } + + test("test disable restful api authentication") { + val response = webTarget.path("api/v1/sessions/count") + .request() + .get() + + assert(HttpServletResponse.SC_OK == response.getStatus) + } +} + +class KerberosKyuubiRestAuthenticationSuite extends RestClientTestHelper { + + override protected val otherConfigs: Map[String, String] = { + Map(KyuubiConf.FRONTEND_REST_AUTHENTICATION_METHOD.key -> "KERBEROS") + } + + test("test without authorization when rest api authentication with KERBEROS") { + val response = webTarget.path("api/v1/sessions/count") + .request() + .get() + + assert(HttpServletResponse.SC_UNAUTHORIZED == response.getStatus) + } +} diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/KyuubiRestFrontendServiceSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/KyuubiRestFrontendServiceSuite.scala index b60517a06d8..5786b57d358 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/KyuubiRestFrontendServiceSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/KyuubiRestFrontendServiceSuite.scala @@ -26,7 +26,7 @@ import org.apache.kyuubi.service.authentication.AnonymousAuthenticationProviderI class KyuubiRestFrontendServiceSuite extends RestFrontendTestHelper { override protected lazy val conf: KyuubiConf = KyuubiConf() - .set(AUTHENTICATION_METHOD, Seq("NONE")) + .set(FRONTEND_REST_AUTHENTICATION_METHOD, Seq("NONE")) test("version") { val resp = v1Call("version") @@ -61,7 +61,7 @@ class KyuubiRestFrontendServiceSuite extends RestFrontendTestHelper { class KerberosKyuubiRestFrontendServiceSuite extends RestFrontendTestHelper { override protected lazy val conf: KyuubiConf = KyuubiConf() - .set(AUTHENTICATION_METHOD, Seq("KERBEROS")) + .set(FRONTEND_REST_AUTHENTICATION_METHOD, Seq("KERBEROS")) .set(AUTHENTICATION_CUSTOM_CLASS, classOf[AnonymousAuthenticationProviderImpl].getName) test("security enabled - KERBEROS") { @@ -72,7 +72,7 @@ class KerberosKyuubiRestFrontendServiceSuite extends RestFrontendTestHelper { class NoneKyuubiRestFrontendServiceSuite extends RestFrontendTestHelper { override protected lazy val conf: KyuubiConf = KyuubiConf() - .set(AUTHENTICATION_METHOD, Seq("NONE")) + .set(FRONTEND_REST_AUTHENTICATION_METHOD, Seq("NONE")) .set(AUTHENTICATION_CUSTOM_CLASS, classOf[AnonymousAuthenticationProviderImpl].getName) test("security enabled - NONE") { @@ -83,7 +83,7 @@ class NoneKyuubiRestFrontendServiceSuite extends RestFrontendTestHelper { class KerberosAndCustomKyuubiRestFrontendServiceSuite extends RestFrontendTestHelper { override protected lazy val conf: KyuubiConf = KyuubiConf() - .set(AUTHENTICATION_METHOD, Seq("KERBEROS,CUSTOM")) + .set(FRONTEND_REST_AUTHENTICATION_METHOD, Seq("KERBEROS,CUSTOM")) .set(AUTHENTICATION_CUSTOM_CLASS, classOf[AnonymousAuthenticationProviderImpl].getName) test("security enabled - KERBEROS,CUSTOM") { diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala index 95aa3de025d..2318ad5cf53 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala @@ -51,7 +51,7 @@ class AdminResourceSuite extends KyuubiFunSuite with RestFrontendTestHelper { private val engineMgr = new KyuubiApplicationManager() override protected lazy val conf: KyuubiConf = KyuubiConf() - .set(AUTHENTICATION_METHOD, Seq("CUSTOM")) + .set(FRONTEND_REST_AUTHENTICATION_METHOD, Seq("CUSTOM")) .set(AUTHENTICATION_CUSTOM_CLASS, classOf[AnonymousAuthenticationProviderImpl].getName) .set(SERVER_ADMINISTRATORS, Set("admin001")) .set(ENGINE_IDLE_TIMEOUT, Duration.ofMinutes(3).toMillis) diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala index f1ee71bec9f..1f913068dac 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala @@ -85,7 +85,7 @@ abstract class BatchesResourceSuiteBase extends KyuubiFunSuite override protected lazy val conf: KyuubiConf = { val testResourceDir = Paths.get(sparkBatchTestResource.get).getParent val kyuubiConf = KyuubiConf() - .set(AUTHENTICATION_METHOD, Seq("CUSTOM")) + .set(FRONTEND_REST_AUTHENTICATION_METHOD, Seq("CUSTOM")) .set(AUTHENTICATION_CUSTOM_CLASS, classOf[AnonymousAuthenticationProviderImpl].getName) .set(SERVER_ADMINISTRATORS, Set("admin")) .set(BATCH_IMPL_VERSION, batchVersion) diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilterSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilterSuite.scala index de4b056ff46..62f4fa16aab 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilterSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilterSuite.scala @@ -19,11 +19,14 @@ package org.apache.kyuubi.server.http.authentication import org.apache.kyuubi.KyuubiFunSuite import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.config.KyuubiConf.AUTHENTICATION_METHOD import org.apache.kyuubi.service.authentication.AuthTypes class AuthenticationFilterSuite extends KyuubiFunSuite { test("add auth handler and destroy") { - val filter = new AuthenticationFilter(KyuubiConf()) + val conf = KyuubiConf() + val filter = + new AuthenticationFilter(conf, conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName)) filter.addAuthHandler(new BasicAuthenticationHandler(null)) assert(filter.authSchemeHandlers.isEmpty) filter.addAuthHandler(new BasicAuthenticationHandler(AuthTypes.LDAP)) diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminCtlSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminCtlSuite.scala index 0c262d2389a..76649ad33e7 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminCtlSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminCtlSuite.scala @@ -56,7 +56,7 @@ class AdminCtlSuite extends RestClientTestHelper with TestPrematureExit { val id = UUID.randomUUID().toString conf.set(HighAvailabilityConf.HA_NAMESPACE, "kyuubi_test") conf.set(KyuubiConf.ENGINE_IDLE_TIMEOUT, 180000L) - conf.set(KyuubiConf.AUTHENTICATION_METHOD, Seq("LDAP", "CUSTOM")) + conf.set(KyuubiConf.FRONTEND_REST_AUTHENTICATION_METHOD, Seq("LDAP", "CUSTOM")) conf.set(KyuubiConf.GROUP_PROVIDER, "hadoop") val user = ldapUser diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala index 43eb06482e7..f6b51f5a85c 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/rest/client/AdminRestApiSuite.scala @@ -50,7 +50,7 @@ class AdminRestApiSuite extends RestClientTestHelper { val id = UUID.randomUUID().toString conf.set(HighAvailabilityConf.HA_NAMESPACE, "kyuubi_test") conf.set(KyuubiConf.ENGINE_IDLE_TIMEOUT, 180000L) - conf.set(KyuubiConf.AUTHENTICATION_METHOD, Seq("LDAP", "CUSTOM")) + conf.set(KyuubiConf.FRONTEND_REST_AUTHENTICATION_METHOD, Seq("LDAP", "CUSTOM")) conf.set(KyuubiConf.GROUP_PROVIDER, "hadoop") val user = ldapUser val engine =