-
Notifications
You must be signed in to change notification settings - Fork 9.1k
HADOOP-19212. Avoid Subject.getSubject method on newer JVMs #7081
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
base: trunk
Are you sure you want to change the base?
Conversation
In Java 23, Subject.getSubject requires setting the system property java.security.manager to allow, else it will throw an exception. More detail is available in the release notes: https://jdk.java.net/23/release-notes This is in support of the eventual removal of the security manager, at which point, Subject.getSubject will be removed. Since this project supports older Java releases, and the API which one must migrate to was only introduced in Java 18, I hid the implementations behind a runtime version check. I verified against several local Java versions that the correct classes are loaded (and more importantly, that the incorrect classes are _not_ loaded). The outright removal of the security manager, and by extension the Subject.getSubject method would be a particularly large problem if we do not address this, which was my motivation here. Some manual testing on Java 11, 17, and 23: ``` » java --version openjdk 11.0.23 2024-04-16 LTS » java -cp hadoop-auth-3.5.0-SNAPSHOT.jar org.apache.hadoop.util.subject.SubjectAdapter Current subject is null » java --version openjdk 17.0.5 2022-10-18 LTS » java -cp hadoop-auth-3.5.0-SNAPSHOT.jar org.apache.hadoop.util.subject.SubjectAdapter Current subject is null » java --version openjdk 23 2024-09-17 » java -cp hadoop-auth-3.5.0-SNAPSHOT.jar org.apache.hadoop.util.subject.SubjectAdapter Current subject is null ``` As desired, the class containing the old implementation does not get loaded on a new JVM: ``` » java --version openjdk 23 2024-09-17 » java -verbose:class -cp hadoop-auth-3.5.0-SNAPSHOT.jar org.apache.hadoop.util.subject.SubjectAdapter | \grep -i subject | grep "class,load" | awk '{print $2}' org.apache.hadoop.util.subject.SubjectAdapter org.apache.hadoop.util.subject.HiddenGetSubject javax.security.auth.Subject org.apache.hadoop.util.subject.GetSubjectNg ``` And the inverse is true for an older JVM: ``` » java --version openjdk 17.0.5 2022-10-18 LTS » java -verbose:class -cp hadoop-auth-3.5.0-SNAPSHOT.jar org.apache.hadoop.util.subject.SubjectAdapter | \grep -i subject | grep "class,load" | awk '{print $2}' org.apache.hadoop.util.subject.SubjectAdapter org.apache.hadoop.util.subject.HiddenGetSubject javax.security.auth.Subject org.apache.hadoop.util.subject.ClassicGetSubject javax.security.auth.Subject$1 ```
🎊 +1 overall
This message was automatically generated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense to me. There's a checkstyle warning but glad it's not a big change.
Are you finding other issues with JDK23? I know it's just come out this month.
I am happy to report no other issues. This Subject usage in hadoop-common impacts a great many projects, so |
Ok sounds like we should get it backported to 3.4.x and 3.3.x too. |
@steveloughran PR for HADOOP-19212. I updated the title to reflect the duplicate change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is great! we'd been warned about this but nobody had written the code.
I've made some comments. Can you also change the title to that of the previous JIRA on this. Thanks
|
||
GetSubjectNg() { | ||
try { | ||
currentMethod = Subject.class.getMethod("current"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use DynMethods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class, and all classes added in this PR, are in the hadoop-auth module because Subject.getSubject is used in both hadoop-auth (KerberosAuthenticator) and hadoop-common (UserGroupInformation). hadoop-common depends on hadoop-auth, so that seemed like a reasonable fit.
DynMethods, however, resides in hadoop-common, and thus it cannot be used in hadoop-auth.
How would you like me to proceed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steveloughran ,
@jbrinegar 's point is valid.
How do you propose solving the circular dependency issue ?
Should the DynMethods code be copied to hadoop-auth ?
Is there some suiteable small external dependency that provides DynMethods ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moving Dyn*
to hadoop-auth
module? then they can be consumed by every hadoop module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree.
I've just confirmed that hadoop-common already depends on hadoop-auth, so the move should be transparent to all users.
static { | ||
int version = 0; | ||
try { | ||
version = Integer.parseInt(System.getProperty("java.specification.version")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Shell.isJavaVersionAtLeast(18)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment about dependencies applies to Shell as well. Let me know how you'd like to proceed with this one as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO testing trying to load the new classes and failing over to the old classes if they cannot be found would be more robust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO testing trying to load the new classes and failing over to the old classes if they cannot be found would be more robust.
interesting thought.
how about
- we avoid Subject
- pull the string on L33 to a constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stoty interesting thought about attempting and fallbacks
We'd try the java18+ first & fall back to java <= 17 otherwise? or do it in the opposite direction, at least for now?
...-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/subject/SubjectAdapter.java
Outdated
Show resolved
Hide resolved
...-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/subject/SubjectAdapter.java
Outdated
Show resolved
Hide resolved
...op-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/subject/GetSubjectNg.java
Outdated
Show resolved
Hide resolved
@steveloughran thank you for the review. I have pushed a second commit that addresses feedback marked with 👍. The title of this PR was updated to include "HADOOP-19212." as a prefix. I am happy to rebase/squash once review is complete, if squashing is necessary. Due to dependencies, I cannot use |
...ommon-project/hadoop-auth/src/main/java/org/apache/hadoop/util/subject/HiddenGetSubject.java
Outdated
Show resolved
Hide resolved
🎊 +1 overall
This message was automatically generated. |
🎊 +1 overall
This message was automatically generated. |
...-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/subject/SubjectAdapter.java
Outdated
Show resolved
Hide resolved
...mon-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm except for 2 minor issues.
JEP 486: Permanently Disable the Security Manager is on the JDK's technical roadmap. As part of the changes (PR in review) will be for Subject.current to throw unconditionally, meaning the workaround to allow a SecurityManager will no longer work. So the timing of the PR here is good. |
💔 -1 overall
This message was automatically generated. |
I am not sure I have the permissions to retrigger the failing checks. Ultimate failure was on the remote side:
This java code compiles just fine:
|
// how getSubject operates depends on the JVM calling it. | ||
// asserting that it does not throw is a valid test, especially on Java 18 and above | ||
// prior to Java 18, this method is just a simple wrapper | ||
assertDoesNotThrow(() -> SubjectAdapter.getSubject()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use our LambdaTestUtils.intercept() here as it fails better, can validates the type and message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment in #7081 (comment) applies here as well.
/** | ||
* Indirectly calls Subject.current(), which exists in Java 18 and above only | ||
*/ | ||
class SubjectAdapterJava18AndAbove implements HiddenSubjectAdapter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use our new DynMethods classes, (which we copied from parquet and which is also found in iceberg). It fails better and as it is so common it's good to get familiar with it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steveloughran my comment from here still applies. #7081 (comment)
|
||
import javax.security.auth.Subject; | ||
|
||
public interface HiddenSubjectAdapter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like the name, maybe rename to JDKSpecificSubjectAdapter or similar ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this important patch, @jbrinegar ! I entered a few minor comments.
This is very nitpicky, but there is a comment in test code that still mentions AccessControlContext:
Can you remove mention of that in the comment?
This is a great step, but there will still be references to the deprecated APIs in other parts of the Hadoop codebase. (See below.) Are you interested in addressing all of these, or do you prefer to focus on getting in a hadoop-common patch and then we can focus on other spots piecemeal?
> grep -r --include '*.java' 'AccessController' *
hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java:import java.security.AccessController;
hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java: AccessControlContext context = AccessController.getContext();
hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/PlatformName.java:import java.security.AccessController;
hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/PlatformName.java: return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/impl/MetricsConfig.java:import static java.security.AccessController.*;
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/FastByteComparisons.java:import java.security.AccessController;
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/FastByteComparisons.java: theUnsafe = (Unsafe) AccessController.doPrivileged(
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java:import java.security.AccessController;
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java: AccessControlContext context = AccessController.getContext();
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/dynamic/DynMethods.java:import java.security.AccessController;
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/dynamic/DynMethods.java: AccessController.doPrivileged(new MakeAccessible(hidden));
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/dynamic/DynConstructors.java:import java.security.AccessController;
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/dynamic/DynConstructors.java: AccessController.doPrivileged(new MakeAccessible(hidden));
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/CleanerUtil.java:import java.security.AccessController;
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/CleanerUtil.java: final Object hack = AccessController.doPrivileged(
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/CleanerUtil.java: final Throwable error = AccessController.doPrivileged(
hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/checker/AbstractFuture.java:import java.security.AccessController;
hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/checker/AbstractFuture.java: AccessController.doPrivileged(
hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalDistributedCacheManager.java:import java.security.AccessController;
hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalDistributedCacheManager.java: return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalDistributedCacheManager.java: AccessController.doPrivileged(new PrivilegedAction<Void>() {
hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/MRApps.java:import java.security.AccessController;
hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/MRApps.java: return AccessController.doPrivileged(
hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/Submitter.java:import java.security.AccessController;
hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/Submitter.java: AccessController.doPrivileged(
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timeline-pluginstorage/src/main/java/org/apache/hadoop/yarn/server/timeline/EntityGroupFSTimelineStore.java:import java.security.AccessController;
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timeline-pluginstorage/src/main/java/org/apache/hadoop/yarn/server/timeline/EntityGroupFSTimelineStore.java: return AccessController.doPrivileged(
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/AuxiliaryServiceWithCustomClassLoader.java:import java.security.AccessController;
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/AuxiliaryServiceWithCustomClassLoader.java: return AccessController.doPrivileged(
* limitations under the License. | ||
*/ | ||
|
||
package org.apache.hadoop.util.subject; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a package-info.java in this new sub-directory and annotate it as private and unstable?
|
||
import javax.security.auth.Subject; | ||
|
||
public interface HiddenSubjectAdapter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be package-private instead of public?
Thank you for working on this patch @jbrinegar! This will help eliminate a big blocker for us in trying to upgrade our stack to JDK 23. |
@ryantomlinson95 @cnauroth @steveloughran @jbrinegar @jojochuang @pan3793 I'd like to bring to your attention HADOOP-19486, and the corresponding thread I have opened on dev@hadoop.apache.org , where I track my full JDK23 support work. Please check my email, and join the discussion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commented...I'd added the comments a while back but forgotten to submit the review. sorry
static { | ||
int version = 0; | ||
try { | ||
version = Integer.parseInt(System.getProperty("java.specification.version")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO testing trying to load the new classes and failing over to the old classes if they cannot be found would be more robust.
interesting thought.
how about
- we avoid Subject
- pull the string on L33 to a constant
static { | ||
int version = 0; | ||
try { | ||
version = Integer.parseInt(System.getProperty("java.specification.version")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stoty interesting thought about attempting and fallbacks
We'd try the java18+ first & fall back to java <= 17 otherwise? or do it in the opposite direction, at least for now?
My alternate patch tries the new API first, and falls back to the old one, @steveloughran . Conversion either way has an overhead, as the Callable / PrivilegedExceptionAction conversion and exception re-wrapping has to happen, but IMO anything we do inside those doAs() blocks is heavyweight enough that the overhad is relatively negligible. |
In Java 23, Subject.getSubject requires setting the system property java.security.manager to allow, else it will throw an exception. More detail is available in the release notes: https://jdk.java.net/23/release-notes
This is in support of the eventual removal of the security manager, at which point, Subject.getSubject will be removed. Since this project supports older Java releases, and the API which one must migrate to was only introduced in Java 18, I hid the implementations behind a runtime version check. I verified against several local Java versions that the correct classes are loaded (and more importantly, that the incorrect classes are not loaded). The outright removal of the security manager, and by extension the Subject.getSubject method would be a particularly large problem if we do not address this, which was my motivation here.
Some manual testing on Java 11, 17, and 23:
As desired, the class containing the old implementation does not get loaded on a new JVM:
And the inverse is true for an older JVM:
Description of PR
How was this patch tested?
For code changes:
LICENSE
,LICENSE-binary
,NOTICE-binary
files?