Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Improvement] Package sun.security.krb5 is not visible in Java 11 and 17 #625

Closed
2 of 3 tasks
kaijchen opened this issue Feb 17, 2023 · 13 comments
Closed
2 of 3 tasks
Assignees

Comments

@kaijchen
Copy link
Contributor

Code of Conduct

Search before asking

  • I have searched in the issues and found no similar issues.

What would you like to be improved?

Sun Kerberos binding, i.e. package sun.security.krb5 is blocking us to build Uniffle in Java 11 and Java 17.

# With JDK 11
mvn package -Djava.version=11

# With JDK 17
mvn package -Djava.version=17

How should we improve?

Apache Kerby, as an Apache Directory sub project, is a Java Kerberos binding. It provides a rich, intuitive and interoperable implementation, library, KDC and various facilities that integrates PKI, OTP and token (OAuth2) as desired in modern environments such as cloud, Hadoop and mobile.

We can replace sun.security.krb5 by Apache Kerby.

Are you willing to submit PR?

  • Yes I am willing to submit a PR!
@advancedxy
Copy link
Contributor

@zuston would you like to take a look at this?

@kaijchen would you also post some compiling failure logs when with JDK 11 or JDK 17?

@zuston
Copy link
Member

zuston commented Feb 21, 2023

@zuston would you like to take a look at this?

Yes. But I don't have much time in this month, perhaps next month.

@advancedxy
Copy link
Contributor

@zuston would you like to take a look at this?

Yes. But I don't have much time in this month, perhaps next month.

No worries. It's not urgent.

@kaijchen
Copy link
Contributor Author

would you also post some compiling failure logs when with JDK 11 or JDK 17?

https://github.com/kaijchen/incubator-uniffle/actions/runs/4228912219/jobs/7344901292#step:5:2884

Error:  Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project rss-common: Compilation failure
Error:  /home/runner/work/incubator-uniffle/incubator-uniffle/common/src/main/java/org/apache/uniffle/common/security/HadoopSecurityContext.java:[59,18] error: package sun.security.krb5 is not visible
Error:    (package sun.security.krb5 is declared in module java.security.jgss, which does not export it to the unnamed module)
Error:  -> [Help 1]

@slfan1989
Copy link
Collaborator

@kaijchen Can you assign this to me?

@kaijchen
Copy link
Contributor Author

@kaijchen Can you assign this to me?

Assigned, thanks for taking this issue.

@slfan1989
Copy link
Collaborator

@kaijchen Thank you very much!

@slfan1989
Copy link
Collaborator

slfan1989 commented Mar 13, 2023

I read this part of the code carefully, and I summarized the relevant information as follows:

We have 2 ways to solve the issue.

  • Remove sun.security.krb5.Config.refresh(); code.
  • Add --add-exports java.security.jgss/sun.security.krb5=ALL-UNNAMED in the pom.xml of the project.

Remove sun.security.krb5.Config.refresh(); code.

The code is based on the hadoop trunk branch. The code is different from hadoop-2.8.5, but this does not affect the conclusion.

  1. From my personal point of view, this part of the code can be removed, because this part of the code should only work when the location of the krb5.conf configuration file changes in the same JVM.
  2. We can find that when using loginUserFromKeytabAndReturnUGI, refreshKrb5Config=true will be added by default. Because the refreshKrb5Config=true parameter is set to true, if the content of krb5.conf changes, it will be automatically reloaded. But if the location of krb5.conf changes during the running of the program, this part of the logic will have no effect.
  3. I think we should not modify the location of krb5.conf during the running of the program. If the location of krb5.conf will not change, I think we can remove sun.security.krb5.Config.refresh();

RefreshKrb5Config=true, The code call is as follows:

UserGroupInformation#loginUserFromKeytabAndReturnUGI
    \--> UserGroupInformation#doSubjectLogin
          \--> HadoopConfiguration#new HadoopLoginContext()
                \--> HadoopLoginContext#constructor
                      \-->  LoginContext#constructor#init
                           \--> LoginContext#constructor#init#getAppConfigurationEntry(name);
                                \--> HadoopConfiguration#getAppConfigurationEntry

We use UserGroupInformation.loginUserFromKeytabAndReturnUGI(principal, keytabFile) to verify the identity of the user and ensure that legitimate users can access HDFS. We can find that refreshKrb5Config=true is added when initializing access to Kerberos configuration in the Hadoop code.

UserGroupInformation#loginUserFromKeytabAndReturnUGI

public
  static UserGroupInformation loginUserFromKeytabAndReturnUGI(String user,
                                  String path
                                  ) throws IOException {
    if (!isSecurityEnabled())
      return UserGroupInformation.getCurrentUser();

    LoginParams params = new LoginParams();
    params.put(LoginParam.PRINCIPAL, user);
    params.put(LoginParam.KEYTAB, path);
    return doSubjectLogin(null, params);
  }

UserGroupInformation#doSubjectLogin

private static UserGroupInformation doSubjectLogin(
      Subject subject, LoginParams params) throws IOException {
    ensureInitialized();
    // initial default login.
    if (subject == null && params == null) {
      params = LoginParams.getDefaults();
    }
    HadoopConfiguration loginConf = new HadoopConfiguration(params);
    try {
      // *****
      // We need to focus on this code
      // *****
      HadoopLoginContext login = newLoginContext(
        authenticationMethod.getLoginAppName(), subject, loginConf);
      login.login();
      UserGroupInformation ugi = new UserGroupInformation(login.getSubject());
      // attach login context for relogin unless this was a pre-existing
      // subject.
      if (subject == null) {
        params.put(LoginParam.PRINCIPAL, ugi.getUserName());
        ugi.setLogin(login);
        ugi.setLastLogin(Time.now());
      }
      return ugi;
    } catch (LoginException le) {
      KerberosAuthException kae =
        new KerberosAuthException(FAILURE_TO_LOGIN, le);
      if (params != null) {
        kae.setPrincipal(params.get(LoginParam.PRINCIPAL));
        kae.setKeytabFile(params.get(LoginParam.KEYTAB));
        kae.setTicketCacheFile(params.get(LoginParam.CCACHE));
      }
      throw kae;
    }
  }

HadoopConfiguration#new HadoopLoginContext()

private static HadoopLoginContext
  newLoginContext(String appName, Subject subject,
                  HadoopConfiguration loginConf)
      throws LoginException {
    // Temporarily switch the thread's ContextClassLoader to match this
    // class's classloader, so that we can properly load HadoopLoginModule
    // from the JAAS libraries.
    Thread t = Thread.currentThread();
    ClassLoader oldCCL = t.getContextClassLoader();
    t.setContextClassLoader(HadoopLoginModule.class.getClassLoader());
    try {
      return new HadoopLoginContext(appName, subject, loginConf);
    } finally {
      t.setContextClassLoader(oldCCL);
    }
  }

HadoopLoginContext#constructor

HadoopLoginContext(String appName, Subject subject,
                       HadoopConfiguration conf) throws LoginException {
      super(appName, subject, null, conf);
      this.appName = appName;
      this.conf = conf;
    }

LoginContext#constructor(Configuration config is HadoopConfiguration)

public LoginContext(String name, Subject subject,
                        CallbackHandler callbackHandler,
                        Configuration config) throws LoginException {
        this.config = config;
        if (config != null) {
            creatorAcc = java.security.AccessController.getContext();
        }
        // 
        init(name);
        if (subject != null) {
            this.subject = subject;
            subjectProvided = true;
        }
        if (callbackHandler == null) {
            loadDefaultCallbackHandler();
        } else if (creatorAcc == null) {
            this.callbackHandler = new SecureCallbackHandler
                                (java.security.AccessController.getContext(),
                                callbackHandler);
        } else {
            this.callbackHandler = callbackHandler;
        }
    }

LoginContext#constructor#init#getAppConfigurationEntry

private void init(String name) throws LoginException {

        .....

        // get the LoginModules configured for this application
        // We can view HadoopConfiguration's getAppConfigurationEntry.
        AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
        if (entries == null) {

            if (sm != null && creatorAcc == null) {
                sm.checkPermission(new AuthPermission
                                ("createLoginContext." + OTHER));
            }

            entries = config.getAppConfigurationEntry(OTHER);
            if (entries == null) {
                MessageFormat form = new MessageFormat(ResourcesMgr.getString
                        ("No.LoginModules.configured.for.name"));
                Object[] source = {name};
                throw new LoginException(form.format(source));
            }
        }
        moduleStack = new ModuleInfo[entries.length];
        for (int i = 0; i < entries.length; i++) {
            // clone returned array
            moduleStack[i] = new ModuleInfo
                                (new AppConfigurationEntry
                                        (entries[i].getLoginModuleName(),
                                        entries[i].getControlFlag(),
                                        entries[i].getOptions()),
                                null);
        }
        .....
    }

add --add-exports java.security.jgss/sun.security.krb5=ALL-UNNAMED

after JDK11, package sun.security.krb5 is not visible. I refer to hbase and flink. These two projects added --add-exports java.security.jgss/sun.security.krb5=ALL-UNNAMED when compiling maven, which can ensure that the compilation passes.

no need to use apache kerby

Apache Kerby is a good project, which is used in the code of Hadoop MiniKDC, but only in test classes. When Hadoop performs Kerberos authentication, it is still implemented based on Java JAAS. We rely on hadoop for kerberos authentication, there should be no need to use Apache Kerby. Hbase and Flink also have the need to verify Kerberos, but without using Apache Kerby, I think we can also directly rely on hadoop without using Apache Kerby.

@jerqi
Copy link
Contributor

jerqi commented Mar 13, 2023

cc @zuston

@slfan1989
Copy link
Collaborator

cc @zuston

@jerqi @zuston @kaijchen I still need to add some information. after the information is completed, please help to review it.

@slfan1989
Copy link
Collaborator

@jerqi @zuston @kaijchen Can you please check my comment above? Thank you so much!

I think adding --add-exports java.security.jgss/sun.security.krb5=ALL-UNNAMED in the pom file is a better option. But I still want to ask sun.security.krb5.Config.refresh(); the purpose of this.

@zuston
Copy link
Member

zuston commented Mar 14, 2023

Wow, really thanks for your great work. @slfan1989

no need to use apache kerby

+1.

But I still want to ask sun.security.krb5.Config.refresh(); the purpose of this.

This part of code is introduced in #184 . If I remember correctly, this is just for unit test to simulate setting the profile w/o, because all tests run in one JVM.

If test cases could pass, I prefer removing this.

@kaijchen
Copy link
Contributor Author

But I still want to ask sun.security.krb5.Config.refresh(); the purpose of this.

Thanks @slfan1989 for the investigation, +1 for this proposal. I'll change the title of this issue.

@kaijchen kaijchen changed the title [Improvement] Replace Sun Kerberos binding with Apache Kerby [Improvement] Package sun.security.krb5 is not visible in Java 11 and 17 Mar 14, 2023
jerqi pushed a commit that referenced this issue Mar 15, 2023
…11 and 17. (#726)

### What changes were proposed in this pull request?

Try remove `sun.security.krb5.Config.refresh();`

### Why are the changes needed?

`sun.security.krb5` is not visible in Java 11 and 17. We need to compile on JDK11 and JDK17

### Does this PR introduce _any_ user-facing change?

No.

### How was this patch tested?

Unit test verification.

Co-authored-by: slfan1989 <louj1988@@>
@jerqi jerqi closed this as completed Mar 15, 2023
advancedxy pushed a commit to advancedxy/incubator-uniffle that referenced this issue Mar 21, 2023
… Java 11 and 17. (apache#726)

### What changes were proposed in this pull request?

Try remove `sun.security.krb5.Config.refresh();`

### Why are the changes needed?

`sun.security.krb5` is not visible in Java 11 and 17. We need to compile on JDK11 and JDK17

### Does this PR introduce _any_ user-facing change?

No.

### How was this patch tested?

Unit test verification.

Co-authored-by: slfan1989 <louj1988@@>
xianjingfeng pushed a commit to xianjingfeng/incubator-uniffle that referenced this issue Apr 5, 2023
… Java 11 and 17. (apache#726)

### What changes were proposed in this pull request?

Try remove `sun.security.krb5.Config.refresh();`

### Why are the changes needed?

`sun.security.krb5` is not visible in Java 11 and 17. We need to compile on JDK11 and JDK17

### Does this PR introduce _any_ user-facing change?

No.

### How was this patch tested?

Unit test verification.

Co-authored-by: slfan1989 <louj1988@@>
jerqi pushed a commit that referenced this issue Jun 6, 2023
…refresh. (#932)

### What changes were proposed in this pull request?

sun.security.krb5.Config should be refresh when krb5 conf file is setup.

### Why are the changes needed?

We found error when use hadoop3.2 profile.  

By my debug, I found the static variable 'singleton' in sun.security.krb5.Config is not reload. We should refresh when krb5 config is setup. As described in #625, I refresh only in java8.

Build error logs:

```
-------------------------------------------------------------------------------
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 1.034 s <<< FAILURE! - in org.apache.uniffle.common.security.SecurityContextFactoryTest
org.apache.uniffle.common.security.SecurityContextFactoryTest  Time elapsed: 1.034 s  <<< ERROR!
java.lang.Exception: java.lang.IllegalArgumentException: Can't get Kerberos realm
	at org.apache.uniffle.common.KerberizedHadoop.setup(KerberizedHadoop.java:111)
	at org.apache.uniffle.common.KerberizedHadoopBase.init(KerberizedHadoopBase.java:41)
	at org.apache.uniffle.common.security.SecurityContextFactoryTest.beforeAll(SecurityContextFactoryTest.java:38)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:126)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeAllMethod(TimeoutExtension.java:68)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllMethods$11(ClassBasedTestDescriptor.java:397)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllMethods(ClassBasedTestDescriptor.java:395)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:209)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:80)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:148)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:150)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:124)
	at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345)
	at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418)
Caused by: java.lang.IllegalArgumentException: Can't get Kerberos realm
	at org.apache.hadoop.security.HadoopKerberosName.setConfiguration(HadoopKerberosName.java:71)
	at org.apache.hadoop.security.UserGroupInformation.initialize(UserGroupInformation.java:319)
	at org.apache.hadoop.security.UserGroupInformation.setConfiguration(UserGroupInformation.java:365)
	at org.apache.uniffle.common.KerberizedHadoop.startKerberizedDFS(KerberizedHadoop.java:258)
	at org.apache.uniffle.common.KerberizedHadoop.setup(KerberizedHadoop.java:107)
	... 61 more
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.hadoop.security.authentication.util.KerberosUtil.getDefaultRealm(KerberosUtil.java:110)
	at org.apache.hadoop.security.HadoopKerberosName.setConfiguration(HadoopKerberosName.java:69)
	... 65 more
Caused by: KrbException: Cannot locate default realm
	at sun.security.krb5.Config.getDefaultRealm(Config.java:1137)
	... 71 more


```

### How was this patch tested?

CI test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants