Hi, I now changed the implementation of option #1 to use separate SSLContext instances in SSLContextAndOptions for client and server role. Each SSLContext is initialized with standard PKIX managers from its own keystore / truststore. There is no custom key- or trust-manager. As far as I understand, this should work in FIPS mode. There is no change in config properties, and it is fully backward compatible.
What are your thoughts? Thanks, Dávid Dávid Paksy <[email protected]> ezt írta (időpont: 2026. aug. 17., H, 11:31): > Hi Andor, > > Sorry, I was too quick to send my response. Probably you didn't meant the > FIPS mode property in ZooKeeper but FIPS mode in Java. > > Enabling FIPS mode in Java imposes strict constraints on which KeyManager > and TrustManager implementations can be instantiated and initialized. > I looked into this and in FIPS mode, SSLContext.init() will throw an > exception if custom manager instances are passed. Something like: > java.security.KeyManagementException: FIPS mode: only SunJSSE > TrustManagers may be used. > > I agree that this can be an issue for the custom KeyManager and > TrustManager implementation as both custom classes get passed directly to > SSLContext.init(). So this approach will not work in FIPS mode. > > I'll look into this more and come up with another proposal. > > Best Regards, > Dávid > > > Dávid Paksy <[email protected]> ezt írta (időpont: 2026. aug. 17., H, > 10:20): > >> Hi Andor, >> >> Many thanks for your feedback. >> >> Hmm, very good question. As far as I understand, in FIPS mode, the >> split-truststore routing should still function. >> >> The certificate routing in ClientServerX509TrustManager is still active >> in FIPS mode. >> >> This class "sits above" the FIPS branching. It routes: >> - checkServerTrusted -> client trust manager (validates remote server >> certs) >> - checkClientTrusted -> server trust manager (validates remote client >> certs) >> >> Each inner trust manager is whatever createTrustManager() returns - in >> FIPS mode that's the raw JDK PKIX trust manager instead of ZKTrustManager. >> The routing itself is unaffected. >> >> Regarding EKU enforcement, there's no explicit EKU checking in >> ZKTrustManager or ClientServerX509TrustManager.The JDK's built-in PKIX >> validator enforces EKU constraints. So in FIPS mode, EKU validation should >> work identically. >> >> Or do you think I'm missing something? >> >> Best Regards, >> Dávid >> >> Andor Molnár <[email protected]> ezt írta (időpont: 2026. aug. 13., Cs, >> 23:00): >> >>> Hi David, >>> >>> Option #1 >>> >>> You mentioned a custom ZooKeeper trustmanager implementation which picks >>> the right certificate for client/server validation. How would it work in >>> FIPS mode when we don’t use custom trustmanagers, but rely on JDK >>> implementation? >>> >>> Apart from this I tend to support this option. Sounds more secure and it >>> could include a fallback mechanism to Option #2 if only one keystore is >>> specified in the config. >>> >>> Andor >>> >>> >>> >>> > On Aug 12, 2026, at 10:44, Dávid Paksy <[email protected]> wrote: >>> > >>> > Hi All, >>> > >>> > I’d like to start a discussion to improve public Certificate >>> Authorities >>> > (CA) compatibility. Industry standards and public CA-s (like DigiCert) >>> are >>> > sunsetting multi-use certificates, making the current requirement for >>> dual >>> > serverAuth and clientAuth Extended Key Usages (EKUs) difficult to >>> manage. >>> > >>> > ZooKeeper currently only supports dual-purpose certificates (or no EKU >>> > extension at all which means unconstrained). These certificates carry >>> both >>> > the serverAuth and clientAuth EKUs, meaning the same key and >>> certificate is >>> > used whether the service running on the host is acting as a TLS server >>> or >>> > as a client in a mutual-TLS (mTLS) handshake. >>> > In a ZooKeeper quorum cluster, every node acts as both TLS client and >>> > server to its peers, making the single-EKU question especially >>> relevant for >>> > inter-node communication. >>> > >>> > In order to support single EKU certs I see the following two possible >>> > options. >>> > >>> > 1. **Separate keystore / truststore files for client and server TLS >>> roles** >>> > >>> > This requires new config properties (under "ssl." and "ssl.quorum." >>> > prefixes): >>> > - client.keyStore.{location,password,passwordPath,type} >>> > - server.trustStore.{location,password,passwordPath,type} >>> > >>> > For code changes we could introduce new X509ExtendedKeyManager and >>> > X509ExtendedTrustManager implementations: >>> > - ClientServerX509KeyManager - routes chooseServerAlias() to the >>> server >>> > keystore and chooseClientAlias() to a dedicated client keystore >>> > - ClientServerX509TrustManager - routes checkClientTrusted() >>> (validating >>> > incoming client certs when acting as server) to a dedicated server-role >>> > truststore, and checkServerTrusted() (validating remote server certs >>> when >>> > acting as client) to the default truststore >>> > >>> > If the new properties are absent, we fall back to the existing shared >>> > keystore / truststore (to be fully backward compatible). >>> > >>> > This approach would give independent trust chains and better >>> > least-privilege in hardened environments - file permissions can >>> restrict >>> > which process/role accesses which key, however this means more files >>> per >>> > node and more config properties to set. >>> > >>> > I filed ZOOKEEPER-5070 for this and have a draft PR there. >>> > >>> > --- >>> > >>> > 2. **Single keystore with different alias for the client and server >>> roles** >>> > >>> > We use the PKIX KeyManagerFactory which performs EKU-aware alias >>> selection. >>> > Here we might not need code change. But this way we are relying on JDK >>> > implementation behavior that isn't formally specified in the >>> > KeyManagerFactory API contract; may differ across JDK vendors (IBM >>> Semeru, >>> > Azul, GraalVM). >>> > >>> > However Trust side has no equivalent auto-routing — the TrustManager >>> uses >>> > one truststore for both checkClientTrusted() and checkServerTrusted(). >>> We >>> > cannot use different CAs for client-facing vs quorum communication. >>> > >>> > This approach would mean fewer files, simpler deployment but principle >>> of >>> > least privilege is weaker - a compromised process always has both keys. >>> > >>> > --- >>> > >>> > This single EKU topic is also being discussed on the Apache Kafka >>> developer >>> > mailing list and they tend to favor option #2 (Single keystore with >>> > different aliases): >>> > https://lists.apache.org/[email protected]:lte=1M:eku >>> > >>> > >>> > What are your thoughts on these? >>> > Any experience using single EKU certificates with ZooKeeper? >>> > Do you see more possible options? >>> > >>> > Best Regards, >>> > Dávid >>> >>>
