HADOOP-7215. RPC clients must use network interface corresponding to the host in the client's kerberos principal key. Contributed by Suresh Srinivas.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1087844 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Suresh Srinivas 2011-04-01 18:38:46 +00:00
parent f2625d494f
commit 67c79a2520
6 changed files with 91 additions and 6 deletions

View File

@ -124,6 +124,10 @@ Trunk (unreleased changes)
HADOOP-7210. Chown command is not working from FSShell HADOOP-7210. Chown command is not working from FSShell
(Uma Maheswara Rao G via todd) (Uma Maheswara Rao G via todd)
HADOOP-7215. RPC clients must use network interface corresponding to
the host in the client's kerberos principal key. (suresh)
>>>>>>> .r1087843
Release 0.22.0 - Unreleased Release 0.22.0 - Unreleased
INCOMPATIBLE CHANGES INCOMPATIBLE CHANGES

View File

@ -18,8 +18,11 @@
package org.apache.hadoop.ipc; package org.apache.hadoop.ipc;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket; import java.net.Socket;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.net.ConnectException; import java.net.ConnectException;
@ -412,6 +415,27 @@ public class Client {
try { try {
this.socket = socketFactory.createSocket(); this.socket = socketFactory.createSocket();
this.socket.setTcpNoDelay(tcpNoDelay); this.socket.setTcpNoDelay(tcpNoDelay);
/*
* Bind the socket to the host specified in the principal name of the
* client, to ensure Server matching address of the client connection
* to host name in principal passed.
*/
if (UserGroupInformation.isSecurityEnabled()) {
KerberosInfo krbInfo =
remoteId.getProtocol().getAnnotation(KerberosInfo.class);
if (krbInfo != null && krbInfo.clientPrincipal() != null) {
String host =
SecurityUtil.getHostFromPrincipal(remoteId.getTicket().getUserName());
// If host name is a valid local address then bind socket to it
InetAddress localAddr = NetUtils.getLocalInetAddress(host);
if (localAddr != null) {
this.socket.bind(new InetSocketAddress(localAddr, 0));
}
}
}
// connection time out is 20s // connection time out is 20s
NetUtils.connect(this.socket, remoteId.getAddress(), 20000); NetUtils.connect(this.socket, remoteId.getAddress(), 20000);
if (rpcTimeout > 0) { if (rpcTimeout > 0) {

View File

@ -22,8 +22,10 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.SocketException;
import java.net.URI; import java.net.URI;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.net.ConnectException; import java.net.ConnectException;
@ -250,7 +252,7 @@ public class NetUtils {
* case, the timeout argument is ignored and the timeout set with * case, the timeout argument is ignored and the timeout set with
* {@link Socket#setSoTimeout(int)} applies for reads.<br><br> * {@link Socket#setSoTimeout(int)} applies for reads.<br><br>
* *
* Any socket created using socket factories returned by {@link #NetUtils}, * Any socket created using socket factories returned by {@link NetUtils},
* must use this interface instead of {@link Socket#getInputStream()}. * must use this interface instead of {@link Socket#getInputStream()}.
* *
* @see #getInputStream(Socket, long) * @see #getInputStream(Socket, long)
@ -272,7 +274,7 @@ public class NetUtils {
* case, the timeout argument is ignored and the timeout set with * case, the timeout argument is ignored and the timeout set with
* {@link Socket#setSoTimeout(int)} applies for reads.<br><br> * {@link Socket#setSoTimeout(int)} applies for reads.<br><br>
* *
* Any socket created using socket factories returned by {@link #NetUtils}, * Any socket created using socket factories returned by {@link NetUtils},
* must use this interface instead of {@link Socket#getInputStream()}. * must use this interface instead of {@link Socket#getInputStream()}.
* *
* @see Socket#getChannel() * @see Socket#getChannel()
@ -301,7 +303,7 @@ public class NetUtils {
* case, the timeout argument is ignored and the write will wait until * case, the timeout argument is ignored and the write will wait until
* data is available.<br><br> * data is available.<br><br>
* *
* Any socket created using socket factories returned by {@link #NetUtils}, * Any socket created using socket factories returned by {@link NetUtils},
* must use this interface instead of {@link Socket#getOutputStream()}. * must use this interface instead of {@link Socket#getOutputStream()}.
* *
* @see #getOutputStream(Socket, long) * @see #getOutputStream(Socket, long)
@ -323,7 +325,7 @@ public class NetUtils {
* case, the timeout argument is ignored and the write will wait until * case, the timeout argument is ignored and the write will wait until
* data is available.<br><br> * data is available.<br><br>
* *
* Any socket created using socket factories returned by {@link #NetUtils}, * Any socket created using socket factories returned by {@link NetUtils},
* must use this interface instead of {@link Socket#getOutputStream()}. * must use this interface instead of {@link Socket#getOutputStream()}.
* *
* @see Socket#getChannel() * @see Socket#getChannel()
@ -426,6 +428,9 @@ public class NetUtils {
return hostNames; return hostNames;
} }
private static final Pattern ipPattern = // Pattern for matching hostname to ip:port
Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:?\\d*");
/** /**
* Attempt to obtain the host name of a name specified by ip address. * Attempt to obtain the host name of a name specified by ip address.
* Check that the node name is an ip addr and if so, attempt to determine * Check that the node name is an ip addr and if so, attempt to determine
@ -434,8 +439,6 @@ public class NetUtils {
* *
* @return Host name or null * @return Host name or null
*/ */
private static final Pattern ipPattern = // Pattern for matching hostname to ip:port
Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:?\\d*");
public static String getHostNameOfIP(String ip) { public static String getHostNameOfIP(String ip) {
// If name is not an ip addr, don't bother looking it up // If name is not an ip addr, don't bother looking it up
if(!ipPattern.matcher(ip).matches()) if(!ipPattern.matcher(ip).matches())
@ -460,4 +463,27 @@ public class NetUtils {
try {return "" + InetAddress.getLocalHost();} try {return "" + InetAddress.getLocalHost();}
catch(UnknownHostException uhe) {return "" + uhe;} catch(UnknownHostException uhe) {return "" + uhe;}
} }
/**
* Checks if {@code host} is a local host name and return {@link InetAddress}
* corresponding to that address.
*
* @param host the specified host
* @return a valid local {@link InetAddress} or null
* @throws SocketException if an I/O error occurs
*/
public static InetAddress getLocalInetAddress(String host)
throws SocketException {
if (host == null) {
return null;
}
InetAddress addr = null;
try {
addr = InetAddress.getByName(host);
if (NetworkInterface.getByInetAddress(addr) == null) {
addr = null; // Not a local address
}
} catch (UnknownHostException ignore) { }
return addr;
}
} }

View File

@ -282,4 +282,13 @@ public class SecurityUtil {
sb.append(host).append(":").append(port); sb.append(host).append(":").append(port);
return sb.toString(); return sb.toString();
} }
/**
* Get the host name from the principal name of format <service>/host@realm.
* @param principalName principal name of format as described above
* @return host name if the the string conforms to the above format, else null
*/
public static String getHostFromPrincipal(String principalName) {
return new KerberosName(principalName).getHostName();
}
} }

View File

@ -24,6 +24,8 @@ import java.net.Socket;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.SocketException; import java.net.SocketException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
public class TestNetUtils { public class TestNetUtils {
@ -58,4 +60,16 @@ public class TestNetUtils {
assertTrue(se.getMessage().contains("Invalid argument")); assertTrue(se.getMessage().contains("Invalid argument"));
} }
} }
/**
* Test for {
* @throws UnknownHostException @link NetUtils#getLocalInetAddress(String)
* @throws SocketException
*/
@Test
public void testGetLocalInetAddress() throws Exception {
assertNotNull(NetUtils.getLocalInetAddress("127.0.0.1"));
assertNull(NetUtils.getLocalInetAddress("invalid-address-for-test"));
assertNull(NetUtils.getLocalInetAddress(null));
}
} }

View File

@ -113,4 +113,12 @@ public class TestSecurityUtil {
} }
assertTrue("Exception for empty keytabfile name was expected", gotException); assertTrue("Exception for empty keytabfile name was expected", gotException);
} }
@Test
public void testGetHostFromPrincipal() {
assertEquals("host",
SecurityUtil.getHostFromPrincipal("service/host@realm"));
assertEquals(null,
SecurityUtil.getHostFromPrincipal("service@realm"));
}
} }