Hi all,

I'd like to open a discussion on how we handle the javax.security.auth.Subject 
API changes introduced by JEP 411, since it affects how Knox runs on newer JDKs 
and I think it's worth aligning as a community before we commit to an approach.


Background / driver
-----------------------
KNOX-3338 tracks the fact that on JDK 23+, 
Subject.getSubject(AccessController.getContext()) and Subject.doAs(...) are 
non-functional and throw UnsupportedOperationException. These were deprecated 
for removal in JDK 17 (JEP 411) and disabled in 23. Knox uses both in the 
request identity-propagation path (e.g. SubjectUtils.getCurrentSubject() and 
the various authentication filters), so Knox does not run correctly on JDK 23+ 
today.

We already had a proposed fix in PR #1277 that used reflection to call the JDK 
18+ replacements (Subject.current() / Subject.callAs()) when available and fall 
back to the deprecated methods otherwise. It works, but I've closed it for now 
because I think we can do better than runtime reflection, and the reflection 
approach also surfaced some correctness concerns around migrating the 
doAs/getSubject call sites consistently (they must be migrated as a paired set 
— current() only observes identities established by callAs()).


The constraint
-----------------
The replacement APIs (Subject.current(), Subject.callAs()) only exist in JDK 
18+. Knox is currently pinned to JDK 17 (maven.compiler.{source,target}=17, 
enforcer [17,18)), so the JDK-17 compile classpath simply doesn't have those 
methods, hence the reflection workaround.

Options I see
1. Reflection (as in PR #1277): works, but adds runtime reflection on a hot 
path and deprecation suppression, and is easy to get subtly wrong across the 
many call sites.
2. Bump the baseline to JDK 21: simplest code (call the new APIs directly), but 
drops JDK 17 support outright.
3. Multi-Release JAR (MRJAR): keep the JDK 17 baseline; ship a 
META-INF/versions/18 overlay for the handful of affected classes that calls 
current()/callAs() directly and type-safely. No runtime reflection, no 
deprecated calls on the modern path.

My preference: MRJAR (option 3)
- We only recently moved to a JDK 17 baseline, and we have not shipped a 
release on it yet (still 3.0.0-SNAPSHOT). Jumping straight to a higher baseline 
would effectively be a multi-step JDK bump within a single un-released cycle, 
which I'd rather avoid.
- There are also downstream distributions and existing deployments still 
standardized on JDK 17. Preserving the 17 baseline avoids disrupting those 
users while still making Knox correct on newer JDKs.
- MRJAR gets us both: run correctly on JDK 18-23+ and keep the 17 baseline, 
without reflection.

The trade-off is build complexity: MRJAR needs a JDK 18+ toolchain available at 
build time to compile the overlay (which means relaxing the enforcer's upper 
bound on the build JDK while keeping the target at 17), plus some 
maven-compiler-plugin wiring for the versioned source set. I think that's a 
reasonable one-time cost.

I'm happy to put up a PR prototyping the MRJAR layout for the affected classes 
if there's agreement on the direction.

Questions for the list:

- Any objection to MRJAR over bumping the baseline?
- Is anyone relying on / planning around a JDK baseline bump for 3.0.0 that I 
should factor in?

Thanks,
Sandor

Reply via email to