Skip to content

Commit d97ecd1

Browse files
committed
ZOOKEEPER-4949: Clean up TLS CRL/OCSP configuration
- Enable FIPS style server hostname verification if truststore is not specified - Make sure tcnative specific enableOCSP method is not called for JRE SSL provider - Add new config option to enable tcnative specific enableOCSP method - Add new config option to separetely enable certificate revocation checking for custom truststores - Add new config option to disable existing implicit certificate revocation checking logic for custom truststores - Document dependencies of TLS truststore related options
1 parent 6c5f788 commit d97ecd1

File tree

7 files changed

+283
-10
lines changed

7 files changed

+283
-10
lines changed

zookeeper-docs/src/main/resources/markdown/zookeeperAdmin.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,7 +1687,7 @@ and [SASL authentication for ZooKeeper](https://cwiki.apache.org/confluence/disp
16871687
Specifies the file path to a Java keystore containing the local
16881688
credentials to be used for client and quorum TLS connections, and the
16891689
password to unlock the file.
1690-
1690+
16911691
* *ssl.keyStore.passwordPath* and *ssl.quorum.keyStore.passwordPath* :
16921692
(Java system properties: **zookeeper.ssl.keyStore.passwordPath** and **zookeeper.ssl.quorum.keyStore.passwordPath**)
16931693
**New in 3.8.0:**
@@ -1762,20 +1762,59 @@ and [SASL authentication for ZooKeeper](https://cwiki.apache.org/confluence/disp
17621762
**New in 3.9.4:**
17631763
Specifies whether the client's hostname verification is enabled in client and quorum TLS negotiation process.
17641764
This option requires the corresponding *hostnameVerification* option to be `true`, or it will be ignored.
1765+
*ssl.clientHostnameVerification* has no effect, as the client does not receive TLS connections.
17651766
Default: true for quorum, false for clients
17661767

17671768
* *ssl.crl* and *ssl.quorum.crl* :
17681769
(Java system properties: **zookeeper.ssl.crl** and **zookeeper.ssl.quorum.crl**)
17691770
**New in 3.5.5:**
17701771
Specifies whether Certificate Revocation List is enabled in client and quorum TLS protocols.
1772+
This option requires that the *ssl.quorum.trustStore.location* is set, or it will be ignored.
1773+
Enabling this option will set the JVM global "com.sun.security.enableCRLDP" system property to true.
17711774
Default: false
17721775

17731776
* *ssl.ocsp* and *ssl.quorum.ocsp* :
17741777
(Java system properties: **zookeeper.ssl.ocsp** and **zookeeper.ssl.quorum.ocsp**)
17751778
**New in 3.5.5:**
17761779
Specifies whether Online Certificate Status Protocol is enabled in client and quorum TLS protocols.
1780+
This option requires that the *ssl.trustStore.location* is set, or it will be ignored for the client.
1781+
Enabling this option will set the JVM global "com.sun.security.enableCRLDP" system property to true.
1782+
Enabling this option will set JVM global "ocsp.enable" security property to true.
17771783
Default: false
17781784

1785+
* *ssl.revocationEnabled* and *ssl.quorum.revocationEnabled* :
1786+
(Java system properties: **zookeeper.ssl.revocationEnabled** and **zookeeper.ssl.quorum.revocationEnabled**)
1787+
**New in 3.10.0:**
1788+
Specifies whether Certificate Revocation checking is enabled in client and quorum TLS protocols.
1789+
This option requires that the *ssl.trustStore.location* is set, or it will be ignored for the client.
1790+
This options has no side effects on JVM global system properties.
1791+
Default: if the option is not set, or set to the value "default" then the JVM default settings
1792+
are used.
1793+
1794+
* *ssl.disableLegacyRevocationLogic* and *ssl.quorum.disableLegacyRevocationLogic* :
1795+
(Java system properties: **zookeeper.ssl.disableLegacyRevocationLogic** and **zookeeper.ssl.quorum.disableLegacyRevocationLogic**)
1796+
**New in 3.10.0:**
1797+
Traditionally, ZK has always explicitly disabled or enabled certificate revocation in the trustmanager based on the values of the
1798+
*ssl.ocsp* and *ssl.crl* properties. This made it impossible to completely rely on System properties for configuring
1799+
revocation checking for ZooKeeper.
1800+
Setting this property disables the logic for implicitly setting the certificate revocation check status on the trustManager,
1801+
allowing the JVM default behavior to be used.
1802+
It is recommended to set *ssl.ocsp* and *ssl.crl* to false, and *ssl.disableLegacyRevocationLogic* to true.
1803+
With those settings, ZK will use the JVM default CRL/OCSP settings, and not change the defaults. *ssl.revocationEnabled*
1804+
can still be used to enable/disable revocation checking for the custom SSL truststore.
1805+
This option requires that the *ssl.trustStore.location* is set, or it will be ignored for the client.
1806+
Default: false.
1807+
1808+
* *ssl.tcnative.ocsp* and *ssl.quorum.tcnative.ocsp* :
1809+
(Java system properties: **zookeeper.ssl.tcnative.ocsp** and **zookeeper.ssl.quorum.tcnative.ocsp**)
1810+
**New in 3.10.0:**
1811+
Specifies whether OCSP stapling is requested by the client.
1812+
This option has no effect unless the the OpenSSL tcnative SSL provider with the OpenSSL library is used.
1813+
Note that Zookeeper uses the the BoringSSL tcnative library by default, so even is the "OpenSSL" SSL provider is requested,
1814+
this won't do anything unless the default BoringSSL library is replaced with the OpenSSL one.
1815+
This options has no side effects on JVM global system properties.
1816+
Default: if the option is not set, or set to the value "default" then the library default is used.
1817+
17791818
* *ssl.clientAuth* and *ssl.quorum.clientAuth* :
17801819
(Java system properties: **zookeeper.ssl.clientAuth** and **zookeeper.ssl.quorum.clientAuth**)
17811820
**Added in 3.5.5, but broken until 3.5.7:**
@@ -1785,7 +1824,8 @@ and [SASL authentication for ZooKeeper](https://cwiki.apache.org/confluence/disp
17851824
* "want": server will "request" client authentication
17861825
* "need": server will "require" client authentication
17871826

1788-
Default: "need"
1827+
*ssl.clientHostnameVerification* has no effect.
1828+
Default: "need"
17891829

17901830
* *ssl.handshakeDetectionTimeoutMillis* and *ssl.quorum.handshakeDetectionTimeoutMillis* :
17911831
(Java system properties: **zookeeper.ssl.handshakeDetectionTimeoutMillis** and **zookeeper.ssl.quorum.handshakeDetectionTimeoutMillis**)

zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,19 @@ public SslContext createNettySslContextForClient(ZKConfig config)
7979
sslContextBuilder.trustManager(tm);
8080
}
8181

82-
sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty()));
82+
SslProvider sslProvider = getSslProvider(config);
83+
sslContextBuilder.sslProvider(sslProvider);
84+
// Note that sslContextBuilder.enableOcsp() doesn't do anything, unless the default BoringSSL
85+
// tcnative dependency is replaced with an OpenSsl one.
86+
if (sslProvider == SslProvider.OPENSSL || sslProvider == SslProvider.OPENSSL_REFCNT) {
87+
sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty()));
88+
}
89+
// Explicit option takes precedence if set
90+
if (config.getTristate(getSslTcnativeOcspEnabledProperty()).isTrue()) {
91+
sslContextBuilder.enableOcsp(true);
92+
} else if (config.getTristate(getSslTcnativeOcspEnabledProperty()).isFalse()) {
93+
sslContextBuilder.enableOcsp(false);
94+
}
8395
String[] enabledProtocols = getEnabledProtocols(config);
8496
if (enabledProtocols != null) {
8597
sslContextBuilder.protocols(enabledProtocols);
@@ -88,11 +100,11 @@ public SslContext createNettySslContextForClient(ZKConfig config)
88100
if (enabledCiphers != null) {
89101
sslContextBuilder.ciphers(enabledCiphers);
90102
}
91-
sslContextBuilder.sslProvider(getSslProvider(config));
92103

93104
SslContext sslContext1 = sslContextBuilder.build();
94105

95-
if (getFipsMode(config) && isServerHostnameVerificationEnabled(config)) {
106+
if ((getFipsMode(config) || config.getProperty(getSslKeystoreLocationProperty(), "").isEmpty())
107+
&& isServerHostnameVerificationEnabled(config)) {
96108
return addHostnameVerification(sslContext1, "Server");
97109
} else {
98110
return sslContext1;
@@ -123,7 +135,16 @@ public SslContext createNettySslContextForServer(ZKConfig config, KeyManager key
123135
sslContextBuilder.trustManager(trustManager);
124136
}
125137

126-
sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty()));
138+
SslProvider sslProvider = getSslProvider(config);
139+
sslContextBuilder.sslProvider(sslProvider);
140+
if (sslProvider == SslProvider.OPENSSL || sslProvider == SslProvider.OPENSSL_REFCNT) {
141+
sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty()));
142+
}
143+
if (config.getTristate(getSslTcnativeOcspEnabledProperty()).isTrue()) {
144+
sslContextBuilder.enableOcsp(true);
145+
} else if (config.getTristate(getSslTcnativeOcspEnabledProperty()).isFalse()) {
146+
sslContextBuilder.enableOcsp(false);
147+
}
127148
String[] enabledProtocols = getEnabledProtocols(config);
128149
if (enabledProtocols != null) {
129150
sslContextBuilder.protocols(enabledProtocols);
@@ -133,7 +154,6 @@ public SslContext createNettySslContextForServer(ZKConfig config, KeyManager key
133154
if (enabledCiphers != null) {
134155
sslContextBuilder.ciphers(enabledCiphers);
135156
}
136-
sslContextBuilder.sslProvider(getSslProvider(config));
137157

138158
SslContext sslContext1 = sslContextBuilder.build();
139159

@@ -191,15 +211,21 @@ private TrustManager getTrustManager(ZKConfig config) throws X509Exception.Trust
191211

192212
boolean sslCrlEnabled = config.getBoolean(getSslCrlEnabledProperty());
193213
boolean sslOcspEnabled = config.getBoolean(getSslOcspEnabledProperty());
214+
TriState sslRevocationEnabled = config.getTristate(getSslRevocationEnabledProperty());
215+
boolean disableLegacyRevocationLogic = config.getBoolean(getSslDisableLegacyRevocationLogicProperty());
194216
boolean sslServerHostnameVerificationEnabled = isServerHostnameVerificationEnabled(config);
195217
boolean sslClientHostnameVerificationEnabled = isClientHostnameVerificationEnabled(config);
196218

197219
if (trustStoreLocation.isEmpty()) {
198220
LOG.warn("{} not specified", getSslTruststoreLocationProperty());
221+
LOG.info("The following options are ignored: {}, {}, {}, {}.\n{} and {} will behave as in FIPS mode.",
222+
getSslCrlEnabledProperty(), getSslOcspEnabledProperty(), getSslRevocationEnabledProperty(),
223+
getSslDisableLegacyRevocationLogicProperty(), getSslHostnameVerificationEnabledProperty(),
224+
getSslClientHostnameVerificationEnabledProperty());
199225
return null;
200226
} else {
201227
return createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType,
202-
sslCrlEnabled, sslOcspEnabled, sslServerHostnameVerificationEnabled,
228+
sslCrlEnabled, sslOcspEnabled, sslRevocationEnabled, disableLegacyRevocationLogic, sslServerHostnameVerificationEnabled,
203229
sslClientHostnameVerificationEnabled, getFipsMode(config));
204230
}
205231
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.zookeeper.common;
20+
21+
/**
22+
* For storing configuration parameters where want to distinguish a default case
23+
* in addition to true and false.
24+
*/
25+
public enum TriState {
26+
True, False, Default;
27+
28+
public static TriState parse(String value) {
29+
if (value == null || value.equalsIgnoreCase("default")) {
30+
return TriState.Default;
31+
} else if (value.equalsIgnoreCase("true")) {
32+
return TriState.True;
33+
} else {
34+
return TriState.False;
35+
}
36+
}
37+
38+
public boolean isTrue() {
39+
return this == TriState.True;
40+
}
41+
42+
public boolean isFalse() {
43+
return this == TriState.False;
44+
}
45+
46+
public boolean isDefault() {
47+
return this == TriState.Default;
48+
}
49+
50+
public boolean isNotDefault() {
51+
return this != TriState.Default;
52+
}
53+
}

zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ public io.netty.handler.ssl.ClientAuth toNettyClientAuth() {
164164
private final String sslClientHostnameVerificationEnabledProperty = getConfigPrefix() + "clientHostnameVerification";
165165
private final String sslCrlEnabledProperty = getConfigPrefix() + "crl";
166166
private final String sslOcspEnabledProperty = getConfigPrefix() + "ocsp";
167+
private final String sslRevocationEnabledProperty = getConfigPrefix() + "revocationEnabled";
168+
private final String sslDisableLegacyRevocationLogic = getConfigPrefix() + "disableLegacyRevocationLogic";
169+
private final String sslTcnativeOcspEnabledProperty = getConfigPrefix() + ".tcnative.ocsp";
167170
private final String sslClientAuthProperty = getConfigPrefix() + "clientAuth";
168171
private final String sslHandshakeDetectionTimeoutMillisProperty = getConfigPrefix() + "handshakeDetectionTimeoutMillis";
169172

@@ -248,6 +251,18 @@ public String getSslOcspEnabledProperty() {
248251
return sslOcspEnabledProperty;
249252
}
250253

254+
public String getSslRevocationEnabledProperty() {
255+
return sslRevocationEnabledProperty;
256+
}
257+
258+
public String getSslTcnativeOcspEnabledProperty() {
259+
return sslTcnativeOcspEnabledProperty;
260+
}
261+
262+
public String getSslDisableLegacyRevocationLogicProperty() {
263+
return sslDisableLegacyRevocationLogic;
264+
}
265+
251266
public String getSslClientAuthProperty() {
252267
return sslClientAuthProperty;
253268
}
@@ -394,6 +409,8 @@ public SSLContextAndOptions createSSLContextAndOptionsFromConfig(ZKConfig config
394409

395410
boolean sslCrlEnabled = config.getBoolean(this.sslCrlEnabledProperty);
396411
boolean sslOcspEnabled = config.getBoolean(this.sslOcspEnabledProperty);
412+
TriState sslRevocationEnabled = config.getTristate(this.sslRevocationEnabledProperty);
413+
boolean disableLegacyRevocationLogic = config.getBoolean(this.sslDisableLegacyRevocationLogic);
397414
boolean sslServerHostnameVerificationEnabled = isServerHostnameVerificationEnabled(config);
398415
boolean sslClientHostnameVerificationEnabled = isClientHostnameVerificationEnabled(config);
399416
boolean fipsMode = getFipsMode(config);
@@ -404,7 +421,7 @@ public SSLContextAndOptions createSSLContextAndOptionsFromConfig(ZKConfig config
404421
try {
405422
trustManagers = new TrustManager[]{
406423
createTrustManager(trustStoreLocationProp, trustStorePasswordProp, trustStoreTypeProp, sslCrlEnabled,
407-
sslOcspEnabled, sslServerHostnameVerificationEnabled, sslClientHostnameVerificationEnabled,
424+
sslOcspEnabled, sslRevocationEnabled, disableLegacyRevocationLogic, sslServerHostnameVerificationEnabled, sslClientHostnameVerificationEnabled,
408425
fipsMode)};
409426
} catch (TrustManagerException trustManagerException) {
410427
throw new SSLContextException("Failed to create TrustManager", trustManagerException);
@@ -521,6 +538,9 @@ public static X509KeyManager createKeyManager(
521538
* @param crlEnabled enable CRL (certificate revocation list) checks.
522539
* @param ocspEnabled enable OCSP (online certificate status protocol)
523540
* checks.
541+
* @param revocationEnabled Enable certificate revocation checks
542+
* @param disableLegacyRevocationLogic if true certificate revocation will only be affected
543+
* by revocationEnabled and the JVM system properties
524544
* @param serverHostnameVerificationEnabled if true, verify hostnames of
525545
* remote servers that client
526546
* sockets created by this
@@ -539,6 +559,8 @@ public static X509TrustManager createTrustManager(
539559
String trustStoreTypeProp,
540560
boolean crlEnabled,
541561
boolean ocspEnabled,
562+
TriState revocationEnabled,
563+
boolean disableLegacyRevocationLogic,
542564
final boolean serverHostnameVerificationEnabled,
543565
final boolean clientHostnameVerificationEnabled,
544566
final boolean fipsMode) throws TrustManagerException {
@@ -549,13 +571,24 @@ public static X509TrustManager createTrustManager(
549571
KeyStore ts = loadTrustStore(trustStoreLocation, trustStorePassword, trustStoreTypeProp);
550572
PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector());
551573
if (crlEnabled || ocspEnabled) {
552-
pbParams.setRevocationEnabled(true);
574+
if (!disableLegacyRevocationLogic) {
575+
pbParams.setRevocationEnabled(true);
576+
}
553577
System.setProperty("com.sun.net.ssl.checkRevocation", "true");
554578
System.setProperty("com.sun.security.enableCRLDP", "true");
555579
if (ocspEnabled) {
556580
Security.setProperty("ocsp.enable", "true");
557581
}
558582
} else {
583+
if (!disableLegacyRevocationLogic) {
584+
pbParams.setRevocationEnabled(false);
585+
}
586+
}
587+
588+
// Setting revocationEnabled explicitly takes precedence
589+
if (revocationEnabled.isTrue()) {
590+
pbParams.setRevocationEnabled(true);
591+
} else if (revocationEnabled.isFalse()) {
559592
pbParams.setRevocationEnabled(false);
560593
}
561594

zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ private void putSSLProperties(X509Util x509Util) {
131131
properties.put(x509Util.getSslHostnameVerificationEnabledProperty(), System.getProperty(x509Util.getSslHostnameVerificationEnabledProperty()));
132132
properties.put(x509Util.getSslCrlEnabledProperty(), System.getProperty(x509Util.getSslCrlEnabledProperty()));
133133
properties.put(x509Util.getSslOcspEnabledProperty(), System.getProperty(x509Util.getSslOcspEnabledProperty()));
134+
properties.put(x509Util.getSslTcnativeOcspEnabledProperty(), System.getProperty(x509Util.getSslTcnativeOcspEnabledProperty()));
135+
properties.put(x509Util.getSslRevocationEnabledProperty(), System.getProperty(x509Util.getSslRevocationEnabledProperty()));
136+
properties.put(x509Util.getSslDisableLegacyRevocationLogicProperty(), System.getProperty(x509Util.getSslDisableLegacyRevocationLogicProperty()));
134137
properties.put(x509Util.getSslClientAuthProperty(), System.getProperty(x509Util.getSslClientAuthProperty()));
135138
properties.put(x509Util.getSslHandshakeDetectionTimeoutMillisProperty(), System.getProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty()));
136139
properties.put(x509Util.getFipsModeProperty(), System.getProperty(x509Util.getFipsModeProperty()));
@@ -284,4 +287,15 @@ public int getInt(String key, int defaultValue) {
284287
return defaultValue;
285288
}
286289

290+
/**
291+
* Returns {@code TriState.True} if and only if the property named by the argument
292+
* exists and is equal to the string {@code "true"}.
293+
* Returns {@code TriState.Default} if and only if the property named by the argument
294+
* does not exist or is equal to the string {@code "default"}.
295+
* Returns {@code TriState.False} otherwise.
296+
*/
297+
public TriState getTristate(String key) {
298+
String propertyValue = getProperty(key);
299+
return TriState.parse(propertyValue);
300+
}
287301
}

0 commit comments

Comments
 (0)