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
(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
INCOMPATIBLE CHANGES

View File

@ -18,8 +18,11 @@
package org.apache.hadoop.ipc;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.net.ConnectException;
@ -412,6 +415,27 @@ private synchronized void setupConnection() throws IOException {
try {
this.socket = socketFactory.createSocket();
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
NetUtils.connect(this.socket, remoteId.getAddress(), 20000);
if (rpcTimeout > 0) {

View File

@ -22,8 +22,10 @@
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.UnknownHostException;
import java.net.ConnectException;
@ -250,7 +252,7 @@ public static InetSocketAddress getConnectAddress(Server server) {
* case, the timeout argument is ignored and the timeout set with
* {@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()}.
*
* @see #getInputStream(Socket, long)
@ -272,7 +274,7 @@ public static InputStream getInputStream(Socket socket)
* case, the timeout argument is ignored and the timeout set with
* {@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()}.
*
* @see Socket#getChannel()
@ -301,7 +303,7 @@ public static InputStream getInputStream(Socket socket, long timeout)
* case, the timeout argument is ignored and the write will wait until
* 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()}.
*
* @see #getOutputStream(Socket, long)
@ -323,7 +325,7 @@ public static OutputStream getOutputStream(Socket socket)
* case, the timeout argument is ignored and the write will wait until
* 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()}.
*
* @see Socket#getChannel()
@ -426,6 +428,9 @@ public static List<String> normalizeHostNames(Collection<String> names) {
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.
* Check that the node name is an ip addr and if so, attempt to determine
@ -434,8 +439,6 @@ public static List<String> normalizeHostNames(Collection<String> names) {
*
* @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) {
// If name is not an ip addr, don't bother looking it up
if(!ipPattern.matcher(ip).matches())
@ -460,4 +463,27 @@ public static String getHostname() {
try {return "" + InetAddress.getLocalHost();}
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 static String buildDTServiceName(URI uri, int defPort) {
sb.append(host).append(":").append(port);
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.ConnectException;
import java.net.SocketException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import org.apache.hadoop.conf.Configuration;
public class TestNetUtils {
@ -58,4 +60,16 @@ public void testAvoidLoopbackTcpSockets() throws Exception {
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 void testStartsWithIncorrectSettings() throws IOException {
}
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"));
}
}