From 22ef03bc7677d6718902a7587bbd26ab750f8d78 Mon Sep 17 00:00:00 2001 From: Daryn Sharp Date: Fri, 12 Oct 2012 16:27:26 +0000 Subject: [PATCH] HADOOP-8784. Improve IPC.Client's token use (daryn) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1397634 13f79535-47bb-0310-9956-ffa450edef68 --- .../hadoop-common/CHANGES.txt | 2 + .../java/org/apache/hadoop/ipc/Client.java | 23 ++- .../org/apache/hadoop/ipc/TestSaslRPC.java | 145 ++++++++++++++---- 3 files changed, 124 insertions(+), 46 deletions(-) diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index 4f3acbc3ee..23e070a01e 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -312,6 +312,8 @@ Release 2.0.3-alpha - Unreleased HADOOP-8912. Add .gitattributes file to prevent CRLF and LF mismatches for source and text files. (Raja Aluri via suresh) + HADOOP-8784. Improve IPC.Client's token use (daryn) + OPTIMIZATIONS HADOOP-8866. SampleQuantiles#query is O(N^2) instead of O(N). (Andrew Wang diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java index a67db2d990..ab8d6de881 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java @@ -225,7 +225,6 @@ private class Connection extends Thread { private IpcConnectionContextProto connectionContext; // connection context private final ConnectionId remoteId; // connection id private AuthMethod authMethod; // authentication method - private boolean useSasl; private Token token; private SaslRpcClient saslRpcClient; @@ -270,8 +269,7 @@ public Connection(ConnectionId remoteId) throws IOException { UserGroupInformation ticket = remoteId.getTicket(); Class protocol = remoteId.getProtocol(); - this.useSasl = UserGroupInformation.isSecurityEnabled(); - if (useSasl && protocol != null) { + if (protocol != null) { TokenInfo tokenInfo = SecurityUtil.getTokenInfo(protocol, conf); if (tokenInfo != null) { TokenSelector tokenSelector = null; @@ -296,12 +294,12 @@ public Connection(ConnectionId remoteId) throws IOException { } } - if (!useSasl) { - authMethod = AuthMethod.SIMPLE; - } else if (token != null) { + if (token != null) { authMethod = AuthMethod.DIGEST; - } else { + } else if (UserGroupInformation.isSecurityEnabled()) { authMethod = AuthMethod.KERBEROS; + } else { + authMethod = AuthMethod.SIMPLE; } connectionContext = ProtoUtil.makeIpcConnectionContext( @@ -576,14 +574,12 @@ private synchronized void setupIOstreams() throws InterruptedException { InputStream inStream = NetUtils.getInputStream(socket); OutputStream outStream = NetUtils.getOutputStream(socket); writeConnectionHeader(outStream); - if (useSasl) { + if (authMethod != AuthMethod.SIMPLE) { final InputStream in2 = inStream; final OutputStream out2 = outStream; UserGroupInformation ticket = remoteId.getTicket(); - if (authMethod == AuthMethod.KERBEROS) { - if (ticket.getRealUser() != null) { - ticket = ticket.getRealUser(); - } + if (ticket.getRealUser() != null) { + ticket = ticket.getRealUser(); } boolean continueSasl = false; try { @@ -614,7 +610,6 @@ public Boolean run() throws IOException { connectionContext.getProtocol(), ProtoUtil.getUgi(connectionContext.getUserInfo()), authMethod); - useSasl = false; } } @@ -1174,7 +1169,7 @@ public Writable call(RPC.RpcKind rpcKind, Writable rpcRequest, call.error); } } else { - return call.rpcResponse; + return call.getRpcResult(); } } } diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSaslRPC.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSaslRPC.java index 5207879521..f1b5fd67fd 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSaslRPC.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSaslRPC.java @@ -19,10 +19,7 @@ package org.apache.hadoop.ipc; import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - +import static org.junit.Assert.*; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; @@ -44,6 +41,7 @@ import org.apache.hadoop.io.Text; import org.apache.hadoop.ipc.Client.ConnectionId; import org.apache.hadoop.net.NetUtils; +import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.KerberosInfo; import org.apache.hadoop.security.SaslInputStream; import org.apache.hadoop.security.SaslRpcClient; @@ -449,23 +447,100 @@ static void testKerberosRpc(String principal, String keytab) throws Exception { } System.out.println("Test is successful."); } - + + // insecure -> insecure @Test - public void testDigestAuthMethodSecureServer() throws Exception { - checkDigestAuthMethod(true); + public void testInsecureClientInsecureServer() throws Exception { + assertEquals(AuthenticationMethod.SIMPLE, + getAuthMethod(false, false, false)); } @Test - public void testDigestAuthMethodInsecureServer() throws Exception { - checkDigestAuthMethod(false); + public void testInsecureClientInsecureServerWithToken() throws Exception { + assertEquals(AuthenticationMethod.TOKEN, + getAuthMethod(false, false, true)); } - private void checkDigestAuthMethod(boolean secureServer) throws Exception { + // insecure -> secure + @Test + public void testInsecureClientSecureServer() throws Exception { + RemoteException e = null; + try { + getAuthMethod(false, true, false); + } catch (RemoteException re) { + e = re; + } + assertNotNull(e); + assertEquals(AccessControlException.class.getName(), e.getClassName()); + } + + @Test + public void testInsecureClientSecureServerWithToken() throws Exception { + assertEquals(AuthenticationMethod.TOKEN, + getAuthMethod(false, true, true)); + } + + // secure -> secure + @Test + public void testSecureClientSecureServer() throws Exception { + /* Should be this when multiple secure auths are supported and we can + * dummy one out: + * assertEquals(AuthenticationMethod.SECURE_AUTH_METHOD, + * getAuthMethod(true, true, false)); + */ + try { + getAuthMethod(true, true, false); + } catch (IOException ioe) { + // can't actually test kerberos w/o kerberos... + String expectedError = "Failed to specify server's Kerberos principal"; + String actualError = ioe.getMessage(); + assertTrue("["+actualError+"] doesn't start with ["+expectedError+"]", + actualError.contains(expectedError)); + } + } + + @Test + public void testSecureClientSecureServerWithToken() throws Exception { + assertEquals(AuthenticationMethod.TOKEN, + getAuthMethod(true, true, true)); + } + + // secure -> insecure + @Test + public void testSecureClientInsecureServerWithToken() throws Exception { + assertEquals(AuthenticationMethod.TOKEN, + getAuthMethod(true, false, true)); + } + + @Test + public void testSecureClientInsecureServer() throws Exception { + /* Should be this when multiple secure auths are supported and we can + * dummy one out: + * assertEquals(AuthenticationMethod.SIMPLE + * getAuthMethod(true, false, false)); + */ + try { + getAuthMethod(true, false, false); + } catch (IOException ioe) { + // can't actually test kerberos w/o kerberos... + String expectedError = "Failed to specify server's Kerberos principal"; + String actualError = ioe.getMessage(); + assertTrue("["+actualError+"] doesn't start with ["+expectedError+"]", + actualError.contains(expectedError)); + } + } + + + private AuthenticationMethod getAuthMethod(final boolean isSecureClient, + final boolean isSecureServer, + final boolean useToken + + ) throws Exception { TestTokenSecretManager sm = new TestTokenSecretManager(); Server server = new RPC.Builder(conf).setProtocol(TestSaslProtocol.class) .setInstance(new TestSaslImpl()).setBindAddress(ADDRESS).setPort(0) .setNumHandlers(5).setVerbose(true).setSecretManager(sm).build(); - if (secureServer) { + if (isSecureServer) { server.enableSecurity(); } else { server.disableSecurity(); @@ -474,30 +549,36 @@ private void checkDigestAuthMethod(boolean secureServer) throws Exception { final UserGroupInformation current = UserGroupInformation.getCurrentUser(); final InetSocketAddress addr = NetUtils.getConnectAddress(server); - TestTokenIdentifier tokenId = new TestTokenIdentifier(new Text(current - .getUserName())); - Token token = new Token(tokenId, - sm); - SecurityUtil.setTokenService(token, addr); - current.addToken(token); + if (useToken) { + TestTokenIdentifier tokenId = new TestTokenIdentifier( + new Text(current.getUserName())); + Token token = + new Token(tokenId, sm); + SecurityUtil.setTokenService(token, addr); + current.addToken(token); + } - current.doAs(new PrivilegedExceptionAction() { - @Override - public Object run() throws IOException { - TestSaslProtocol proxy = null; - try { - proxy = (TestSaslProtocol) RPC.getProxy(TestSaslProtocol.class, - TestSaslProtocol.versionID, addr, conf); - Assert.assertEquals(AuthenticationMethod.TOKEN, proxy.getAuthMethod()); - } finally { - if (proxy != null) { - RPC.stopProxy(proxy); + conf.set(HADOOP_SECURITY_AUTHENTICATION, isSecureClient ? "kerberos" : "simple"); + UserGroupInformation.setConfiguration(conf); + try { + return current.doAs(new PrivilegedExceptionAction() { + @Override + public AuthenticationMethod run() throws IOException { + TestSaslProtocol proxy = null; + try { + proxy = (TestSaslProtocol) RPC.getProxy(TestSaslProtocol.class, + TestSaslProtocol.versionID, addr, conf); + return proxy.getAuthMethod(); + } finally { + if (proxy != null) { + RPC.stopProxy(proxy); + } } } - return null; - } - }); - server.stop(); + }); + } finally { + server.stop(); + } } public static void main(String[] args) throws Exception {