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:
parent
f2625d494f
commit
67c79a2520
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue