blob: 752003dad8c69568e1c541c7f6063a2effc610a9 [file] [log] [blame]
/*
* 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.security.provider;
import static java.util.Objects.requireNonNull;
import java.util.Collection;
import java.util.Objects;
import net.jcip.annotations.NotThreadSafe;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenIdentifier;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of {@link AuthenticationProviderSelector} which can choose from the
* authentication implementations which HBase provides out of the box: Simple, Kerberos, and
* Delegation Token authentication.
*
* This implementation will ignore any {@link SaslAuthenticationProvider}'s which are available
* on the classpath or specified in the configuration because HBase cannot correctly choose which
* token should be returned to a client when multiple are present. It is expected that users
* implement their own {@link AuthenticationProviderSelector} when writing a custom provider.
*
* This implementation is not thread-safe. {@link #configure(Configuration, Collection)} and
* {@link #selectProvider(String, User)} is not safe if they are called concurrently.
*/
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.AUTHENTICATION)
@NotThreadSafe
public class BuiltInProviderSelector implements AuthenticationProviderSelector {
private static final Logger LOG = LoggerFactory.getLogger(BuiltInProviderSelector.class);
Configuration conf;
SimpleSaslClientAuthenticationProvider simpleAuth = null;
GssSaslClientAuthenticationProvider krbAuth = null;
DigestSaslClientAuthenticationProvider digestAuth = null;
Text digestAuthTokenKind = null;
@Override
public void configure(
Configuration conf, Collection<SaslClientAuthenticationProvider> providers) {
if (this.conf != null) {
throw new IllegalStateException("configure() should only be called once");
}
this.conf = Objects.requireNonNull(conf);
for (SaslClientAuthenticationProvider provider : Objects.requireNonNull(providers)) {
final String name = provider.getSaslAuthMethod().getName();
if (SimpleSaslAuthenticationProvider.SASL_AUTH_METHOD.getName().contentEquals(name)) {
if (simpleAuth != null) {
throw new IllegalStateException(
"Encountered multiple SimpleSaslClientAuthenticationProvider instances");
}
simpleAuth = (SimpleSaslClientAuthenticationProvider) provider;
} else if (GssSaslAuthenticationProvider.SASL_AUTH_METHOD.getName().equals(name)) {
if (krbAuth != null) {
throw new IllegalStateException(
"Encountered multiple GssSaslClientAuthenticationProvider instances");
}
krbAuth = (GssSaslClientAuthenticationProvider) provider;
} else if (DigestSaslAuthenticationProvider.SASL_AUTH_METHOD.getName().equals(name)) {
if (digestAuth != null) {
throw new IllegalStateException(
"Encountered multiple DigestSaslClientAuthenticationProvider instances");
}
digestAuth = (DigestSaslClientAuthenticationProvider) provider;
digestAuthTokenKind = new Text(digestAuth.getTokenKind());
} else {
LOG.warn("Ignoring unknown SaslClientAuthenticationProvider: {}", provider.getClass());
}
}
if (simpleAuth == null || krbAuth == null || digestAuth == null) {
throw new IllegalStateException("Failed to load SIMPLE, KERBEROS, and DIGEST authentication "
+ "providers. Classpath is not sane.");
}
}
@Override
public Pair<SaslClientAuthenticationProvider, Token<? extends TokenIdentifier>> selectProvider(
String clusterId, User user) {
requireNonNull(clusterId, "Null clusterId was given");
requireNonNull(user, "Null user was given");
// Superfluous: we don't do SIMPLE auth over SASL, but we should to simplify.
if (!User.isHBaseSecurityEnabled(conf)) {
return new Pair<>(simpleAuth, null);
}
final Text clusterIdAsText = new Text(clusterId);
// Must be digest auth, look for a token.
// TestGenerateDelegationToken is written expecting DT is used when DT and Krb are both present.
// (for whatever that's worth).
for (Token<? extends TokenIdentifier> token : user.getTokens()) {
// We need to check for two things:
// 1. This token is for the HBase cluster we want to talk to
// 2. We have suppporting client implementation to handle the token (the "kind" of token)
if (clusterIdAsText.equals(token.getService()) &&
digestAuthTokenKind.equals(token.getKind())) {
return new Pair<>(digestAuth, token);
}
}
// Unwrap PROXY auth'n method if that's what we have coming in.
final UserGroupInformation currentUser = user.getUGI();
// May be null if Hadoop AuthenticationMethod is PROXY
final UserGroupInformation realUser = currentUser.getRealUser();
if (currentUser.hasKerberosCredentials() ||
(realUser != null && realUser.hasKerberosCredentials())) {
return new Pair<>(krbAuth, null);
}
// This indicates that a client is requesting some authentication mechanism which the servers
// don't know how to process (e.g. there is no provider which can support it). This may be
// a bug or simply a misconfiguration of client *or* server.
LOG.warn("No matching SASL authentication provider and supporting token found from providers"
+ " for user: {}", user);
return null;
}
}