Add SSL support for Zookeeper client

Zookeeper server from version 3.5.x is able to server traffic over SSL.
With the fix in Zookeeper version 3.5.7 skipping client SSL
authentication is possible. Bump Zookeeper client library to 3.5.7 to
match Zookeeper service version.

Add Gerrit configuration to use SSL for Zookeeper client.

Feature: Issue 12583
Change-Id: I75b9b89e8994a517a9030906efa01415ed5bce6c
(cherry picked from commit 7820b9541583f6c98da99b61bce66e9a056e9b7b)
diff --git a/BUILD b/BUILD
index 80c7dda..9238349 100644
--- a/BUILD
+++ b/BUILD
@@ -22,6 +22,8 @@
         "@curator-recipes//jar",
         "@global-refdb//jar",
         "@zookeeper//jar",
+        "@zookeeper-jute//jar",
+        "@netty-all//jar",
     ],
 )
 
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index 5280f73..3e18e21 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -29,8 +29,20 @@
 
     maven_jar(
         name = "zookeeper",
-        artifact = "org.apache.zookeeper:zookeeper:3.4.14",
-        sha1 = "c114c1e1c8172a7cd3f6ae39209a635f7a06c1a1",
+        artifact = "org.apache.zookeeper:zookeeper:3.5.7",
+        sha1 = "12bdf55ba8be7fc891996319d37f35eaad7e63ea",
+    )
+
+    maven_jar(
+        name = "zookeeper-jute",
+        artifact = "org.apache.zookeeper:zookeeper-jute:3.5.7",
+        sha1 = "1270f80b08904499a6839a2ee1800da687ad96b4",
+    )
+
+    maven_jar(
+        name = "netty-all",
+        artifact = "io.netty:netty-all:4.1.45.Final",
+        sha1 = "e830eae36d22f2bba3118a3bc08e17f15263a01d",
     )
 
     maven_jar(
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java
index 7523d1f..d9c7a4a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.Inject;
+import java.util.Optional;
 import org.apache.commons.lang.StringUtils;
 import org.apache.curator.RetryPolicy;
 import org.apache.curator.framework.CuratorFramework;
@@ -41,6 +42,7 @@
   private final int DEFAULT_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS = 300;
   private final int DEFAULT_CAS_RETRY_POLICY_MAX_RETRIES = 3;
   private final int DEFAULT_TRANSACTION_LOCK_TIMEOUT = 1000;
+  private final boolean DEFAULT_SSL_CONNECTION = false;
 
   static {
     CuratorFrameworkFactory.Builder b = CuratorFrameworkFactory.builder();
@@ -56,6 +58,13 @@
   public static final String KEY_RETRY_POLICY_MAX_SLEEP_TIME_MS = "retryPolicyMaxSleepTimeMs";
   public static final String KEY_RETRY_POLICY_MAX_RETRIES = "retryPolicyMaxRetries";
   public static final String KEY_ROOT_NODE = "rootNode";
+  public static final String SSL_CONNECTION = "sslConnection";
+  public static final String SSL_KEYSTORE_LOCATION = "sslKeyStoreLocation";
+  public static final String SSL_TRUSTSTORE_LOCATION = "sslTrustStoreLocation";
+
+  public static final String SSL_KEYSTORE_PASSWORD = "sslKeyStorePassword";
+  public static final String SSL_TRUSTSTORE_PASSWORD = "sslTrustStorePassword";
+
   public final String KEY_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS = "casRetryPolicyBaseSleepTimeMs";
   public final String KEY_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS = "casRetryPolicyMaxSleepTimeMs";
   public final String KEY_CAS_RETRY_POLICY_MAX_RETRIES = "casRetryPolicyMaxRetries";
@@ -72,6 +81,12 @@
   private final int casMaxSleepTimeMs;
   private final int casMaxRetries;
 
+  private Boolean isSSLEnabled;
+  private Optional<String> sslKeyStoreLocation;
+  private Optional<String> sslTrustStoreLocation;
+  private Optional<String> sslKeyStorePassword;
+  private Optional<String> sslTrustStorePassword;
+
   public static final String SECTION = "ref-database";
   private final Long transactionLockTimeOut;
 
@@ -145,10 +160,39 @@
             TRANSACTION_LOCK_TIMEOUT_KEY,
             DEFAULT_TRANSACTION_LOCK_TIMEOUT);
 
+    isSSLEnabled =
+        getBoolean(zkConfig, SECTION, SUBSECTION, SSL_CONNECTION, DEFAULT_SSL_CONNECTION);
+
+    sslKeyStoreLocation = getOptionalString(zkConfig, SECTION, SUBSECTION, SSL_KEYSTORE_LOCATION);
+
+    sslTrustStoreLocation =
+        getOptionalString(zkConfig, SECTION, SUBSECTION, SSL_TRUSTSTORE_LOCATION);
+
+    sslKeyStorePassword = getOptionalString(zkConfig, SECTION, SUBSECTION, SSL_KEYSTORE_PASSWORD);
+
+    sslTrustStorePassword =
+        getOptionalString(zkConfig, SECTION, SUBSECTION, SSL_TRUSTSTORE_PASSWORD);
+
     checkArgument(StringUtils.isNotEmpty(connectionString), "zookeeper.%s contains no servers");
   }
 
   public CuratorFramework buildCurator() {
+    if (isSSLEnabled) {
+
+      System.setProperty(
+          "zookeeper.clientCnxnSocket", "org.apache.zookeeper.ClientCnxnSocketNetty");
+      System.setProperty("zookeeper.client.secure", "true");
+
+      sslKeyStoreLocation.ifPresent(
+          location -> System.setProperty("zookeeper.ssl.keyStore.location", location));
+      sslTrustStoreLocation.ifPresent(
+          location -> System.setProperty("zookeeper.ssl.trustStore.location", location));
+      sslKeyStorePassword.ifPresent(
+          passw -> System.setProperty("zookeeper.ssl.keyStore.password", passw));
+      sslTrustStorePassword.ifPresent(
+          passw -> System.setProperty("zookeeper.ssl.trustStore.password", passw));
+    }
+
     if (build == null) {
       this.build =
           CuratorFrameworkFactory.builder()
@@ -194,6 +238,11 @@
     }
   }
 
+  private Optional<String> getOptionalString(
+      Config cfg, String section, String subsection, String name) {
+    return Optional.ofNullable(cfg.getString(section, subsection, name)).filter(s -> !s.isEmpty());
+  }
+
   private String getString(
       Config cfg, String section, String subsection, String name, String defaultValue) {
     String value = cfg.getString(section, subsection, name);
@@ -202,4 +251,9 @@
     }
     return defaultValue;
   }
+
+  private Boolean getBoolean(
+      Config cfg, String section, String subSection, String name, Boolean defaultValue) {
+    return cfg.getBoolean(section, subSection, name, defaultValue);
+  }
 }
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 03ffab4..f3f6563 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -9,6 +9,10 @@
 
 Originally this code was a part of [multi-site plugin](https://gerrit.googlesource.com/plugins/multi-site/) but currently can be use independently.
 
+## Requirements
+
+Supported Zookeeper version is 3.5.x. For SSL connection minimal Zookeeper version is 3.5.1[ZOOKEEPER-2125](https://issues.apache.org/jira/browse/ZOOKEEPER-2125).
+
 ## Setup
 
 * Install @PLUGIN@ plugin
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 5cd89a5..00a2468 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -16,6 +16,15 @@
   transactionLockTimeoutMs = 1000
 ```
 
+## Sample SSL configuration
+```
+[ref-database "zookeeper"]
+  connectString = "zookeeperhost:2281"
+  sslConnection = true
+  rootNode = "/gerrit/multi-site"
+  transactionLockTimeoutMs = 1000
+```
+
 ## Configuration parameters
 
 ```ref-database.zookeeper.connectString```
@@ -81,3 +90,25 @@
     acquires the exclusive lock for a reference.
 
     Defaults: 1000
+
+```ref-database.zookeeper.sslConnection```
+:   Enable ssl for Zookeeper connection.
+
+    Defaults: false
+
+```ref-database.zookeeper.sslKeyStoreLocation```
+:   Optional configuration of the path to the ssl key store location.
+
+```ref-database.zookeeper.sslTrustStoreLocation```
+:   Optional configuration of the path to the ssl trust store for server-side X.509 certificate validation.
+
+File '@PLUGIN@.secure.config'
+--------------------
+
+## Configuration parameters
+
+```ref-database.zookeeper.sslKeyStorePassword```
+:   Optional configuration for the password to the ssl key store.
+
+```ref-database.zookeeper.sslTrustStorePassword```
+:   Optional configuration for the password to the ssl trust store.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java b/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java
index 59b7afd..107ece9 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java
@@ -33,7 +33,7 @@
 public class ZookeeperTestContainerSupport {
 
   static class ZookeeperContainer extends GenericContainer<ZookeeperContainer> {
-    public static String ZOOKEEPER_VERSION = "3.4.13";
+    public static String ZOOKEEPER_VERSION = "3.5.5";
 
     public ZookeeperContainer() {
       super("zookeeper:" + ZOOKEEPER_VERSION);