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