浏览代码

ZOOKEEPER-3689: zkCli/ZooKeeperMain relies on system properties for TLS config

The current zkCli uses system properties to set confidential information like keystore location, password etc. This is not secure as these properties need to be passed on the command line as "-D"  arguments.

Currently, there is no way to create a ZookeeperAdmin does not have a constructor which takes both canBeReadOnly and ZKClientConfig as parameters.  I am introducing a new constructor in ZookeeperAdmin which takes an additional ZKClientConfig parameter. This ZKClientConfig is created by an optional command line argument ``client-configuration``. If no argument is passed, a ZookeeperAdmin object with null client config is created, just like before.

Author: Sankalp <sankal@amazon.com>

Reviewers: Enrico Olivelli <eolivelli@apache.org>, Norbert Kalmar <nkalmar@apache.org>, Justin Ling Mao <maoling199210191@sina.com>

Closes #1285 from sankalpbhatia/ZOOKEEPER-3689
Sankalp 5 年之前
父节点
当前提交
b4a3238d1d

+ 2 - 0
zookeeper-docs/src/main/resources/markdown/zookeeperCLI.md

@@ -24,6 +24,8 @@ Enter into the ZooKeeper-cli
 bin/zkCli.sh
 bin/zkCli.sh
 # connect to the remote host with timeout:3s
 # connect to the remote host with timeout:3s
 bin/zkCli.sh -timeout 3000 -server remoteIP:2181
 bin/zkCli.sh -timeout 3000 -server remoteIP:2181
+# connect with a custom client configuration properties file
+bin/zkCli.sh -client-configuration /path/to/client.properties
 ```
 ```
 ## help
 ## help
 Showing helps about ZooKeeper commands
 Showing helps about ZooKeeper commands

+ 17 - 2
zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java

@@ -65,6 +65,7 @@ import org.apache.zookeeper.cli.SyncCommand;
 import org.apache.zookeeper.cli.VersionCommand;
 import org.apache.zookeeper.cli.VersionCommand;
 import org.apache.zookeeper.client.ZKClientConfig;
 import org.apache.zookeeper.client.ZKClientConfig;
 import org.apache.zookeeper.server.ExitCode;
 import org.apache.zookeeper.server.ExitCode;
+import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
 import org.apache.zookeeper.util.ServiceUtils;
 import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
@@ -130,7 +131,7 @@ public class ZooKeeperMain {
     }
     }
 
 
     static void usage() {
     static void usage() {
-        System.err.println("ZooKeeper -server host:port cmd args");
+        System.err.println("ZooKeeper -server host:port -client-configuration properties-file cmd args");
         List<String> cmdList = new ArrayList<String>(commandMap.keySet());
         List<String> cmdList = new ArrayList<String>(commandMap.keySet());
         Collections.sort(cmdList);
         Collections.sort(cmdList);
         for (String cmd : cmdList) {
         for (String cmd : cmdList) {
@@ -205,6 +206,8 @@ public class ZooKeeperMain {
                         options.put("timeout", it.next());
                         options.put("timeout", it.next());
                     } else if (opt.equals("-r")) {
                     } else if (opt.equals("-r")) {
                         options.put("readonly", "true");
                         options.put("readonly", "true");
+                    } else if (opt.equals("-client-configuration")) {
+                        options.put("client-configuration", it.next());
                     }
                     }
                 } catch (NoSuchElementException e) {
                 } catch (NoSuchElementException e) {
                     System.err.println("Error: no argument found for option " + opt);
                     System.err.println("Error: no argument found for option " + opt);
@@ -286,7 +289,19 @@ public class ZooKeeperMain {
             System.setProperty(ZKClientConfig.SECURE_CLIENT, "true");
             System.setProperty(ZKClientConfig.SECURE_CLIENT, "true");
             System.out.println("Secure connection is enabled");
             System.out.println("Secure connection is enabled");
         }
         }
-        zk = new ZooKeeperAdmin(host, Integer.parseInt(cl.getOption("timeout")), new MyWatcher(), readOnly);
+
+        ZKClientConfig clientConfig = null;
+
+        if (cl.getOption("client-configuration") != null) {
+            try {
+                clientConfig = new ZKClientConfig(cl.getOption("client-configuration"));
+            } catch (QuorumPeerConfig.ConfigException e) {
+                e.printStackTrace();
+                ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
+            }
+        }
+
+        zk = new ZooKeeperAdmin(host, Integer.parseInt(cl.getOption("timeout")), new MyWatcher(), readOnly, clientConfig);
     }
     }
 
 
     public static void main(String[] args) throws IOException, InterruptedException {
     public static void main(String[] args) throws IOException, InterruptedException {

+ 48 - 0
zookeeper-server/src/main/java/org/apache/zookeeper/admin/ZooKeeperAdmin.java

@@ -124,6 +124,54 @@ public class ZooKeeperAdmin extends ZooKeeper {
         super(connectString, sessionTimeout, watcher, conf);
         super(connectString, sessionTimeout, watcher, conf);
     }
     }
 
 
+    /**
+     * Create a ZooKeeperAdmin object which is used to perform dynamic reconfiguration
+     * operations.
+     *
+     * @param connectString
+     *            comma separated host:port pairs, each corresponding to a zk
+     *            server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002" If
+     *            the optional chroot suffix is used the example would look
+     *            like: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a"
+     *            where the client would be rooted at "/app/a" and all paths
+     *            would be relative to this root - ie getting/setting/etc...
+     *            "/foo/bar" would result in operations being run on
+     *            "/app/a/foo/bar" (from the server perspective).
+     * @param sessionTimeout
+     *            session timeout in milliseconds
+     * @param watcher
+     *            a watcher object which will be notified of state changes, may
+     *            also be notified for node events
+     * @param canBeReadOnly
+     *            whether the created client is allowed to go to
+     *            read-only mode in case of partitioning. Read-only mode
+     *            basically means that if the client can't find any majority
+     *            servers but there's partitioned server it could reach, it
+     *            connects to one in read-only mode, i.e. read requests are
+     *            allowed while write requests are not. It continues seeking for
+     *            majority in the background.
+     * @param conf
+     *            passing this conf object gives each client the flexibility of
+     *            configuring properties differently compared to other instances
+     *
+     * @throws IOException
+     *             in cases of network failure
+     * @throws IllegalArgumentException
+     *             if an invalid chroot path is specified
+     *
+     * @see ZooKeeper#ZooKeeper(String, int, Watcher, boolean, ZKClientConfig)
+     *
+     * @since 3.6.1
+     */
+    public ZooKeeperAdmin(
+        String connectString,
+        int sessionTimeout,
+        Watcher watcher,
+        boolean canBeReadOnly,
+        ZKClientConfig conf) throws IOException {
+        super(connectString, sessionTimeout, watcher, canBeReadOnly, conf);
+    }
+
     /**
     /**
      * Create a ZooKeeperAdmin object which is used to perform dynamic reconfiguration
      * Create a ZooKeeperAdmin object which is used to perform dynamic reconfiguration
      * operations.
      * operations.