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