فهرست منبع

HADOOP-7853. multiple javax security configurations cause conflicts. (daryn via tucu)

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1208751 13f79535-47bb-0310-9956-ffa450edef68
Alejandro Abdelnur 13 سال پیش
والد
کامیت
d8930feeae

+ 2 - 5
hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java

@@ -110,10 +110,6 @@ public class KerberosAuthenticator implements Authenticator {
     }
   }
 
-  static {
-    javax.security.auth.login.Configuration.setConfiguration(new KerberosConfiguration());
-  }
-
   private URL url;
   private HttpURLConnection conn;
   private Base64 base64;
@@ -187,7 +183,8 @@ public class KerberosAuthenticator implements Authenticator {
       Subject subject = Subject.getSubject(context);
       if (subject == null) {
         subject = new Subject();
-        LoginContext login = new LoginContext("", subject);
+        LoginContext login = new LoginContext("", subject,
+            null, new KerberosConfiguration());
         login.login();
       }
       Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {

+ 3 - 0
hadoop-common-project/hadoop-common/CHANGES.txt

@@ -97,6 +97,9 @@ Trunk (unreleased changes)
     HADOOP-7833. Fix findbugs warnings in protobuf generated code.
     (John Lee via suresh)
 
+    HADOOP-7853. multiple javax security configurations cause conflicts. 
+    (daryn via tucu)
+
   OPTIMIZATIONS
 
     HADOOP-7761. Improve the performance of raw comparisons. (todd)

+ 1 - 1
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/SecurityUtil.java

@@ -70,7 +70,7 @@ public class SecurityUtil {
       if (isOriginalTGT(t))
         return t;
     }
-    throw new IOException("Failed to find TGT from current Subject");
+    throw new IOException("Failed to find TGT from current Subject:"+current);
   }
   
   /**

+ 47 - 37
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java

@@ -118,18 +118,30 @@ public class UserGroupInformation {
 
     @Override
     public boolean commit() throws LoginException {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("hadoop login commit");
+      }
       // if we already have a user, we are done.
       if (!subject.getPrincipals(User.class).isEmpty()) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("using existing subject:"+subject.getPrincipals());
+        }
         return true;
       }
       Principal user = null;
       // if we are using kerberos, try it out
       if (useKerberos) {
         user = getCanonicalUser(KerberosPrincipal.class);
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("using kerberos user:"+user);
+        }
       }
       // if we don't have a kerberos user, use the OS user
       if (user == null) {
         user = getCanonicalUser(OS_PRINCIPAL_CLASS);
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("using local user:"+user);
+        }
       }
       // if we found the user, add our principal
       if (user != null) {
@@ -148,11 +160,17 @@ public class UserGroupInformation {
 
     @Override
     public boolean login() throws LoginException {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("hadoop login");
+      }
       return true;
     }
 
     @Override
     public boolean logout() throws LoginException {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("hadoop logout");
+      }
       return true;
     }
   }
@@ -220,26 +238,6 @@ public class UserGroupInformation {
     if (!(groups instanceof TestingGroups)) {
       groups = Groups.getUserToGroupsMappingService(conf);
     }
-    // Set the configuration for JAAS to be the Hadoop configuration. 
-    // This is done here rather than a static initializer to avoid a
-    // circular dependence.
-    javax.security.auth.login.Configuration existingConfig = null;
-    try {
-      existingConfig =
-        javax.security.auth.login.Configuration.getConfiguration();
-    } catch (SecurityException se) {
-      // If no security configuration is on the classpath, then
-      // we catch this exception, and we don't need to delegate
-      // to anyone
-    }
-
-    if (existingConfig instanceof HadoopConfiguration) {
-      LOG.info("JAAS Configuration already set up for Hadoop, not re-installing.");
-    } else {
-      javax.security.auth.login.Configuration.setConfiguration(
-        new HadoopConfiguration(existingConfig));
-    }
-
     isInitialized = true;
     UserGroupInformation.conf = conf;
   }
@@ -398,12 +396,6 @@ public class UserGroupInformation {
     private static final AppConfigurationEntry[] KEYTAB_KERBEROS_CONF =
       new AppConfigurationEntry[]{KEYTAB_KERBEROS_LOGIN, HADOOP_LOGIN};
 
-    private final javax.security.auth.login.Configuration parent;
-
-    HadoopConfiguration(javax.security.auth.login.Configuration parent) {
-      this.parent = parent;
-    }
-
     @Override
     public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
       if (SIMPLE_CONFIG_NAME.equals(appName)) {
@@ -414,13 +406,16 @@ public class UserGroupInformation {
         KEYTAB_KERBEROS_OPTIONS.put("keyTab", keytabFile);
         KEYTAB_KERBEROS_OPTIONS.put("principal", keytabPrincipal);
         return KEYTAB_KERBEROS_CONF;
-      } else if (parent != null) {
-        return parent.getAppConfigurationEntry(appName);
       }
       return null;
     }
   }
   
+  private static LoginContext
+  newLoginContext(String appName, Subject subject) throws LoginException {
+    return new LoginContext(appName, subject, null, new HadoopConfiguration());
+  }
+  
   private LoginContext getLogin() {
     return user.getLogin();
   }
@@ -476,10 +471,10 @@ public class UserGroupInformation {
         Subject subject = new Subject();
         LoginContext login;
         if (isSecurityEnabled()) {
-          login = new LoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME,
+          login = newLoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME,
               subject);
         } else {
-          login = new LoginContext(HadoopConfiguration.SIMPLE_CONFIG_NAME, 
+          login = newLoginContext(HadoopConfiguration.SIMPLE_CONFIG_NAME, 
               subject);
         }
         login.login();
@@ -503,6 +498,9 @@ public class UserGroupInformation {
       } catch (LoginException le) {
         throw new IOException("failure to login", le);
       }
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("UGI loginUser:"+loginUser);
+      }
     }
     return loginUser;
   }
@@ -616,7 +614,7 @@ public class UserGroupInformation {
     long start = 0;
     try {
       login = 
-        new LoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, subject);
+        newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, subject);
       start = System.currentTimeMillis();
       login.login();
       metrics.loginSuccess.add(System.currentTimeMillis() - start);
@@ -695,7 +693,7 @@ public class UserGroupInformation {
         login.logout();
         // login and also update the subject field of this instance to
         // have the new credentials (pass it to the LoginContext constructor)
-        login = new LoginContext(
+        login = newLoginContext(
             HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, getSubject());
         LOG.info("Initiating re-login for " + keytabPrincipal);
         start = System.currentTimeMillis();
@@ -744,7 +742,7 @@ public class UserGroupInformation {
       //login and also update the subject field of this instance to 
       //have the new credentials (pass it to the LoginContext constructor)
       login = 
-        new LoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, 
+        newLoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, 
             getSubject());
       LOG.info("Initiating re-login for " + getUserName());
       login.login();
@@ -781,7 +779,7 @@ public class UserGroupInformation {
       Subject subject = new Subject();
       
       LoginContext login = 
-        new LoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, subject); 
+        newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, subject); 
        
       start = System.currentTimeMillis();
       login.login();
@@ -1053,11 +1051,12 @@ public class UserGroupInformation {
    */
   @Override
   public String toString() {
+    StringBuilder sb = new StringBuilder(getUserName());
+    sb.append(" (auth:"+getAuthenticationMethod()+")");
     if (getRealUser() != null) {
-      return getUserName() + " via " +  getRealUser().toString();
-    } else {
-      return getUserName();
+      sb.append(" via ").append(getRealUser().toString());
     }
+    return sb.toString();
   }
 
   /**
@@ -1132,6 +1131,7 @@ public class UserGroupInformation {
    * @return the value from the run method
    */
   public <T> T doAs(PrivilegedAction<T> action) {
+    logPrivilegedAction(subject, action);
     return Subject.doAs(subject, action);
   }
   
@@ -1149,9 +1149,11 @@ public class UserGroupInformation {
   public <T> T doAs(PrivilegedExceptionAction<T> action
                     ) throws IOException, InterruptedException {
     try {
+      logPrivilegedAction(subject, action);
       return Subject.doAs(subject, action);
     } catch (PrivilegedActionException pae) {
       Throwable cause = pae.getCause();
+      LOG.error("PriviledgedActionException as:"+this+" cause:"+cause);
       if (cause instanceof IOException) {
         throw (IOException) cause;
       } else if (cause instanceof Error) {
@@ -1166,6 +1168,14 @@ public class UserGroupInformation {
     }
   }
 
+  private void logPrivilegedAction(Subject subject, Object action) {
+    if (LOG.isDebugEnabled()) {
+      // would be nice if action included a descriptive toString()
+      String where = new Throwable().getStackTrace()[2].toString();
+      LOG.debug("PrivilegedAction as:"+this+" from:"+where);
+    }
+  }
+
   private void print() throws IOException {
     System.out.println("User: " + getUserName());
     System.out.print("Group Ids: ");

+ 7 - 6
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestDoAsEffectiveUser.java

@@ -116,8 +116,9 @@ public class TestDoAsEffectiveUser {
             return UserGroupInformation.getCurrentUser();
           }
         });
-    Assert.assertTrue(curUGI.toString().equals(
-        PROXY_USER_NAME + " via " + REAL_USER_NAME));
+    Assert.assertEquals(
+        PROXY_USER_NAME + " (auth:PROXY) via " + REAL_USER_NAME + " (auth:SIMPLE)",
+        curUGI.toString());
   }
 
   @TokenInfo(TestTokenSelector.class)
@@ -174,7 +175,7 @@ public class TestDoAsEffectiveUser {
             }
           });
 
-      Assert.assertEquals(PROXY_USER_NAME + " via " + REAL_USER_NAME, retVal);
+      Assert.assertEquals(PROXY_USER_NAME + " (auth:SIMPLE) via " + REAL_USER_NAME + " (auth:SIMPLE)", retVal);
     } catch (Exception e) {
       e.printStackTrace();
       Assert.fail();
@@ -216,7 +217,7 @@ public class TestDoAsEffectiveUser {
             }
           });
 
-      Assert.assertEquals(PROXY_USER_NAME + " via " + REAL_USER_NAME, retVal);
+      Assert.assertEquals(PROXY_USER_NAME + " (auth:SIMPLE) via " + REAL_USER_NAME + " (auth:SIMPLE)", retVal);
     } catch (Exception e) {
       e.printStackTrace();
       Assert.fail();
@@ -446,7 +447,7 @@ public class TestDoAsEffectiveUser {
       }
     });
     //The user returned by server must be the one in the token.
-    Assert.assertEquals(REAL_USER_NAME + " via SomeSuperUser", retVal);
+    Assert.assertEquals(REAL_USER_NAME + " (auth:TOKEN) via SomeSuperUser (auth:SIMPLE)", retVal);
   }
 
   /*
@@ -498,7 +499,7 @@ public class TestDoAsEffectiveUser {
         }
       }
     });
-    String expected = REAL_USER_NAME + " via SomeSuperUser";
+    String expected = REAL_USER_NAME + " (auth:TOKEN) via SomeSuperUser (auth:SIMPLE)";
     Assert.assertEquals(retVal + "!=" + expected, expected, retVal);
   }
   

+ 22 - 39
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUserGroupInformation.java

@@ -17,6 +17,8 @@
 package org.apache.hadoop.security;
 
 import static org.junit.Assert.*;
+import org.junit.*;
+
 import org.mockito.Mockito;
 import static org.mockito.Mockito.mock;
 
@@ -32,9 +34,6 @@ import javax.security.auth.Subject;
 import javax.security.auth.login.AppConfigurationEntry;
 import javax.security.auth.login.LoginContext;
 
-import junit.framework.Assert;
-import org.junit.Test;
-
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.metrics2.MetricsRecordBuilder;
 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
@@ -50,17 +49,32 @@ public class TestUserGroupInformation {
   final private static String[] GROUP_NAMES = 
     new String[]{GROUP1_NAME, GROUP2_NAME, GROUP3_NAME};
   
-  private static javax.security.auth.login.Configuration mockJaasConf;
-
-  static {
-    setupMockJaasParent();
-
+  /**
+   * UGI should not use the default security conf, else it will collide
+   * with other classes that may change the default conf.  Using this dummy
+   * class that simply throws an exception will ensure that the tests fail
+   * if UGI uses the static default config instead of its own config
+   */
+  private static class DummyLoginConfiguration extends
+    javax.security.auth.login.Configuration
+  {
+    @Override
+    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+      throw new RuntimeException("UGI is not using its own security conf!");
+    } 
+  }
+  
+  /** configure ugi */
+  @BeforeClass
+  public static void setup() {
     Configuration conf = new Configuration();
     conf.set("hadoop.security.auth_to_local",
         "RULE:[2:$1@$0](.*@HADOOP.APACHE.ORG)s/@.*//" +
         "RULE:[1:$1@$0](.*@HADOOP.APACHE.ORG)s/@.*//"
         + "DEFAULT");
     UserGroupInformation.setConfiguration(conf);
+    javax.security.auth.login.Configuration.setConfiguration(
+        new DummyLoginConfiguration());
   }
   
   /** Test login method */
@@ -351,37 +365,6 @@ public class TestUserGroupInformation {
     }
   }
 
-  /**
-   * Setup a JAAS Configuration that handles a fake app.
-   * This runs before UserGroupInformation has been initialized,
-   * so UGI picks up this Configuration as the parent.
-   */
-  private static void setupMockJaasParent() {
-    javax.security.auth.login.Configuration existing = null;
-    try {
-      existing =javax.security.auth.login.Configuration.getConfiguration();
-      assertFalse("setupMockJaasParent should run before the Hadoop " +
-                  "configuration provider is installed.",
-                  existing.getClass().getCanonicalName()
-                  .startsWith("org.apache.hadoop"));
-    } catch (SecurityException se) {
-      // We get this if no configuration has been set. So it's OK.
-    }
-
-    mockJaasConf = mock(javax.security.auth.login.Configuration.class);
-    Mockito.doReturn(new AppConfigurationEntry[] {})
-      .when(mockJaasConf)
-      .getAppConfigurationEntry("foobar-app");
-    javax.security.auth.login.Configuration.setConfiguration(mockJaasConf);
-  }
-
-  @Test
-  public void testDelegateJaasConfiguration() throws Exception {
-    // This will throw if the Configuration doesn't have any entries
-    // for "foobar"
-    LoginContext login = new LoginContext("foobar-app");
-  }
-
   /**
    * Test for the case that UserGroupInformation.getCurrentUser()
    * is called when the AccessControlContext has a Subject associated