瀏覽代碼

Merging change r1087875 for HADOOP-7215 from branch-0.20-security

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-0.20-security-203@1087879 13f79535-47bb-0310-9956-ffa450edef68
Suresh Srinivas 14 年之前
父節點
當前提交
e76bf5cade

+ 3 - 0
CHANGES.txt

@@ -54,6 +54,9 @@ Release 0.20.203.0 - unreleased
     HADOOP-6879. Provide SSH based (Jsch) remote execution API for system
     tests. (cos)
 
+    HADOOP-7215. RPC clients must use network interface corresponding to 
+    the host in the client's kerberos principal key. (suresh)
+
 Release 0.20.202.0 - unreleased
 
     MAPREDUCE-2355. Add a configuration knob 

+ 24 - 0
src/core/org/apache/hadoop/ipc/Client.java

@@ -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;
@@ -386,6 +389,27 @@ public class Client {
         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);
           this.socket.setSoTimeout(pingInterval);

+ 29 - 4
src/core/org/apache/hadoop/net/NetUtils.java

@@ -22,8 +22,10 @@ import java.io.InputStream;
 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.nio.channels.SocketChannel;
@@ -281,7 +283,7 @@ public class NetUtils {
    * 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)
@@ -303,7 +305,7 @@ public class NetUtils {
    * 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()
@@ -332,7 +334,7 @@ public class NetUtils {
    * 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)
@@ -354,7 +356,7 @@ public class NetUtils {
    * 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()
@@ -441,4 +443,27 @@ public class NetUtils {
     }
     return hostNames;
   }
+  
+  /**
+   * 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;
+  }
 }

+ 9 - 0
src/core/org/apache/hadoop/security/SecurityUtil.java

@@ -274,4 +274,13 @@ public class SecurityUtil {
       throw new RuntimeException(ex);
     }
   }
+  
+  /**
+   * 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();
+  }
 }

+ 8 - 0
src/test/org/apache/hadoop/security/TestSecurityUtil.java

@@ -82,4 +82,12 @@ public class TestSecurityUtil {
     assertEquals("hdfs/" + local + "@REALM", SecurityUtil.getServerPrincipal(
         "hdfs/_HOST@REALM", "0.0.0.0"));
   }
+  
+  @Test
+  public void testGetHostFromPrincipal() {
+    assertEquals("host", 
+        SecurityUtil.getHostFromPrincipal("service/host@realm"));
+    assertEquals(null,
+        SecurityUtil.getHostFromPrincipal("service@realm"));
+  }
 }