Skip to content

Commit

Permalink
HADOOP-12437. Allow SecurityUtil to lookup alternate hostnames. (Cont…
Browse files Browse the repository at this point in the history
…ributed by Arpit Agarwal)
  • Loading branch information
arp7 committed Sep 24, 2015
1 parent 71a81b6 commit df31c44
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 46 deletions.
3 changes: 3 additions & 0 deletions hadoop-common-project/hadoop-common/CHANGES.txt
Expand Up @@ -1147,6 +1147,9 @@ Release 2.8.0 - UNRELEASED
HADOOP-12438. Reset RawLocalFileSystem.useDeprecatedFileStatus in
TestLocalFileSystem. (Chris Nauroth via wheat9)

HADOOP-12437. Allow SecurityUtil to lookup alternate hostnames.
(Arpit Agarwal)

Release 2.7.2 - UNRELEASED

INCOMPATIBLE CHANGES
Expand Down
Expand Up @@ -294,6 +294,12 @@ public class CommonConfigurationKeysPublic {
/** See <a href="{@docRoot}/../core-default.html">core-default.xml</a> */
public static final String HADOOP_SECURITY_AUTH_TO_LOCAL =
"hadoop.security.auth_to_local";
/** See <a href="{@docRoot}/../core-default.html">core-default.xml</a> */
public static final String HADOOP_SECURITY_DNS_INTERFACE_KEY =
"hadoop.security.dns.interface";
/** See <a href="{@docRoot}/../core-default.html">core-default.xml</a> */
public static final String HADOOP_SECURITY_DNS_NAMESERVER_KEY =
"hadoop.security.dns.nameserver";

/** See <a href="{@docRoot}/../core-default.html">core-default.xml</a> */
public static final String HADOOP_KERBEROS_MIN_SECONDS_BEFORE_RELOGIN =
Expand Down
Expand Up @@ -18,6 +18,8 @@

package org.apache.hadoop.net;

import com.google.common.net.InetAddresses;
import com.sun.istack.Nullable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
Expand All @@ -27,9 +29,11 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Vector;

import javax.naming.NamingException;
Expand Down Expand Up @@ -68,7 +72,7 @@ public class DNS {
* @return The host name associated with the provided IP
* @throws NamingException If a NamingException is encountered
*/
public static String reverseDns(InetAddress hostIp, String ns)
public static String reverseDns(InetAddress hostIp, @Nullable String ns)
throws NamingException {
//
// Builds the reverse IP lookup form
Expand Down Expand Up @@ -228,28 +232,44 @@ public static String getDefaultIP(String strInterface)
* (e.g. eth0 or eth0:0)
* @param nameserver
* The DNS host name
* @param tryfallbackResolution
* if true and if reverse DNS resolution fails then attempt to
* resolve the hostname with
* {@link InetAddress#getCanonicalHostName()} which includes
* hosts file resolution.
* @return A string vector of all host names associated with the IPs tied to
* the specified interface
* @throws UnknownHostException if the given interface is invalid
*/
public static String[] getHosts(String strInterface, String nameserver)
throws UnknownHostException {
String[] ips = getIPs(strInterface);
Vector<String> hosts = new Vector<String>();
for (int ctr = 0; ctr < ips.length; ctr++) {
public static String[] getHosts(String strInterface,
@Nullable String nameserver,
boolean tryfallbackResolution)
throws UnknownHostException {
final List<String> hosts = new Vector<String>();
final List<InetAddress> addresses =
getIPsAsInetAddressList(strInterface, true);
for (InetAddress address : addresses) {
try {
hosts.add(reverseDns(InetAddress.getByName(ips[ctr]),
nameserver));
} catch (UnknownHostException ignored) {
hosts.add(reverseDns(address, nameserver));
} catch (NamingException ignored) {
}
}
if (hosts.isEmpty() && tryfallbackResolution) {
for (InetAddress address : addresses) {
final String canonicalHostName = address.getCanonicalHostName();
// Don't use the result if it looks like an IP address.
if (!InetAddresses.isInetAddress(canonicalHostName)) {
hosts.add(canonicalHostName);
}
}
}

if (hosts.isEmpty()) {
LOG.warn("Unable to determine hostname for interface " + strInterface);
return new String[] { cachedHostname };
} else {
return hosts.toArray(new String[hosts.size()]);
LOG.warn("Unable to determine hostname for interface " +
strInterface);
hosts.add(cachedHostname);
}
return hosts.toArray(new String[hosts.size()]);
}


Expand Down Expand Up @@ -315,7 +335,7 @@ private static String resolveLocalHostIPAddress() {
*/
public static String[] getHosts(String strInterface)
throws UnknownHostException {
return getHosts(strInterface, null);
return getHosts(strInterface, null, false);
}

/**
Expand All @@ -331,17 +351,19 @@ public static String[] getHosts(String strInterface)
* @throws UnknownHostException
* If one is encountered while querying the default interface
*/
public static String getDefaultHost(String strInterface, String nameserver)
public static String getDefaultHost(@Nullable String strInterface,
@Nullable String nameserver,
boolean tryfallbackResolution)
throws UnknownHostException {
if ("default".equals(strInterface)) {
if (strInterface == null || "default".equals(strInterface)) {
return cachedHostname;
}

if ("default".equals(nameserver)) {
return getDefaultHost(strInterface);
if (nameserver != null && "default".equals(nameserver)) {
nameserver = null;
}

String[] hosts = getHosts(strInterface, nameserver);
String[] hosts = getHosts(strInterface, nameserver, tryfallbackResolution);
return hosts[0];
}

Expand All @@ -357,9 +379,74 @@ public static String getDefaultHost(String strInterface, String nameserver)
* @throws UnknownHostException
* If one is encountered while querying the default interface
*/
public static String getDefaultHost(String strInterface)
public static String getDefaultHost(@Nullable String strInterface)
throws UnknownHostException {
return getDefaultHost(strInterface, null);
return getDefaultHost(strInterface, null, false);
}

/**
* Returns the default (first) host name associated by the provided
* nameserver with the address bound to the specified network interface.
*
* @param strInterface
* The name of the network interface to query (e.g. eth0)
* @param nameserver
* The DNS host name
* @throws UnknownHostException
* If one is encountered while querying the default interface
*/
public static String getDefaultHost(@Nullable String strInterface,
@Nullable String nameserver)
throws UnknownHostException {
return getDefaultHost(strInterface, nameserver, false);
}

/**
* Returns all the IPs associated with the provided interface, if any, as
* a list of InetAddress objects.
*
* @param strInterface
* The name of the network interface or sub-interface to query
* (eg eth0 or eth0:0) or the string "default"
* @param returnSubinterfaces
* Whether to return IPs associated with subinterfaces of
* the given interface
* @return A list of all the IPs associated with the provided
* interface. The local host IP is returned if the interface
* name "default" is specified or there is an I/O error looking
* for the given interface.
* @throws UnknownHostException
* If the given interface is invalid
*
*/
public static List<InetAddress> getIPsAsInetAddressList(String strInterface,
boolean returnSubinterfaces) throws UnknownHostException {
if ("default".equals(strInterface)) {
return Arrays.asList(InetAddress.getByName(cachedHostAddress));
}
NetworkInterface netIf;
try {
netIf = NetworkInterface.getByName(strInterface);
if (netIf == null) {
netIf = getSubinterface(strInterface);
}
} catch (SocketException e) {
LOG.warn("I/O error finding interface " + strInterface +
": " + e.getMessage());
return Arrays.asList(InetAddress.getByName(cachedHostAddress));
}
if (netIf == null) {
throw new UnknownHostException("No such interface " + strInterface);
}

// NB: Using a LinkedHashSet to preserve the order for callers
// that depend on a particular element being 1st in the array.
// For example, getDefaultIP always returns the first element.
LinkedHashSet<InetAddress> allAddrs = new LinkedHashSet<InetAddress>();
allAddrs.addAll(Collections.list(netIf.getInetAddresses()));
if (!returnSubinterfaces) {
allAddrs.removeAll(getSubinterfaceInetAddrs(netIf));
}
return new Vector<InetAddress>(allAddrs);
}
}
Expand Up @@ -17,6 +17,8 @@
package org.apache.hadoop.security;

import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION;
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_DNS_INTERFACE_KEY;
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_DNS_NAMESERVER_KEY;

import java.io.IOException;
import java.net.InetAddress;
Expand All @@ -29,6 +31,7 @@
import java.util.List;
import java.util.ServiceLoader;

import javax.annotation.Nullable;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;

Expand All @@ -39,6 +42,7 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.net.DNS;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.apache.hadoop.security.token.Token;
Expand Down Expand Up @@ -180,13 +184,38 @@ private static String replacePattern(String[] components, String hostname)
throws IOException {
String fqdn = hostname;
if (fqdn == null || fqdn.isEmpty() || fqdn.equals("0.0.0.0")) {
fqdn = getLocalHostName();
fqdn = getLocalHostName(null);
}
return components[0] + "/" +
StringUtils.toLowerCase(fqdn) + "@" + components[2];
}

static String getLocalHostName() throws UnknownHostException {

/**
* Retrieve the name of the current host. Multihomed hosts may restrict the
* hostname lookup to a specific interface and nameserver with {@link
* org.apache.hadoop.fs.CommonConfigurationKeysPublic#HADOOP_SECURITY_DNS_INTERFACE_KEY}
* and {@link org.apache.hadoop.fs.CommonConfigurationKeysPublic#HADOOP_SECURITY_DNS_NAMESERVER_KEY}
*
* @param conf Configuration object. May be null.
* @return
* @throws UnknownHostException
*/
static String getLocalHostName(@Nullable Configuration conf)
throws UnknownHostException {
if (conf != null) {
String dnsInterface = conf.get(HADOOP_SECURITY_DNS_INTERFACE_KEY);
String nameServer = conf.get(HADOOP_SECURITY_DNS_NAMESERVER_KEY);

if (dnsInterface != null) {
return DNS.getDefaultHost(dnsInterface, nameServer, true);
} else if (nameServer != null) {
throw new IllegalArgumentException(HADOOP_SECURITY_DNS_NAMESERVER_KEY +
" requires " + HADOOP_SECURITY_DNS_INTERFACE_KEY + ". Check your" +
"configuration.");
}
}

// Fallback to querying the default hostname as we did before.
return InetAddress.getLocalHost().getCanonicalHostName();
}

Expand All @@ -207,7 +236,7 @@ static String getLocalHostName() throws UnknownHostException {
@InterfaceStability.Evolving
public static void login(final Configuration conf,
final String keytabFileKey, final String userNameKey) throws IOException {
login(conf, keytabFileKey, userNameKey, getLocalHostName());
login(conf, keytabFileKey, userNameKey, getLocalHostName(conf));
}

/**
Expand Down
Expand Up @@ -88,6 +88,31 @@
</description>
</property>

<property>
<name>hadoop.security.dns.interface</name>
<description>
The name of the Network Interface from which the service should determine
its host name for Kerberos login. e.g. eth2. In a multi-homed environment,
the setting can be used to affect the _HOST subsitution in the service
Kerberos principal. If this configuration value is not set, the service
will use its default hostname as returned by
InetAddress.getLocalHost().getCanonicalHostName().

Most clusters will not require this setting.
</description>
</property>

<property>
<name>hadoop.security.dns.nameserver</name>
<description>
The host name or IP address of the name server (DNS) which a service Node
should use to determine its own host name for Kerberos Login. Requires
hadoop.security.dns.interface.

Most clusters will not require this setting.
</description>
</property>

<!--
=== Multiple group mapping providers configuration sample ===
This sample illustrates a typical use case for CompositeGroupsMapping where
Expand Down

0 comments on commit df31c44

Please sign in to comment.