From 21ce1b6512aecdf1f74dd1570840243cbff90ea8 Mon Sep 17 00:00:00 2001 From: Jon Marius Venstad Date: Thu, 14 Dec 2023 18:56:52 +0100 Subject: Revert "Revert "Jonmv/zk 3.9.1 clients 2"" --- .../apps/clustercontroller/ClusterController.java | 2 +- .../ClusterControllerClusterConfigurer.java | 2 +- clustercontroller-core/pom.xml | 2 +- .../ai/vespa/reindexing/ReindexingMaintainer.java | 2 +- .../reindexing/http/ReindexingV1ApiHandler.java | 2 +- dependency-versions/pom.xml | 3 +- jdisc_core/abi-spec.json | 2 +- pom.xml | 1 + zkfacade/pom.xml | 33 +-- .../main/java/com/yahoo/vespa/curator/Curator.java | 2 +- .../yahoo/vespa/zookeeper/client/package-info.java | 4 + zookeeper-client-common/pom.xml | 13 ++ .../zookeeper/client/VespaSslContextProvider.java | 25 --- .../zookeeper/client/ZkClientConfigBuilder.java | 11 +- .../apache/zookeeper/common/ClientX509Util.java | 232 +++++++++++++++++++++ .../client/ZkClientConfigBuilderTest.java | 1 - zookeeper-command-line-client/pom.xml | 17 +- zookeeper-common/OWNERS | 1 + zookeeper-common/README.md | 4 + zookeeper-common/pom.xml | 51 +++++ .../tls/VespaZookeeperTlsContextUtils.java | 26 +++ .../zookeeper/ConfigServerZooKeeperServer.java | 2 + .../ReconfigurableVespaZooKeeperServer.java | 2 + .../zookeeper/VespaMtlsAuthenticationProvider.java | 17 +- .../vespa/zookeeper/VespaZooKeeperServerImpl.java | 2 + zookeeper-server/zookeeper-server-common/pom.xml | 6 + .../com/yahoo/vespa/zookeeper/Configurator.java | 36 +--- .../vespa/zookeeper/DummyVespaZooKeeperServer.java | 1 + .../com/yahoo/vespa/zookeeper/Reconfigurer.java | 1 + .../vespa/zookeeper/VespaZooKeeperServer.java | 24 --- .../com/yahoo/vespa/zookeeper/ZooKeeperRunner.java | 1 + .../com/yahoo/vespa/zookeeper/package-info.java | 5 - .../zookeeper/server/VespaZooKeeperServer.java | 24 +++ .../yahoo/vespa/zookeeper/server/package-info.java | 5 + .../yahoo/vespa/zookeeper/ConfiguratorTest.java | 18 +- .../yahoo/vespa/zookeeper/ReconfigurerTest.java | 1 + .../zookeeper/ConfigServerZooKeeperServer.java | 2 + .../ReconfigurableVespaZooKeeperServer.java | 2 + .../zookeeper/VespaMtlsAuthenticationProvider.java | 15 +- .../vespa/zookeeper/VespaZooKeeperServerImpl.java | 2 + .../apache/zookeeper/common/ClientX509Util.java | 116 ++++++----- 41 files changed, 499 insertions(+), 219 deletions(-) create mode 100644 zkfacade/src/main/java/com/yahoo/vespa/zookeeper/client/package-info.java delete mode 100644 zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/VespaSslContextProvider.java create mode 100644 zookeeper-client-common/src/main/java/org/apache/zookeeper/common/ClientX509Util.java create mode 100644 zookeeper-common/OWNERS create mode 100644 zookeeper-common/README.md create mode 100644 zookeeper-common/pom.xml create mode 100644 zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/tls/VespaZookeeperTlsContextUtils.java delete mode 100644 zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServer.java delete mode 100644 zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/package-info.java create mode 100644 zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/VespaZooKeeperServer.java create mode 100644 zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/package-info.java diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java index ed954512a26..7e0c6fe3f63 100644 --- a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java +++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java @@ -10,7 +10,7 @@ import com.yahoo.vespa.clustercontroller.core.FleetControllerOptions; import com.yahoo.vespa.clustercontroller.core.RemoteClusterControllerTaskScheduler; import com.yahoo.vespa.clustercontroller.core.restapiv2.ClusterControllerStateRestAPI; import com.yahoo.vespa.clustercontroller.core.status.StatusHandler; -import com.yahoo.vespa.zookeeper.VespaZooKeeperServer; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java index b87b3d4f5ea..5a2034f0372 100644 --- a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java +++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java @@ -11,7 +11,7 @@ import com.yahoo.vespa.config.content.FleetcontrollerConfig; import com.yahoo.cloud.config.SlobroksConfig; import com.yahoo.vespa.config.content.StorDistributionConfig; import com.yahoo.cloud.config.ZookeepersConfig; -import com.yahoo.vespa.zookeeper.VespaZooKeeperServer; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; import java.time.Duration; import java.util.Map; diff --git a/clustercontroller-core/pom.xml b/clustercontroller-core/pom.xml index 7f845a26c73..579e8dd91bb 100644 --- a/clustercontroller-core/pom.xml +++ b/clustercontroller-core/pom.xml @@ -100,7 +100,7 @@ com.yahoo.vespa zookeeper-client-common ${project.version} - compile + provided diff --git a/jdisc_core/abi-spec.json b/jdisc_core/abi-spec.json index 31594fed155..382ce72bd0a 100644 --- a/jdisc_core/abi-spec.json +++ b/jdisc_core/abi-spec.json @@ -604,8 +604,8 @@ "methods" : [ "public void ()", "public void (com.yahoo.jdisc.handler.ContentChannel)", - "public void addListener(java.lang.Runnable, java.util.concurrent.Executor)", "public void (com.yahoo.jdisc.handler.ResponseHandler)", + "public void addListener(java.lang.Runnable, java.util.concurrent.Executor)", "public com.yahoo.jdisc.handler.ContentChannel handleResponse(com.yahoo.jdisc.Response)", "public final boolean cancel(boolean)", "public final boolean isCancelled()" diff --git a/pom.xml b/pom.xml index 4e021fde2e3..52e0ad68215 100644 --- a/pom.xml +++ b/pom.xml @@ -139,6 +139,7 @@ zkfacade zookeeper-client-common zookeeper-command-line-client + zookeeper-common zookeeper-server diff --git a/zkfacade/pom.xml b/zkfacade/pom.xml index daaa7dfa14f..02be1006bc3 100644 --- a/zkfacade/pom.xml +++ b/zkfacade/pom.xml @@ -58,36 +58,9 @@ - org.apache.zookeeper - zookeeper - - - - org.slf4j - slf4j-api - - - - - - io.dropwizard.metrics - metrics-core - compile - - - org.slf4j - slf4j-api - - - - - org.xerial.snappy - snappy-java + com.yahoo.vespa + ${zookeeper.client.artifactId} + ${project.version} compile diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java index c372c69ad6b..169aee416e5 100644 --- a/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java +++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java @@ -8,7 +8,7 @@ import com.yahoo.concurrent.DaemonThreadFactory; import com.yahoo.path.Path; import com.yahoo.vespa.curator.recipes.CuratorCounter; import com.yahoo.vespa.defaults.Defaults; -import com.yahoo.vespa.zookeeper.VespaZooKeeperServer; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; import com.yahoo.vespa.zookeeper.client.ZkClientConfigBuilder; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; diff --git a/zkfacade/src/main/java/com/yahoo/vespa/zookeeper/client/package-info.java b/zkfacade/src/main/java/com/yahoo/vespa/zookeeper/client/package-info.java new file mode 100644 index 00000000000..7c81b651f30 --- /dev/null +++ b/zkfacade/src/main/java/com/yahoo/vespa/zookeeper/client/package-info.java @@ -0,0 +1,4 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +package com.yahoo.vespa.zookeeper.client; +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/zookeeper-client-common/pom.xml b/zookeeper-client-common/pom.xml index 12ff1517e53..ccfdbd9a429 100644 --- a/zookeeper-client-common/pom.xml +++ b/zookeeper-client-common/pom.xml @@ -20,6 +20,12 @@ ${project.version} provided + + com.yahoo.vespa + defaults + ${project.version} + provided + org.apache.zookeeper zookeeper @@ -27,6 +33,13 @@ + + com.yahoo.vespa + zookeeper-common + ${project.version} + compile + + org.junit.jupiter diff --git a/zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/VespaSslContextProvider.java b/zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/VespaSslContextProvider.java deleted file mode 100644 index 9cc71eab96e..00000000000 --- a/zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/VespaSslContextProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.zookeeper.client; - -import com.yahoo.security.tls.TransportSecurityUtils; - -import javax.net.ssl.SSLContext; -import java.util.function.Supplier; - -/** - * Provider for Vespa {@link SSLContext} instance to Zookeeper + misc utility methods for providing Vespa TLS specific ZK configuration. - * - * @author bjorncs - */ -public class VespaSslContextProvider implements Supplier { - - private static final SSLContext sslContext = TransportSecurityUtils.getSystemTlsContext() - .map(tc -> tc.sslContext().context()).orElse(null); - - @Override - public SSLContext get() { - if (sslContext == null) throw new IllegalStateException("Vespa TLS is not enabled"); - return sslContext; - } - -} diff --git a/zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilder.java b/zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilder.java index 5c969454d11..1aaae08d4ef 100644 --- a/zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilder.java +++ b/zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilder.java @@ -1,9 +1,8 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.zookeeper.client; -import com.yahoo.security.tls.MixedMode; import com.yahoo.security.tls.TlsContext; -import com.yahoo.security.tls.TransportSecurityUtils; +import com.yahoo.vespa.zookeeper.tls.VespaZookeeperTlsContextUtils; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; @@ -14,7 +13,6 @@ import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; /** @@ -31,7 +29,7 @@ public class ZkClientConfigBuilder { public static final String SSL_CLIENTAUTH_PROPERTY = "zookeeper.ssl.clientAuth"; public static final String CLIENT_CONNECTION_SOCKET = "zookeeper.clientCnxnSocket"; - private static final TlsContext defaultTlsContext = getTlsContext().orElse(null); + private static final TlsContext defaultTlsContext = VespaZookeeperTlsContextUtils.tlsContext().orElse(null); private final TlsContext tlsContext; @@ -71,7 +69,6 @@ public class ZkClientConfigBuilder { builder.put(CLIENT_SECURE_PROPERTY, Boolean.toString(tlsContext != null)); builder.put(CLIENT_CONNECTION_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); if (tlsContext != null) { - builder.put(SSL_CONTEXT_SUPPLIER_CLASS_PROPERTY, VespaSslContextProvider.class.getName()); String protocolsConfigValue = Arrays.stream(tlsContext.parameters().getProtocols()).sorted().collect(Collectors.joining(",")); builder.put(SSL_ENABLED_PROTOCOLS_PROPERTY, protocolsConfigValue); String ciphersConfigValue = Arrays.stream(tlsContext.parameters().getCipherSuites()).sorted().collect(Collectors.joining(",")); @@ -81,8 +78,4 @@ public class ZkClientConfigBuilder { return Map.copyOf(builder); } - private static Optional getTlsContext() { - if (TransportSecurityUtils.getInsecureMixedMode() == MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER) return Optional.empty(); - return TransportSecurityUtils.getSystemTlsContext(); - } } diff --git a/zookeeper-client-common/src/main/java/org/apache/zookeeper/common/ClientX509Util.java b/zookeeper-client-common/src/main/java/org/apache/zookeeper/common/ClientX509Util.java new file mode 100644 index 00000000000..ab528ca7cbb --- /dev/null +++ b/zookeeper-client-common/src/main/java/org/apache/zookeeper/common/ClientX509Util.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zookeeper.common; + +import com.yahoo.security.tls.MixedMode; +import com.yahoo.security.tls.TransportSecurityUtils; +import com.yahoo.vespa.zookeeper.tls.VespaZookeeperTlsContextUtils; +import io.netty.handler.ssl.DelegatingSslContext; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslProvider; +import java.util.Arrays; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * X509 utilities specific for client-server communication framework. + *

+ * Modified to use Vespa's TLS context, whenever it is available, instead of the file-based key and trust stores of ZK 3.9. + * Based on https://github.com/apache/zookeeper/blob/branch-3.9/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java + * + * @author jonmv + */ +public class ClientX509Util extends X509Util { + + private static final Logger LOG = LoggerFactory.getLogger(ClientX509Util.class); + + private final String sslAuthProviderProperty = getConfigPrefix() + "authProvider"; + private final String sslProviderProperty = getConfigPrefix() + "sslProvider"; + + @Override + protected String getConfigPrefix() { + return "zookeeper.ssl."; + } + + @Override + protected boolean shouldVerifyClientHostname() { + return false; + } + + public String getSslAuthProviderProperty() { + return sslAuthProviderProperty; + } + + public String getSslProviderProperty() { + return sslProviderProperty; + } + + public SslContext createNettySslContextForClient(ZKConfig config) + throws X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); + KeyManager km; + TrustManager tm; + if ( TransportSecurityUtils.getInsecureMixedMode() != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER + && VespaZookeeperTlsContextUtils.tlsContext().isPresent()) { + km = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().keyManager(); + tm = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().trustManager(); + } + else { + String keyStoreLocation = config.getProperty(getSslKeystoreLocationProperty(), ""); + String keyStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslKeystorePasswdProperty(), + getSslKeystorePasswdPathProperty()); + String keyStoreType = config.getProperty(getSslKeystoreTypeProperty()); + + if (keyStoreLocation.isEmpty()) { + LOG.warn("{} not specified", getSslKeystoreLocationProperty()); + km = null; + } + else { + km = createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType); + } + + tm = getTrustManager(config); + } + + if (km != null) { + sslContextBuilder.keyManager(km); + } + if (tm != null) { + sslContextBuilder.trustManager(tm); + } + + sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty())); + sslContextBuilder.protocols(getEnabledProtocols(config)); + Iterable enabledCiphers = getCipherSuites(config); + if (enabledCiphers != null) { + sslContextBuilder.ciphers(enabledCiphers); + } + sslContextBuilder.sslProvider(getSslProvider(config)); + + SslContext sslContext1 = sslContextBuilder.build(); + + if (getFipsMode(config) && isServerHostnameVerificationEnabled(config)) { + return addHostnameVerification(sslContext1, "Server"); + } else { + return sslContext1; + } + } + + public SslContext createNettySslContextForServer(ZKConfig config) + throws X509Exception.SSLContextException, X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { + KeyManager km; + TrustManager tm; + if (VespaZookeeperTlsContextUtils.tlsContext().isPresent()) { + km = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().keyManager(); + tm = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().trustManager(); + } + else { + String keyStoreLocation = config.getProperty(getSslKeystoreLocationProperty(), ""); + String keyStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslKeystorePasswdProperty(), + getSslKeystorePasswdPathProperty()); + String keyStoreType = config.getProperty(getSslKeystoreTypeProperty()); + + if (keyStoreLocation.isEmpty()) { + throw new X509Exception.SSLContextException( + "Keystore is required for SSL server: " + getSslKeystoreLocationProperty()); + } + km = createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType); + tm = getTrustManager(config); + } + return createNettySslContextForServer(config, km, tm); + } + + public SslContext createNettySslContextForServer(ZKConfig config, KeyManager keyManager, TrustManager trustManager) throws SSLException { + SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(keyManager); + + if (trustManager != null) { + sslContextBuilder.trustManager(trustManager); + } + + sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty())); + sslContextBuilder.protocols(getEnabledProtocols(config)); + sslContextBuilder.clientAuth(getClientAuth(config).toNettyClientAuth()); + Iterable enabledCiphers = getCipherSuites(config); + if (enabledCiphers != null) { + sslContextBuilder.ciphers(enabledCiphers); + } + sslContextBuilder.sslProvider(getSslProvider(config)); + + SslContext sslContext1 = sslContextBuilder.build(); + + if (getFipsMode(config) && isClientHostnameVerificationEnabled(config)) { + return addHostnameVerification(sslContext1, "Client"); + } else { + return sslContext1; + } + } + + private SslContext addHostnameVerification(SslContext sslContext, String clientOrServer) { + return new DelegatingSslContext(sslContext) { + @Override + protected void initEngine(SSLEngine sslEngine) { + SSLParameters sslParameters = sslEngine.getSSLParameters(); + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + sslEngine.setSSLParameters(sslParameters); + if (LOG.isDebugEnabled()) { + LOG.debug("{} hostname verification: enabled HTTPS style endpoint identification algorithm", clientOrServer); + } + } + }; + } + + private String[] getEnabledProtocols(final ZKConfig config) { + String enabledProtocolsInput = config.getProperty(getSslEnabledProtocolsProperty()); + if (enabledProtocolsInput == null) { + return new String[]{ config.getProperty(getSslProtocolProperty(), DEFAULT_PROTOCOL) }; + } + return enabledProtocolsInput.split(","); + } + + private X509Util.ClientAuth getClientAuth(final ZKConfig config) { + return X509Util.ClientAuth.fromPropertyValue(config.getProperty(getSslClientAuthProperty())); + } + + private Iterable getCipherSuites(final ZKConfig config) { + String cipherSuitesInput = config.getProperty(getSslCipherSuitesProperty()); + if (cipherSuitesInput == null) { + if (getSslProvider(config) != SslProvider.JDK) { + return null; + } + return Arrays.asList(X509Util.getDefaultCipherSuites()); + } else { + return Arrays.asList(cipherSuitesInput.split(",")); + } + } + + public SslProvider getSslProvider(ZKConfig config) { + return SslProvider.valueOf(config.getProperty(getSslProviderProperty(), "JDK")); + } + + private TrustManager getTrustManager(ZKConfig config) throws X509Exception.TrustManagerException { + String trustStoreLocation = config.getProperty(getSslTruststoreLocationProperty(), ""); + String trustStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslTruststorePasswdProperty(), + getSslTruststorePasswdPathProperty()); + String trustStoreType = config.getProperty(getSslTruststoreTypeProperty()); + + boolean sslCrlEnabled = config.getBoolean(getSslCrlEnabledProperty()); + boolean sslOcspEnabled = config.getBoolean(getSslOcspEnabledProperty()); + boolean sslServerHostnameVerificationEnabled = isServerHostnameVerificationEnabled(config); + boolean sslClientHostnameVerificationEnabled = isClientHostnameVerificationEnabled(config); + + if (trustStoreLocation.isEmpty()) { + LOG.warn("{} not specified", getSslTruststoreLocationProperty()); + return null; + } else { + return createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, + sslCrlEnabled, sslOcspEnabled, sslServerHostnameVerificationEnabled, + sslClientHostnameVerificationEnabled, getFipsMode(config)); + } + } +} diff --git a/zookeeper-client-common/src/test/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilderTest.java b/zookeeper-client-common/src/test/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilderTest.java index 56bfe8381c2..697d76dc53f 100644 --- a/zookeeper-client-common/src/test/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilderTest.java +++ b/zookeeper-client-common/src/test/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilderTest.java @@ -39,7 +39,6 @@ public class ZkClientConfigBuilderTest { ZKClientConfig config = builder.toConfig(); assertEquals("true", config.getProperty(CLIENT_SECURE_PROPERTY)); assertEquals("org.apache.zookeeper.ClientCnxnSocketNetty", config.getProperty(CLIENT_CONNECTION_SOCKET)); - assertEquals(com.yahoo.vespa.zookeeper.client.VespaSslContextProvider.class.getName(), config.getProperty(SSL_CONTEXT_SUPPLIER_CLASS_PROPERTY)); assertEquals("TLSv1.3", config.getProperty(SSL_ENABLED_PROTOCOLS_PROPERTY)); assertEquals("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", config.getProperty(SSL_ENABLED_CIPHERSUITES_PROPERTY)); assertEquals("NEED", config.getProperty(SSL_CLIENTAUTH_PROPERTY)); diff --git a/zookeeper-command-line-client/pom.xml b/zookeeper-command-line-client/pom.xml index dae8fdc671b..64208e283bf 100644 --- a/zookeeper-command-line-client/pom.xml +++ b/zookeeper-command-line-client/pom.xml @@ -12,8 +12,10 @@ 8-SNAPSHOT - org.apache.zookeeper - zookeeper + com.yahoo.vespa + ${zookeeper.client.artifactId} + ${project.version} + compile com.yahoo.vespa @@ -21,6 +23,12 @@ ${project.version} compile + + com.yahoo.vespa + defaults + ${project.version} + compile + com.yahoo.vespa security-utils @@ -49,11 +57,6 @@ log4j-over-slf4j compile - - org.xerial.snappy - snappy-java - compile - com.github.spotbugs diff --git a/zookeeper-common/OWNERS b/zookeeper-common/OWNERS new file mode 100644 index 00000000000..d0a102ecbf4 --- /dev/null +++ b/zookeeper-common/OWNERS @@ -0,0 +1 @@ +jonmv diff --git a/zookeeper-common/README.md b/zookeeper-common/README.md new file mode 100644 index 00000000000..f0c7cee342d --- /dev/null +++ b/zookeeper-common/README.md @@ -0,0 +1,4 @@ + +# zookeeper-common + +Shared configuration logic for ZooKeeper diff --git a/zookeeper-common/pom.xml b/zookeeper-common/pom.xml new file mode 100644 index 00000000000..2c8ed8fe476 --- /dev/null +++ b/zookeeper-common/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + + com.yahoo.vespa + parent + 8-SNAPSHOT + ../parent/pom.xml + + zookeeper-common + jar + 8-SNAPSHOT + + + + + com.yahoo.vespa + security-utils + ${project.version} + provided + + + + com.yahoo.vespa + defaults + ${project.version} + provided + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + diff --git a/zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/tls/VespaZookeeperTlsContextUtils.java b/zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/tls/VespaZookeeperTlsContextUtils.java new file mode 100644 index 00000000000..78de6c61e17 --- /dev/null +++ b/zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/tls/VespaZookeeperTlsContextUtils.java @@ -0,0 +1,26 @@ +package com.yahoo.vespa.zookeeper.tls; + +import com.yahoo.security.tls.ConfigFileBasedTlsContext; +import com.yahoo.security.tls.TlsContext; +import com.yahoo.security.tls.TransportSecurityUtils; +import com.yahoo.vespa.defaults.Defaults; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +/** + * @author jonmv + */ +public class VespaZookeeperTlsContextUtils { + + private static final Path ZOOKEEPER_TLS_CONFIG_FILE = Path.of(Defaults.getDefaults().underVespaHome("var/zookeeper/conf/tls.conf.json")); + private static final TlsContext tlsContext = Files.exists(ZOOKEEPER_TLS_CONFIG_FILE) + ? new ConfigFileBasedTlsContext(ZOOKEEPER_TLS_CONFIG_FILE, TransportSecurityUtils.getInsecureAuthorizationMode()) + : TransportSecurityUtils.getSystemTlsContext().orElse(null); + + public static Optional tlsContext() { + return Optional.ofNullable(tlsContext); + } + +} diff --git a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java index d986f02d89a..a7cd14c415f 100644 --- a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java +++ b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java @@ -4,6 +4,8 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; + import java.nio.file.Path; /** diff --git a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java index 1b469beb1b8..d869cbb6938 100644 --- a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java +++ b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java @@ -5,6 +5,8 @@ import ai.vespa.validation.Validation; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; + import java.nio.file.Path; import java.time.Duration; diff --git a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java index 68f7459530e..90554910293 100644 --- a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java +++ b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java @@ -2,19 +2,24 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.security.X509SslContext; +import com.yahoo.security.tls.TlsContext; +import com.yahoo.security.tls.TransportSecurityUtils; import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.common.ClientX509Util; +import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.auth.AuthenticationProvider; import org.apache.zookeeper.server.auth.X509AuthenticationProvider; +import javax.net.ssl.KeyManager; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; import java.util.logging.Logger; /** - * A {@link AuthenticationProvider} to be used in combination with Vespa mTLS + * A {@link AuthenticationProvider} to be used in combination with Vespa mTLS. * * @author bjorncs */ @@ -23,15 +28,7 @@ public class VespaMtlsAuthenticationProvider extends X509AuthenticationProvider private static final Logger log = Logger.getLogger(VespaMtlsAuthenticationProvider.class.getName()); public VespaMtlsAuthenticationProvider() { - super(trustManager(), keyManager()); - } - - private static X509KeyManager keyManager() { - return new VespaSslContextProvider().tlsContext().map(X509SslContext::keyManager).orElse(null); - } - - private static X509TrustManager trustManager() { - return new VespaSslContextProvider().tlsContext().map(X509SslContext::trustManager).orElse(null); + super(null, null); } @Override diff --git a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java index 4a7f85d6985..4f93eb0efa5 100644 --- a/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java +++ b/zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java @@ -5,6 +5,8 @@ import ai.vespa.validation.Validation; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; + import java.nio.file.Path; import java.time.Duration; diff --git a/zookeeper-server/zookeeper-server-common/pom.xml b/zookeeper-server/zookeeper-server-common/pom.xml index 86734ec6c56..2238f6ad086 100644 --- a/zookeeper-server/zookeeper-server-common/pom.xml +++ b/zookeeper-server/zookeeper-server-common/pom.xml @@ -12,6 +12,12 @@ container-plugin 8-SNAPSHOT + + com.yahoo.vespa + zookeeper-common + ${project.version} + compile + junit junit diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java index 727e369885e..14288bab710 100644 --- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java +++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java @@ -3,10 +3,10 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.cloud.config.ZookeeperServerConfig.Server; -import com.yahoo.security.tls.ConfigFileBasedTlsContext; import com.yahoo.security.tls.MixedMode; import com.yahoo.security.tls.TlsContext; import com.yahoo.security.tls.TransportSecurityUtils; +import com.yahoo.vespa.zookeeper.tls.VespaZookeeperTlsContextUtils; import java.io.FileWriter; import java.io.IOException; @@ -47,9 +47,8 @@ public class Configurator { // Doc says that it is max size of data in a zookeeper node, but it goes for everything that // needs to be serialized, see https://issues.apache.org/jira/browse/ZOOKEEPER-1162 for details System.setProperty(ZOOKEEPER_JUTE_MAX_BUFFER, Integer.valueOf(zookeeperServerConfig.juteMaxBuffer()).toString()); - // Need to set these as a system properties instead of config, config does not work + // Need to set this as a system properties instead of config, config does not work System.setProperty("zookeeper.authProvider.x509", "com.yahoo.vespa.zookeeper.VespaMtlsAuthenticationProvider"); - System.setProperty("zookeeper.ssl.authProvider", "x509"); // Need to set this as a system property, otherwise it will be parsed for _every_ packet and an exception will be thrown (and handled) System.setProperty("zookeeper.globalOutstandingLimit", "1000"); System.setProperty("zookeeper.snapshot.compression.method", zookeeperServerConfig.snapshotMethod()); @@ -60,13 +59,9 @@ public class Configurator { } void writeConfigToDisk() { - VespaTlsConfig config; - String cfgFile = zookeeperServerConfig.vespaTlsConfigFile(); - if (cfgFile.isBlank()) { - config = VespaTlsConfig.fromSystem(); - } else { - config = VespaTlsConfig.fromConfig(Paths.get(cfgFile)); - } + VespaTlsConfig config = VespaZookeeperTlsContextUtils.tlsContext() + .map(ctx -> new VespaTlsConfig(ctx, TransportSecurityUtils.getInsecureMixedMode())) + .orElse(VespaTlsConfig.tlsDisabled()); writeConfigToDisk(config); } @@ -90,7 +85,7 @@ public class Configurator { } } - private String transformConfigToString(ZookeeperServerConfig config, VespaTlsConfig vespaTlsConfig, Map dynamicConfig) { + private static String transformConfigToString(ZookeeperServerConfig config, VespaTlsConfig vespaTlsConfig, Map dynamicConfig) { Map configEntries = new LinkedHashMap<>(); configEntries.put("tickTime", Integer.toString(config.tickTime())); configEntries.put("initLimit", Integer.toString(config.initLimit())); @@ -118,7 +113,7 @@ public class Configurator { return transformConfigToString(configEntries); } - void addServerSpecs(Map configEntries, ZookeeperServerConfig config, Map dynamicConfig) { + static void addServerSpecs(Map configEntries, ZookeeperServerConfig config, Map dynamicConfig) { int myIndex = ensureThisServerIsRepresented(config.myid(), config.server()); // If dynamic config refers to servers that are not in the current config, we must ignore it. @@ -210,7 +205,7 @@ public class Configurator { .toList(); } - Path makeAbsolutePath(String filename) { + static Path makeAbsolutePath(String filename) { Path path = Paths.get(filename); return path.isAbsolute() ? path : Paths.get(getDefaults().underVespaHome(filename)); } @@ -220,8 +215,6 @@ public class Configurator { default void appendSharedTlsConfig(Map configEntries, VespaTlsConfig vespaTlsConfig) { vespaTlsConfig.context().ifPresent(ctx -> { - VespaSslContextProvider.set(ctx); - configEntries.put(configFieldPrefix() + ".context.supplier.class", VespaSslContextProvider.class.getName()); String enabledCiphers = Arrays.stream(ctx.parameters().getCipherSuites()).sorted().collect(Collectors.joining(",")); configEntries.put(configFieldPrefix() + ".ciphersuites", enabledCiphers); String enabledProtocols = Arrays.stream(ctx.parameters().getProtocols()).sorted().collect(Collectors.joining(",")); @@ -276,19 +269,6 @@ public class Configurator { this.mixedMode = mixedMode; } - static VespaTlsConfig fromSystem() { - return new VespaTlsConfig( - TransportSecurityUtils.getSystemTlsContext().orElse(null), - TransportSecurityUtils.getInsecureMixedMode()); - } - - static VespaTlsConfig fromConfig(Path file) { - return new VespaTlsConfig( - new ConfigFileBasedTlsContext(file, TransportSecurityUtils.getInsecureAuthorizationMode()), - TransportSecurityUtils.getInsecureMixedMode()); - } - - static VespaTlsConfig tlsDisabled() { return new VespaTlsConfig(null, MixedMode.defaultValue()); } boolean tlsEnabled() { return context != null; } diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/DummyVespaZooKeeperServer.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/DummyVespaZooKeeperServer.java index cc3d5117241..f99d4cb6881 100644 --- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/DummyVespaZooKeeperServer.java +++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/DummyVespaZooKeeperServer.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.component.annotation.Inject; import com.yahoo.component.AbstractComponent; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; import java.nio.file.Path; diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java index f2886be93d7..201bb7af272 100644 --- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java +++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java @@ -5,6 +5,7 @@ import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; import com.yahoo.protect.Process; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; import com.yahoo.yolean.Exceptions; import java.time.Duration; diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServer.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServer.java deleted file mode 100644 index ef6083ae5f7..00000000000 --- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServer.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.zookeeper; - -import java.nio.file.Path; - -/** - * Interface for a component that starts/stops a ZooKeeper server. Implementations should make sure - * that the server is up and accepts connection (typically by returning from constructor only after - * writing a node to ZooKeeper successfully). - * - * @author hmusum - */ -public interface VespaZooKeeperServer { - - /** Shut down the server. Blocks until shutdown has completed */ - void shutdown(); - - /** Start the server with the given config file */ - void start(Path configFilePath); - - /** Whether this server support dynamic reconfiguration */ - boolean reconfigurable(); - -} diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java index eaae3c74d11..9c18dde3380 100644 --- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java +++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.concurrent.DaemonThreadFactory; import com.yahoo.protect.Process; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; import com.yahoo.yolean.Exceptions; import java.nio.file.Files; diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/package-info.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/package-info.java deleted file mode 100644 index f43f095d66d..00000000000 --- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage -package com.yahoo.vespa.zookeeper; - -import com.yahoo.osgi.annotation.ExportPackage; diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/VespaZooKeeperServer.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/VespaZooKeeperServer.java new file mode 100644 index 00000000000..0eddf5175d4 --- /dev/null +++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/VespaZooKeeperServer.java @@ -0,0 +1,24 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.zookeeper.server; + +import java.nio.file.Path; + +/** + * Interface for a component that starts/stops a ZooKeeper server. Implementations should make sure + * that the server is up and accepts connection (typically by returning from constructor only after + * writing a node to ZooKeeper successfully). + * + * @author hmusum + */ +public interface VespaZooKeeperServer { + + /** Shut down the server. Blocks until shutdown has completed */ + void shutdown(); + + /** Start the server with the given config file */ + void start(Path configFilePath); + + /** Whether this server support dynamic reconfiguration */ + boolean reconfigurable(); + +} diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/package-info.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/package-info.java new file mode 100644 index 00000000000..fd6967ffbe4 --- /dev/null +++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/server/package-info.java @@ -0,0 +1,5 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +package com.yahoo.vespa.zookeeper.server; + +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ConfiguratorTest.java b/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ConfiguratorTest.java index 3cf1d07be65..2a4c8065346 100644 --- a/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ConfiguratorTest.java +++ b/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ConfiguratorTest.java @@ -224,17 +224,19 @@ public class ConfiguratorTest { } private String tlsQuorumConfig() { - return "ssl.quorum.context.supplier.class=com.yahoo.vespa.zookeeper.VespaSslContextProvider\n" + - "ssl.quorum.ciphersuites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n" + - "ssl.quorum.enabledProtocols=TLSv1.2,TLSv1.3\n" + - "ssl.quorum.clientAuth=NEED\n"; + return """ + ssl.quorum.ciphersuites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + ssl.quorum.enabledProtocols=TLSv1.2,TLSv1.3 + ssl.quorum.clientAuth=NEED + """; } private String tlsClientServerConfig() { - return "ssl.context.supplier.class=com.yahoo.vespa.zookeeper.VespaSslContextProvider\n" + - "ssl.ciphersuites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n" + - "ssl.enabledProtocols=TLSv1.2,TLSv1.3\n" + - "ssl.clientAuth=NEED\n"; + return """ + ssl.ciphersuites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + ssl.enabledProtocols=TLSv1.2,TLSv1.3 + ssl.clientAuth=NEED + """; } private void validateConfigFileMultipleHosts(File cfgFile, boolean hosted) { diff --git a/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ReconfigurerTest.java b/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ReconfigurerTest.java index b21f907ec5d..ebf1194fdfe 100644 --- a/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ReconfigurerTest.java +++ b/zookeeper-server/zookeeper-server-common/src/test/java/com/yahoo/vespa/zookeeper/ReconfigurerTest.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.net.HostName; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; import org.junit.After; import org.junit.Before; import org.junit.Rule; diff --git a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java index d986f02d89a..a7cd14c415f 100644 --- a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java +++ b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ConfigServerZooKeeperServer.java @@ -4,6 +4,8 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; + import java.nio.file.Path; /** diff --git a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java index 1b469beb1b8..d869cbb6938 100644 --- a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java +++ b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java @@ -5,6 +5,8 @@ import ai.vespa.validation.Validation; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; + import java.nio.file.Path; import java.time.Duration; diff --git a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java index 100de4894ae..90554910293 100644 --- a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java +++ b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java @@ -2,7 +2,10 @@ package com.yahoo.vespa.zookeeper; import com.yahoo.security.X509SslContext; +import com.yahoo.security.tls.TlsContext; +import com.yahoo.security.tls.TransportSecurityUtils; import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; @@ -16,7 +19,7 @@ import java.security.cert.X509Certificate; import java.util.logging.Logger; /** - * A {@link AuthenticationProvider} to be used in combination with Vespa mTLS + * A {@link AuthenticationProvider} to be used in combination with Vespa mTLS. * * @author bjorncs */ @@ -25,15 +28,7 @@ public class VespaMtlsAuthenticationProvider extends X509AuthenticationProvider private static final Logger log = Logger.getLogger(VespaMtlsAuthenticationProvider.class.getName()); public VespaMtlsAuthenticationProvider() { - super(trustManager(), keyManager()); - } - - private static X509KeyManager keyManager() { - return new VespaSslContextProvider().tlsContext().map(X509SslContext::keyManager).orElse(null); - } - - private static X509TrustManager trustManager() { - return new VespaSslContextProvider().tlsContext().map(X509SslContext::trustManager).orElse(null); + super(null, null); } @Override diff --git a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java index 4a7f85d6985..4f93eb0efa5 100644 --- a/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java +++ b/zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java @@ -5,6 +5,8 @@ import ai.vespa.validation.Validation; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; +import com.yahoo.vespa.zookeeper.server.VespaZooKeeperServer; + import java.nio.file.Path; import java.time.Duration; diff --git a/zookeeper-server/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java b/zookeeper-server/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java index c0034a4723f..83cfaf11a92 100644 --- a/zookeeper-server/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java +++ b/zookeeper-server/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java @@ -18,6 +18,7 @@ package org.apache.zookeeper.common; +import com.yahoo.vespa.zookeeper.tls.VespaZookeeperTlsContextUtils; import io.netty.handler.ssl.DelegatingSslContext; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; @@ -28,21 +29,16 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLParameters; import javax.net.ssl.TrustManager; - -import org.apache.zookeeper.common.X509Exception.KeyManagerException; -import org.apache.zookeeper.common.X509Exception.SSLContextException; -import org.apache.zookeeper.server.auth.ProviderRegistry; -import org.apache.zookeeper.server.auth.X509AuthenticationProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * - * NOTE: Overridden because ZK 3.9 completely broke the SSL setup APIs; for clients, key and trust stores are - * now mandatory, unlike for servers, where it's still possible to provide a custom authProvider. This patch fixes that. - * Based on https://github.com/apache/zookeeper/blob/branch-3.9/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java - *

* X509 utilities specific for client-server communication framework. + *

+ * Modified to use Vespa's TLS context, whenever it is available, instead of the file-based key and trust stores of ZK 3.9. + * Based on https://github.com/apache/zookeeper/blob/branch-3.9/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java + * + * @author jonmv */ public class ClientX509Util extends X509Util { @@ -70,37 +66,31 @@ public class ClientX509Util extends X509Util { } public SslContext createNettySslContextForClient(ZKConfig config) - throws X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { - + throws X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); KeyManager km; TrustManager tm; - String authProviderProp = System.getProperty(getSslAuthProviderProperty()); - if (authProviderProp == null) { + if (VespaZookeeperTlsContextUtils.tlsContext().isPresent()) { + km = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().keyManager(); + tm = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().trustManager(); + } + else { String keyStoreLocation = config.getProperty(getSslKeystoreLocationProperty(), ""); String keyStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslKeystorePasswdProperty(), getSslKeystorePasswdPathProperty()); String keyStoreType = config.getProperty(getSslKeystoreTypeProperty()); + if (keyStoreLocation.isEmpty()) { LOG.warn("{} not specified", getSslKeystoreLocationProperty()); km = null; - } else { + } + else { km = createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType); } - tm = getTrustManager(config); - } else { - X509AuthenticationProvider authProvider = (X509AuthenticationProvider) ProviderRegistry.getProvider( - System.getProperty(getSslAuthProviderProperty(), "x509")); - if (authProvider == null) { - LOG.error("Auth provider not found: {}", authProviderProp); - throw new SSLException("Could not create SSLContext with specified auth provider: " + authProviderProp); - } - LOG.info("Using auth provider for client: {}", authProviderProp); - km = authProvider.getKeyManager(); - tm = authProvider.getTrustManager(); + tm = getTrustManager(config); } - SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); if (km != null) { sslContextBuilder.keyManager(km); } @@ -108,36 +98,54 @@ public class ClientX509Util extends X509Util { sslContextBuilder.trustManager(tm); } - return createNettySslContext(config, sslContextBuilder, "Server"); + sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty())); + sslContextBuilder.protocols(getEnabledProtocols(config)); + Iterable enabledCiphers = getCipherSuites(config); + if (enabledCiphers != null) { + sslContextBuilder.ciphers(enabledCiphers); + } + sslContextBuilder.sslProvider(getSslProvider(config)); + + SslContext sslContext1 = sslContextBuilder.build(); + + if (getFipsMode(config) && isServerHostnameVerificationEnabled(config)) { + return addHostnameVerification(sslContext1, "Server"); + } else { + return sslContext1; + } } public SslContext createNettySslContextForServer(ZKConfig config) - throws X509Exception.SSLContextException, X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { - String keyStoreLocation = config.getProperty(getSslKeystoreLocationProperty(), ""); - String keyStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslKeystorePasswdProperty(), - getSslKeystorePasswdPathProperty()); - String keyStoreType = config.getProperty(getSslKeystoreTypeProperty()); - - if (keyStoreLocation.isEmpty()) { - throw new X509Exception.SSLContextException( - "Keystore is required for SSL server: " + getSslKeystoreLocationProperty()); + throws X509Exception.SSLContextException, X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { + KeyManager km; + TrustManager tm; + if (VespaZookeeperTlsContextUtils.tlsContext().isPresent()) { + km = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().keyManager(); + tm = VespaZookeeperTlsContextUtils.tlsContext().get().sslContext().trustManager(); } + else { + String keyStoreLocation = config.getProperty(getSslKeystoreLocationProperty(), ""); + String keyStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslKeystorePasswdProperty(), + getSslKeystorePasswdPathProperty()); + String keyStoreType = config.getProperty(getSslKeystoreTypeProperty()); - KeyManager km = createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType); - TrustManager trustManager = getTrustManager(config); - - return createNettySslContextForServer(config, km, trustManager); + if (keyStoreLocation.isEmpty()) { + throw new X509Exception.SSLContextException( + "Keystore is required for SSL server: " + getSslKeystoreLocationProperty()); + } + km = createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType); + tm = getTrustManager(config); + } + return createNettySslContextForServer(config, km, tm); } - public SslContext createNettySslContextForServer(ZKConfig config, KeyManager km, TrustManager tm) throws SSLException { - SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(km); - if (tm != null) { - sslContextBuilder.trustManager(tm); + public SslContext createNettySslContextForServer(ZKConfig config, KeyManager keyManager, TrustManager trustManager) throws SSLException { + SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(keyManager); + + if (trustManager != null) { + sslContextBuilder.trustManager(trustManager); } - return createNettySslContext(config, sslContextBuilder, "Client"); - } - SslContext createNettySslContext(ZKConfig config, SslContextBuilder sslContextBuilder, String clientOrServer) throws SSLException { sslContextBuilder.enableOcsp(config.getBoolean(getSslOcspEnabledProperty())); sslContextBuilder.protocols(getEnabledProtocols(config)); sslContextBuilder.clientAuth(getClientAuth(config).toNettyClientAuth()); @@ -147,12 +155,12 @@ public class ClientX509Util extends X509Util { } sslContextBuilder.sslProvider(getSslProvider(config)); - SslContext sslContext = sslContextBuilder.build(); + SslContext sslContext1 = sslContextBuilder.build(); if (getFipsMode(config) && isClientHostnameVerificationEnabled(config)) { - return addHostnameVerification(sslContext, clientOrServer); + return addHostnameVerification(sslContext1, "Client"); } else { - return sslContext; + return sslContext1; } } @@ -201,7 +209,7 @@ public class ClientX509Util extends X509Util { private TrustManager getTrustManager(ZKConfig config) throws X509Exception.TrustManagerException { String trustStoreLocation = config.getProperty(getSslTruststoreLocationProperty(), ""); String trustStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslTruststorePasswdProperty(), - getSslTruststorePasswdPathProperty()); + getSslTruststorePasswdPathProperty()); String trustStoreType = config.getProperty(getSslTruststoreTypeProperty()); boolean sslCrlEnabled = config.getBoolean(getSslCrlEnabledProperty()); @@ -214,8 +222,8 @@ public class ClientX509Util extends X509Util { return null; } else { return createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, - sslCrlEnabled, sslOcspEnabled, sslServerHostnameVerificationEnabled, - sslClientHostnameVerificationEnabled, getFipsMode(config)); + sslCrlEnabled, sslOcspEnabled, sslServerHostnameVerificationEnabled, + sslClientHostnameVerificationEnabled, getFipsMode(config)); } } } -- cgit v1.2.3