PHOENIX-4749 Allow impersonation when SPNEGO is disabled
Client impersonation is no longer tied to SPNEGO auth.
Signed-off-by: Josh Elser <elserj@apache.org>
diff --git a/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
index 8436086..e3f0f52 100644
--- a/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
+++ b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
@@ -54,6 +54,7 @@
import org.apache.phoenix.util.InstanceResolver;
import java.io.File;
+import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.InetAddress;
@@ -197,7 +198,7 @@
QueryServicesOptions.DEFAULT_QUERY_SERVER_DISABLE_KERBEROS_LOGIN);
// handle secure cluster credentials
- if (isKerberos && !disableSpnego && !disableLogin) {
+ if (isKerberos && !disableLogin) {
hostname = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
getConf().get(QueryServices.QUERY_SERVER_DNS_INTERFACE_ATTRIB, "default"),
getConf().get(QueryServices.QUERY_SERVER_DNS_NAMESERVER_ATTRIB, "default")));
@@ -230,47 +231,9 @@
final HttpServer.Builder builder = new HttpServer.Builder().withPort(port)
.withHandler(service, getSerialization(getConf()));
- // Enable SPNEGO and Impersonation when using Kerberos
+ // Enable client auth when using Kerberos auth for HBase
if (isKerberos) {
- UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
- LOG.debug("Current user is " + ugi);
- if (!ugi.hasKerberosCredentials()) {
- ugi = UserGroupInformation.getLoginUser();
- LOG.debug("Current user does not have Kerberos credentials, using instead " + ugi);
- }
-
- // Make sure the proxyuser configuration is up to date
- ProxyUsers.refreshSuperUserGroupsConfiguration(getConf());
-
- String keytabPath = getConf().get(QueryServices.QUERY_SERVER_KEYTAB_FILENAME_ATTRIB);
- File keytab = new File(keytabPath);
- String httpKeytabPath = getConf().get(QueryServices.QUERY_SERVER_HTTP_KEYTAB_FILENAME_ATTRIB, null);
- String httpPrincipal = getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB, null);
- // Backwards compat for a configuration key change
- if (httpPrincipal == null) {
- httpPrincipal = getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB_LEGACY, null);
- }
- File httpKeytab = null;
- if (null != httpKeytabPath)
- httpKeytab = new File(httpKeytabPath);
-
- String realmsString = getConf().get(QueryServices.QUERY_SERVER_KERBEROS_ALLOWED_REALMS, null);
- String[] additionalAllowedRealms = null;
- if (null != realmsString) {
- additionalAllowedRealms = StringUtils.split(realmsString, ',');
- }
-
- // Enable SPNEGO and impersonation (through standard Hadoop configuration means)
- if ((null != httpKeytabPath) && (null != httpPrincipal))
- builder.withSpnego(httpPrincipal, additionalAllowedRealms)
- .withAutomaticLogin(httpKeytab)
- .withImpersonation(new PhoenixDoAsCallback(ugi, getConf()));
- else
- builder.withSpnego(ugi.getUserName(), additionalAllowedRealms)
- .withAutomaticLogin(keytab)
- .withImpersonation(new PhoenixDoAsCallback(ugi, getConf()));
-
-
+ configureClientAuthentication(builder, disableSpnego);
}
setRemoteUserExtractorIfNecessary(builder, getConf());
@@ -294,6 +257,51 @@
}
}
+ @VisibleForTesting
+ void configureClientAuthentication(final HttpServer.Builder builder, boolean disableSpnego) throws IOException {
+ UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+ LOG.debug("Current user is " + ugi);
+ if (!ugi.hasKerberosCredentials()) {
+ ugi = UserGroupInformation.getLoginUser();
+ LOG.debug("Current user does not have Kerberos credentials, using instead " + ugi);
+ }
+
+ // Make sure the proxyuser configuration is up to date
+ ProxyUsers.refreshSuperUserGroupsConfiguration(getConf());
+
+ // Always enable impersonation for the proxy user (through standard Hadoop configuration means)
+ builder.withImpersonation(new PhoenixDoAsCallback(ugi, getConf()));
+
+ // Enable SPNEGO for client authentication unless it's explicitly disabled
+ if (!disableSpnego) {
+ String keytabPath = getConf().get(QueryServices.QUERY_SERVER_KEYTAB_FILENAME_ATTRIB);
+ File keytab = new File(keytabPath);
+ String httpKeytabPath =
+ getConf().get(QueryServices.QUERY_SERVER_HTTP_KEYTAB_FILENAME_ATTRIB, null);
+ String httpPrincipal =
+ getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB, null);
+ // Backwards compat for a configuration key change
+ if (httpPrincipal == null) {
+ httpPrincipal =
+ getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB_LEGACY, null);
+ }
+ File httpKeytab = null;
+ if (null != httpKeytabPath) httpKeytab = new File(httpKeytabPath);
+
+ String realmsString = getConf().get(QueryServices.QUERY_SERVER_KERBEROS_ALLOWED_REALMS, null);
+ String[] additionalAllowedRealms = null;
+ if (null != realmsString) {
+ additionalAllowedRealms = StringUtils.split(realmsString, ',');
+ }
+ if ((null != httpKeytabPath) && (null != httpPrincipal)) {
+ builder.withSpnego(httpPrincipal, additionalAllowedRealms).withAutomaticLogin(httpKeytab);
+ } else {
+ builder.withSpnego(ugi.getUserName(), additionalAllowedRealms)
+ .withAutomaticLogin(keytab);
+ }
+ }
+ }
+
public synchronized void stop() {
server.stop();
}
diff --git a/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/QueryServerConfigurationTest.java b/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/QueryServerConfigurationTest.java
new file mode 100644
index 0000000..f2a1022
--- /dev/null
+++ b/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/QueryServerConfigurationTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.phoenix.queryserver.server;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.calcite.avatica.server.DoAsRemoteUserCallback;
+import org.apache.calcite.avatica.server.HttpServer;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.phoenix.query.QueryServices;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.mockito.Mockito.*;
+
+public class QueryServerConfigurationTest {
+ private static final Configuration CONF = HBaseConfiguration.create();
+
+ @Rule public TemporaryFolder testFolder = new TemporaryFolder();
+
+ private HttpServer.Builder builder;
+ private QueryServer queryServer;
+
+ @Before
+ public void setup() throws IOException {
+ File keytabFile = testFolder.newFile("test.keytab");
+ CONF.set(QueryServices.QUERY_SERVER_KEYTAB_FILENAME_ATTRIB, keytabFile.getAbsolutePath());
+ builder = mock(HttpServer.Builder.class);
+ queryServer = new QueryServer(new String[0], CONF);
+ }
+
+ @Test
+ public void testSpnegoEnabled() throws IOException {
+ // SPENEGO settings will be provided to the builder when enabled
+ doReturn(builder).when(builder).withSpnego(anyString(), any(String[].class));
+ configureAndVerifyImpersonation(builder, false);
+ // A keytab file will also be provided for automatic login
+ verify(builder).withAutomaticLogin(any(File.class));
+ }
+
+ @Test
+ public void testSpnegoDisabled() throws IOException {
+ configureAndVerifyImpersonation(builder, true);
+ verify(builder, never()).withSpnego(anyString(), any(String[].class));
+ verify(builder, never()).withAutomaticLogin(any(File.class));
+ }
+
+ private void configureAndVerifyImpersonation(HttpServer.Builder builder, boolean disableSpnego)
+ throws IOException {
+ queryServer.configureClientAuthentication(builder, disableSpnego);
+ verify(builder).withImpersonation(any(DoAsRemoteUserCallback.class));
+ }
+}