From 2fc2927251c5ffb9f1e81f547d471e1b40c18e60 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Mon, 13 Jun 2016 18:44:40 -0400 Subject: [PATCH] HBASE-5291 SPNEGO authentication for web UIs Builds on the existing wiring with hadoop-auth's SPNEGO classes. Unit tests built with Apache Kerby and Apache HC. Documentation added to instruct how to set it up. --- hbase-server/pom.xml | 22 ++ .../org/apache/hadoop/hbase/http/HttpServer.java | 93 +++++++- .../org/apache/hadoop/hbase/http/InfoServer.java | 9 + .../hadoop/hbase/regionserver/HRegionServer.java | 1 + .../hbase/http/HttpServerFunctionalTest.java | 43 ++++ .../hadoop/hbase/http/TestSpnegoHttpServer.java | 258 +++++++++++++++++++++ pom.xml | 17 ++ src/main/asciidoc/_chapters/security.adoc | 53 +++++ 8 files changed, 486 insertions(+), 10 deletions(-) create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestSpnegoHttpServer.java diff --git a/hbase-server/pom.xml b/hbase-server/pom.xml index 8762034..ebc29ab 100644 --- a/hbase-server/pom.xml +++ b/hbase-server/pom.xml @@ -577,6 +577,28 @@ + + org.apache.kerby + kerb-client + test + + + org.apache.kerby + kerb-simplekdc + test + + + org.apache.httpcomponents + httpclient + + 4.5.2 + test + + + org.apache.httpcomponents + httpcore + test + diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java index 667e597..e8f875e 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java @@ -27,6 +27,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -102,11 +103,30 @@ import com.sun.jersey.spi.container.servlet.ServletContainer; @InterfaceStability.Evolving public class HttpServer implements FilterContainer { private static final Log LOG = LogFactory.getLog(HttpServer.class); + private static final String EMPTY_STRING = ""; static final String FILTER_INITIALIZERS_PROPERTY = "hbase.http.filter.initializers"; static final String HTTP_MAX_THREADS = "hbase.http.max.threads"; + public static final String HTTP_UI_AUTHENTICATION = "hbase.security.authentication.ui"; + static final String HTTP_AUTHENTICATION_PREFIX = "hbase.security.authentication.spnego."; + static final String HTTP_SPNEGO_AUTHENTICATION_PREFIX = HTTP_AUTHENTICATION_PREFIX + + "spnego."; + static final String HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_SUFFIX = "kerberos.principal"; + public static final String HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_KEY = + HTTP_SPNEGO_AUTHENTICATION_PREFIX + HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_SUFFIX; + static final String HTTP_SPNEGO_AUTHENTICATION_KEYTAB_SUFFIX = "kerberos.keytab"; + public static final String HTTP_SPNEGO_AUTHENTICATION_KEYTAB_KEY = + HTTP_SPNEGO_AUTHENTICATION_PREFIX + HTTP_SPNEGO_AUTHENTICATION_KEYTAB_SUFFIX; + static final String HTTP_SPNEGO_AUTHENTICATION_KRB_NAME_SUFFIX = "kerberos.name.rules"; + public static final String HTTP_SPNEGO_AUTHENTICATION_KRB_NAME_KEY = + HTTP_SPNEGO_AUTHENTICATION_PREFIX + HTTP_SPNEGO_AUTHENTICATION_KRB_NAME_SUFFIX; + static final String HTTP_AUTHENTICATION_SIGNATURE_SECRET_FILE_SUFFIX = + "signature.secret.file"; + public static final String HTTP_AUTHENTICATION_SIGNATURE_SECRET_FILE_KEY = + HTTP_AUTHENTICATION_PREFIX + HTTP_AUTHENTICATION_SIGNATURE_SECRET_FILE_SUFFIX; + // The ServletContext attribute where the daemon Configuration // gets stored. public static final String CONF_CONTEXT_ATTRIBUTE = "hbase.conf"; @@ -175,6 +195,9 @@ public class HttpServer implements FilterContainer { // The -keypass option in keytool private String keyPassword; + private String kerberosNameRulesKey; + private String signatureSecretFileKey; + @Deprecated private String name; @Deprecated @@ -302,6 +325,16 @@ public class HttpServer implements FilterContainer { return this; } + public Builder setKerberosNameRulesKey(String kerberosNameRulesKey) { + this.kerberosNameRulesKey = kerberosNameRulesKey; + return this; + } + + public Builder setSignatureSecretFileKey(String signatureSecretFileKey) { + this.signatureSecretFileKey = signatureSecretFileKey; + return this; + } + public Builder setAppDir(String appDir) { this.appDir = appDir; return this; @@ -344,7 +377,8 @@ public class HttpServer implements FilterContainer { HttpServer server = new HttpServer(this); if (this.securityEnabled) { - server.initSpnego(conf, hostName, usernameConfKey, keytabConfKey); + server.initSpnego(conf, hostName, usernameConfKey, keytabConfKey, kerberosNameRulesKey, + signatureSecretFileKey); } if (connector != null) { @@ -927,21 +961,60 @@ public class HttpServer implements FilterContainer { } private void initSpnego(Configuration conf, String hostName, - String usernameConfKey, String keytabConfKey) throws IOException { + String usernameConfKey, String keytabConfKey, String kerberosNameRuleKey, + String signatureSecretKeyFileKey) throws IOException { Map params = new HashMap(); - String principalInConf = conf.get(usernameConfKey); - if (principalInConf != null && !principalInConf.isEmpty()) { - params.put("kerberos.principal", SecurityUtil.getServerPrincipal( + String principalInConf = getOrEmptyString(conf, usernameConfKey); + if (!principalInConf.isEmpty()) { + params.put(HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_SUFFIX, SecurityUtil.getServerPrincipal( principalInConf, hostName)); } - String httpKeytab = conf.get(keytabConfKey); - if (httpKeytab != null && !httpKeytab.isEmpty()) { - params.put("kerberos.keytab", httpKeytab); + String httpKeytab = getOrEmptyString(conf, keytabConfKey); + if (!httpKeytab.isEmpty()) { + params.put(HTTP_SPNEGO_AUTHENTICATION_KEYTAB_SUFFIX, httpKeytab); + } + String kerberosNameRule = getOrEmptyString(conf, kerberosNameRuleKey); + if (!kerberosNameRule.isEmpty()) { + params.put(HTTP_SPNEGO_AUTHENTICATION_KRB_NAME_SUFFIX, kerberosNameRule); + } + String signatureSecretKeyFile = getOrEmptyString(conf, signatureSecretKeyFileKey); + if (!signatureSecretKeyFile.isEmpty()) { + params.put(HTTP_AUTHENTICATION_SIGNATURE_SECRET_FILE_SUFFIX, + signatureSecretKeyFile); } params.put(AuthenticationFilter.AUTH_TYPE, "kerberos"); - defineFilter(webAppContext, SPNEGO_FILTER, - AuthenticationFilter.class.getName(), params, null); + // Verify that the required options were provided + if (isMissing(params.get(HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_SUFFIX)) || + isMissing(params.get(HTTP_SPNEGO_AUTHENTICATION_KEYTAB_SUFFIX))) { + throw new IllegalArgumentException(usernameConfKey + " and " + + keytabConfKey + " are both required in the configuration " + + "to enable SPNEGO/Kerberos authentication for the Web UI"); + } + + addGlobalFilter(SPNEGO_FILTER, AuthenticationFilter.class.getName(), params); + } + + /** + * Returns true if the argument is non-null and not whitespace + */ + private boolean isMissing(String value) { + if (null == value) { + return true; + } + return value.trim().isEmpty(); + } + + /** + * Extracts the value for the given key from the configuration of returns a string of + * zero length. + */ + private String getOrEmptyString(Configuration conf, String key) { + if (null == key) { + return EMPTY_STRING; + } + final String value = conf.get(key.trim()); + return null == value ? EMPTY_STRING : value; } /** diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/InfoServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/InfoServer.java index 5ff6370..0f6c3dd 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/InfoServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/InfoServer.java @@ -75,6 +75,15 @@ public class InfoServer { HBaseConfiguration.getPassword(c, "ssl.server.truststore.password", null), c.get("ssl.server.truststore.type", "jks")); } + // Enable SPNEGO authentication + if ("kerberos".equalsIgnoreCase(c.get(HttpServer.HTTP_UI_AUTHENTICATION, null))) { + builder.setUsernameConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_KEY) + .setKeytabConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_KEYTAB_KEY) + .setKerberosNameRulesKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_KRB_NAME_KEY) + .setSignatureSecretFileKey( + HttpServer.HTTP_AUTHENTICATION_SIGNATURE_SECRET_FILE_KEY) + .setSecurityEnabled(true); + } this.httpServer = builder.build(); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index 6449622..4d6b9b8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -94,6 +94,7 @@ import org.apache.hadoop.hbase.exceptions.UnknownProtocolException; import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.executor.ExecutorType; import org.apache.hadoop.hbase.fs.HFileSystem; +import org.apache.hadoop.hbase.http.HttpServer; import org.apache.hadoop.hbase.http.InfoServer; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/http/HttpServerFunctionalTest.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/http/HttpServerFunctionalTest.java index 5c832be..7e773b7 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/http/HttpServerFunctionalTest.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/http/HttpServerFunctionalTest.java @@ -27,6 +27,7 @@ import org.apache.hadoop.hbase.http.HttpServer.Builder; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.ServerSocket; import java.net.URI; import java.net.URL; import java.net.MalformedURLException; @@ -94,6 +95,15 @@ public class HttpServerFunctionalTest extends Assert { return createServer(TEST, conf, pathSpecs); } + public static HttpServer createTestServerWithSecurity(Configuration conf) throws IOException { + prepareTestWebapp(); + return localServerBuilder(TEST).setFindPort(true).setConf(conf).setSecurityEnabled(true) + // InfoServer normally sets these for us + .setUsernameConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_KEY) + .setKeytabConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_KEYTAB_KEY) + .build(); + } + /** * Prepare the test webapp by creating the directory from the test properties * fail if the directory cannot be created. @@ -226,4 +236,37 @@ public class HttpServerFunctionalTest extends Assert { } return out.toString(); } + + /** + * Recursively deletes a {@link File}. + */ + protected static void deleteRecursively(File d) { + if (d.isDirectory()) { + for (String name : d.list()) { + File child = new File(d, name); + if (child.isFile()) { + child.delete(); + } else { + deleteRecursively(d); + } + } + } + d.delete(); + } + + /** + * Picks a free port on the host by binding a Socket to '0'. + */ + protected static int getFreePort() throws IOException { + ServerSocket s = new ServerSocket(0); + try { + s.setReuseAddress(true); + int port = s.getLocalPort(); + return port; + } finally { + if (null != s) { + s.close(); + } + } + } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestSpnegoHttpServer.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestSpnegoHttpServer.java new file mode 100644 index 0000000..a84895f --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestSpnegoHttpServer.java @@ -0,0 +1,258 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.apache.hadoop.hbase.http; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.Principal; +import java.security.PrivilegedExceptionAction; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosTicket; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.http.TestHttpServer.EchoServlet; +import org.apache.hadoop.hbase.http.resource.JerseyResource; +import org.apache.hadoop.hbase.testclassification.MiscTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.apache.hadoop.security.authentication.util.KerberosName; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.auth.AuthSchemeProvider; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.KerberosCredentials; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.AuthSchemes; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.config.Lookup; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.auth.SPNegoSchemeFactory; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.apache.kerby.kerberos.kerb.KrbException; +import org.apache.kerby.kerberos.kerb.client.JaasKrbUtil; +import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Test class for SPNEGO authentication on the HttpServer. Uses Kerby's MiniKDC and Apache + * HttpComponents to verify that a simple Servlet is reachable via SPNEGO and unreachable w/o. + */ +@Category({MiscTests.class, SmallTests.class}) +public class TestSpnegoHttpServer extends HttpServerFunctionalTest { + private static final Log LOG = LogFactory.getLog(TestSpnegoHttpServer.class); + private static final String KDC_SERVER_HOST = "localhost"; + private static final String CLIENT_PRINCIPAL = "client"; + + private static HttpServer server; + private static URL baseUrl; + private static SimpleKdcServer kdc; + private static File infoServerKeytab; + private static File clientKeytab; + + @BeforeClass + public static void setupServer() throws Exception { + final String serverPrincipal = "HTTP/" + KDC_SERVER_HOST; + final File target = new File(System.getProperty("user.dir"), "target"); + assertTrue(target.exists()); + + kdc = buildMiniKdc(); + kdc.start(); + + File keytabDir = new File(target, TestSpnegoHttpServer.class.getSimpleName() + + "_keytabs"); + if (keytabDir.exists()) { + deleteRecursively(keytabDir); + } + keytabDir.mkdirs(); + + infoServerKeytab = new File(keytabDir, serverPrincipal.replace('/', '_') + ".keytab"); + clientKeytab = new File(keytabDir, CLIENT_PRINCIPAL + ".keytab"); + + setupUser(kdc, clientKeytab, CLIENT_PRINCIPAL); + setupUser(kdc, infoServerKeytab, serverPrincipal); + + Configuration conf = buildSpnegoConfiguration(serverPrincipal, infoServerKeytab); + + server = createTestServerWithSecurity(conf); + server.addServlet("echo", "/echo", EchoServlet.class); + server.addJerseyResourcePackage(JerseyResource.class.getPackage().getName(), "/jersey/*"); + server.start(); + baseUrl = getServerURL(server); + + LOG.info("HTTP server started: "+ baseUrl); + } + + @AfterClass + public static void stopServer() throws Exception { + try { + if (null != server) { + server.stop(); + } + } catch (Exception e) { + LOG.info("Failed to stop info server", e); + } + try { + if (null != kdc) { + kdc.stop(); + } + } catch (Exception e) { + LOG.info("Failed to stop mini KDC", e); + } + } + + private static void setupUser(SimpleKdcServer kdc, File keytab, String principal) + throws KrbException { + kdc.createPrincipal(principal); + kdc.exportPrincipal(principal, keytab); + } + + private static SimpleKdcServer buildMiniKdc() throws Exception { + SimpleKdcServer kdc = new SimpleKdcServer(); + + final File target = new File(System.getProperty("user.dir"), "target"); + File kdcDir = new File(target, TestSpnegoHttpServer.class.getSimpleName()); + if (kdcDir.exists()) { + deleteRecursively(kdcDir); + } + kdcDir.mkdirs(); + kdc.setWorkDir(kdcDir); + + kdc.setKdcHost(KDC_SERVER_HOST); + int kdcPort = getFreePort(); + kdc.setAllowTcp(true); + kdc.setAllowUdp(false); + kdc.setKdcTcpPort(kdcPort); + + LOG.info("Starting KDC server at " + KDC_SERVER_HOST + ":" + kdcPort); + + kdc.init(); + + return kdc; + } + + private static Configuration buildSpnegoConfiguration(String serverPrincipal, File + serverKeytab) { + Configuration conf = new Configuration(); + KerberosName.setRules("DEFAULT"); + + conf.setInt(HttpServer.HTTP_MAX_THREADS, 10); + + // Enable Kerberos (pre-req) + conf.set("hbase.security.authentication", "kerberos"); + conf.set(HttpServer.HTTP_UI_AUTHENTICATION, "kerberos"); + conf.set(HttpServer.HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_KEY, serverPrincipal); + conf.set(HttpServer.HTTP_SPNEGO_AUTHENTICATION_KEYTAB_KEY, serverKeytab.getAbsolutePath()); + + return conf; + } + + @Test + public void testUnauthorizedClientsDisallowed() throws IOException { + URL url = new URL(getServerURL(server), "/echo?a=b"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, conn.getResponseCode()); + } + + @Test + public void testAllowedClient() throws Exception { + // Create the subject for the client + final Subject clientSubject = JaasKrbUtil.loginUsingKeytab(CLIENT_PRINCIPAL, clientKeytab); + final Set clientPrincipals = clientSubject.getPrincipals(); + // Make sure the subject has a principal + assertFalse(clientPrincipals.isEmpty()); + + // Get a TGT for the subject (might have many, different encryption types). The first should + // be the default encryption type. + Set privateCredentials = + clientSubject.getPrivateCredentials(KerberosTicket.class); + assertFalse(privateCredentials.isEmpty()); + KerberosTicket tgt = privateCredentials.iterator().next(); + assertNotNull(tgt); + + // The name of the principal + final String principalName = clientPrincipals.iterator().next().getName(); + + // Run this code, logged in as the subject (the client) + HttpResponse resp = Subject.doAs(clientSubject, + new PrivilegedExceptionAction() { + @Override + public HttpResponse run() throws Exception { + // Logs in with Kerberos via GSS + GSSManager gssManager = GSSManager.getInstance(); + // jGSS Kerberos login constant + Oid oid = new Oid("1.2.840.113554.1.2.2"); + GSSName gssClient = gssManager.createName(principalName, GSSName.NT_USER_NAME); + GSSCredential credential = gssManager.createCredential(gssClient, + GSSCredential.DEFAULT_LIFETIME, oid, GSSCredential.INITIATE_ONLY); + + HttpClientContext context = HttpClientContext.create(); + Lookup authRegistry = RegistryBuilder.create() + .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true, true)) + .build(); + + HttpClient client = HttpClients.custom().setDefaultAuthSchemeRegistry(authRegistry).build(); + BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, new KerberosCredentials(credential)); + + URL url = new URL(getServerURL(server), "/echo?a=b"); + context.setTargetHost(new HttpHost(url.getHost(), url.getPort())); + context.setCredentialsProvider(credentialsProvider); + context.setAuthSchemeRegistry(authRegistry); + + HttpGet get = new HttpGet(url.toURI()); + return client.execute(get, context); + } + }); + + assertNotNull(resp); + assertEquals(HttpURLConnection.HTTP_OK, resp.getStatusLine().getStatusCode()); + assertEquals("a:b", EntityUtils.toString(resp.getEntity()).trim()); + } + + @Test(expected = IllegalArgumentException.class) + public void testMissingConfigurationThrowsException() throws Exception { + Configuration conf = new Configuration(); + conf.setInt(HttpServer.HTTP_MAX_THREADS, 10); + // Enable Kerberos (pre-req) + conf.set("hbase.security.authentication", "kerberos"); + // Intentionally skip keytab and principal + + HttpServer customServer = createTestServerWithSecurity(conf); + customServer.addServlet("echo", "/echo", EchoServlet.class); + customServer.addJerseyResourcePackage(JerseyResource.class.getPackage().getName(), "/jersey/*"); + customServer.start(); + } +} diff --git a/pom.xml b/pom.xml index 2424ccb..88db3a1 100644 --- a/pom.xml +++ b/pom.xml @@ -1149,6 +1149,7 @@ 3.2.2 3.1 + 4.4.4 2.2.0 12.0.1 1.3.9 @@ -1178,6 +1179,7 @@ 1.0.8 2.11.6 1.46 + 1.0.0-RC2 2.4 1.6 @@ -1437,6 +1439,11 @@ ${httpclient.version} + org.apache.httpcomponents + httpcore + ${httpcore.version} + + commons-cli commons-cli ${commons-cli.version} @@ -1718,6 +1725,16 @@ ${bouncycastle.version} test + + org.apache.kerby + kerb-client + ${kerby.version} + + + org.apache.kerby + kerb-simplekdc + ${kerby.version} + diff --git a/src/main/asciidoc/_chapters/security.adoc b/src/main/asciidoc/_chapters/security.adoc index 101affa..9ed6682 100644 --- a/src/main/asciidoc/_chapters/security.adoc +++ b/src/main/asciidoc/_chapters/security.adoc @@ -69,6 +69,59 @@ See Nick Dimiduk's contribution on this link:http://stackoverflow.com/questions/ If you know how to fix this without opening a second port for HTTPS, patches are appreciated. ==== +[[hbase.secure.spnego.ui]] +== Using SPNEGO for Kerberos authentication with Web UIs + +Kerberos-authentication to HBase Web UIs can be enabled via configuring SPNEGO with the `hbase.security.authentication.ui` +property in _hbase-site.xml_. Enabling this authentication requires that HBase is also configured to use Kerberos authentication +for RPCs (e.g `hbase.security.authentication` = `kerberos`). + +[source,xml] +---- + + hbase.security.authentication.ui + kerberos + Controls what kind of authentication should be used for the HBase web UIs. + + + hbase.security.authentication + kerberos + The Kerberos keytab file to use for SPNEGO authentication by the web server. + +---- + +A number of properties exist to configure SPNEGO authentication for the web server: + +[source,xml] +---- + + hbase.security.authentication.spnego.kerberos.principal + HTTP/_HOST@EXAMPLE.COM + Required for SPNEGO, the Kerberos principal to use for SPNEGO authentication by the + web server. The _HOST keyword will be automatically substituted with the node's + hostname. + + + hbase.security.authentication.spnego.kerberos.keytab + /etc/security/keytabs/spnego.service.keytab + Required for SPNEGO, the Kerberos keytab file to use for SPNEGO authentication by the + web server. + + + hbase.security.authentication.spnego.kerberos.name.rules + + Optional, Hadoop-style `auth_to_local` rules which will be parsed and used in the + handling of Kerberos principals + + + hbase.security.authentication.signature.secret.file + + Optional, a file whose contents will be used as a secret to sign the HTTP cookies + as a part of the SPNEGO authentication handshake. If this is not provided, Java's `Random` library + will be used for the secret. + +---- + [[hbase.secure.configuration]] == Secure Client Access to Apache HBase -- 2.1.2