aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pom.xml1
-rw-r--r--zookeeper-client-common/pom.xml12
-rw-r--r--zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/VespaSslContextProvider.java25
-rw-r--r--zookeeper-client-common/src/main/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilder.java11
-rw-r--r--zookeeper-client-common/src/main/java/org/apache/zookeeper/common/ClientX509Util.java229
-rw-r--r--zookeeper-client-common/src/test/java/com/yahoo/vespa/zookeeper/client/ZkClientConfigBuilderTest.java1
-rw-r--r--zookeeper-common/OWNERS1
-rw-r--r--zookeeper-common/README.md4
-rw-r--r--zookeeper-common/pom.xml51
-rw-r--r--zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZookeeperTlsContextUtils.java26
-rw-r--r--zookeeper-server/zookeeper-server-3.8.0/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java17
-rw-r--r--zookeeper-server/zookeeper-server-common/pom.xml6
-rw-r--r--zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java35
-rw-r--r--zookeeper-server/zookeeper-server/src/main/java/com/yahoo/vespa/zookeeper/VespaMtlsAuthenticationProvider.java15
-rw-r--r--zookeeper-server/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java116
15 files changed, 413 insertions, 137 deletions
diff --git a/pom.xml b/pom.xml
index 4e021fde2e3..52e0ad68215 100644
--- a/pom.xml
+++ b/pom.xml
@@ -139,6 +139,7 @@
<module>zkfacade</module>
<module>zookeeper-client-common</module>
<module>zookeeper-command-line-client</module>
+ <module>zookeeper-common</module>
<module>zookeeper-server</module>
</modules>
diff --git a/zookeeper-client-common/pom.xml b/zookeeper-client-common/pom.xml
index bb86e759dd2..77451c5bec0 100644
--- a/zookeeper-client-common/pom.xml
+++ b/zookeeper-client-common/pom.xml
@@ -21,6 +21,12 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>defaults</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
@@ -28,6 +34,12 @@
<!-- compile scope -->
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>zookeeper-common</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<scope>compile</scope>
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<SSLContext> {
-
- 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..1b240aa4785 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.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<TlsContext> 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..9eda60ea361
--- /dev/null
+++ b/zookeeper-client-common/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
@@ -0,0 +1,229 @@
+/*
+ * 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.vespa.zookeeper.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.
+ * <p>
+ * <em>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</em>
+ *
+ * @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 (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<String> 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<String> 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<String> 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-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 @@
+<!-- Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+# 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 @@
+<?xml version="1.0"?>
+<!-- Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>8-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>zookeeper-common</artifactId>
+ <packaging>jar</packaging>
+ <version>8-SNAPSHOT</version>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>security-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>defaults</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZookeeperTlsContextUtils.java b/zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZookeeperTlsContextUtils.java
new file mode 100644
index 00000000000..493f80d2b40
--- /dev/null
+++ b/zookeeper-common/src/main/java/com/yahoo/vespa/zookeeper/VespaZookeeperTlsContextUtils.java
@@ -0,0 +1,26 @@
+package com.yahoo.vespa.zookeeper;
+
+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> tlsContext() {
+ return Optional.ofNullable(tlsContext);
+ }
+
+}
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-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
@@ -13,6 +13,12 @@
<version>8-SNAPSHOT</version>
<dependencies>
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>zookeeper-common</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
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..ca18e7ef146 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,7 +3,6 @@ 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;
@@ -47,9 +46,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 +58,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 +84,7 @@ public class Configurator {
}
}
- private String transformConfigToString(ZookeeperServerConfig config, VespaTlsConfig vespaTlsConfig, Map<String, String> dynamicConfig) {
+ private static String transformConfigToString(ZookeeperServerConfig config, VespaTlsConfig vespaTlsConfig, Map<String, String> dynamicConfig) {
Map<String, String> configEntries = new LinkedHashMap<>();
configEntries.put("tickTime", Integer.toString(config.tickTime()));
configEntries.put("initLimit", Integer.toString(config.initLimit()));
@@ -118,7 +112,7 @@ public class Configurator {
return transformConfigToString(configEntries);
}
- void addServerSpecs(Map<String, String> configEntries, ZookeeperServerConfig config, Map<String, String> dynamicConfig) {
+ static void addServerSpecs(Map<String, String> configEntries, ZookeeperServerConfig config, Map<String, String> 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 +204,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 +214,6 @@ public class Configurator {
default void appendSharedTlsConfig(Map<String, String> 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 +268,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/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/org/apache/zookeeper/common/ClientX509Util.java b/zookeeper-server/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
index c0034a4723f..9eda60ea361 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.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;
/**
- *
- * <em>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</em>
- * <p>
* X509 utilities specific for client-server communication framework.
+ * <p>
+ * <em>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</em>
+ *
+ * @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<String> 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));
}
}
}