From 48aa0ba61ba420752356dcf4093aab639dae8b61 Mon Sep 17 00:00:00 2001 From: Kostiantyn Nemchenko Date: Fri, 28 Aug 2020 09:22:15 +0300 Subject: [PATCH] Add SSL support for ZooKeeper (#1662) Close #1658 --- docs/ENVIRONMENT.rst | 12 +++++++++++- docs/SETTINGS.rst | 12 +++++++++++- patroni/config.py | 2 +- patroni/dcs/zookeeper.py | 6 +++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/ENVIRONMENT.rst b/docs/ENVIRONMENT.rst index 7e19fc0a..d3a54c99 100644 --- a/docs/ENVIRONMENT.rst +++ b/docs/ENVIRONMENT.rst @@ -76,7 +76,17 @@ Environment names for Etcdv3 are similar as for Etcd, you just need to use ``ETC ZooKeeper --------- -- **PATRONI\_ZOOKEEPER\_HOSTS**: comma separated list of ZooKeeper cluster members: "'host1:port1','host2:port2','etc...'". It is important to quote every single entity! +- **PATRONI\_ZOOKEEPER\_HOSTS**: Comma separated list of ZooKeeper cluster members: "'host1:port1','host2:port2','etc...'". It is important to quote every single entity! +- **PATRONI\_ZOOKEEPER\_USE\_SSL**: (optional) Whether SSL is used or not. Defaults to ``false``. If set to ``false``, all SSL specific parameters are ignored. +- **PATRONI\_ZOOKEEPER\_CACERT**: (optional) The CA certificate. If present it will enable validation. +- **PATRONI\_ZOOKEEPER\_CERT**: (optional) File with the client certificate. +- **PATRONI\_ZOOKEEPER\_KEY**: (optional) File with the client key. +- **PATRONI\_ZOOKEEPER\_KEY\_PASSWORD**: (optional) The client key password. +- **PATRONI\_ZOOKEEPER\_VERIFY**: (optional) Whether to verify certificate or not. Defaults to ``true``. + +.. note:: + It is required to install ``kazoo>=2.6.0`` to support SSL. + Exhibitor --------- diff --git a/docs/SETTINGS.rst b/docs/SETTINGS.rst index 502116ba..21030daa 100644 --- a/docs/SETTINGS.rst +++ b/docs/SETTINGS.rst @@ -147,7 +147,17 @@ If you want that Patroni works with Etcd cluster via protocol version 3, you nee ZooKeeper ---------- -- **hosts**: list of ZooKeeper cluster members in format: ['host1:port1', 'host2:port2', 'etc...']. +- **hosts**: List of ZooKeeper cluster members in format: ['host1:port1', 'host2:port2', 'etc...']. +- **use_ssl**: (optional) Whether SSL is used or not. Defaults to ``false``. If set to ``false``, all SSL specific parameters are ignored. +- **cacert**: (optional) The CA certificate. If present it will enable validation. +- **cert**: (optional) File with the client certificate. +- **key**: (optional) File with the client key. +- **key_password**: (optional) The client key password. +- **verify**: (optional) Whether to verify certificate or not. Defaults to ``true``. + +.. note:: + It is required to install ``kazoo>=2.6.0`` to support SSL. + Exhibitor --------- diff --git a/patroni/config.py b/patroni/config.py index f81fa16a..b1f0a923 100644 --- a/patroni/config.py +++ b/patroni/config.py @@ -308,7 +308,7 @@ class Config(object): 'CACERT', 'CERT', 'KEY', 'VERIFY', 'TOKEN', 'CHECKS', 'DC', 'CONSISTENCY', 'REGISTER_SERVICE', 'SERVICE_CHECK_INTERVAL', 'NAMESPACE', 'CONTEXT', 'USE_ENDPOINTS', 'SCOPE_LABEL', 'ROLE_LABEL', 'POD_IP', 'PORTS', 'LABELS', - 'BYPASS_API_SERVICE') and name: + 'BYPASS_API_SERVICE', 'KEY_PASSWORD', 'USE_SSL') and name: value = os.environ.pop(param) if suffix == 'PORT': value = value and parse_int(value) diff --git a/patroni/dcs/zookeeper.py b/patroni/dcs/zookeeper.py index 9c6b01b6..30b1e222 100644 --- a/patroni/dcs/zookeeper.py +++ b/patroni/dcs/zookeeper.py @@ -63,10 +63,14 @@ class ZooKeeper(AbstractDCS): if isinstance(hosts, list): hosts = ','.join(hosts) + mapping = {'use_ssl': 'use_ssl', 'verify': 'verify_certs', 'cacert': 'ca', + 'cert': 'certfile', 'key': 'keyfile', 'key_password': 'keyfile_password'} + kwargs = {v: config[k] for k, v in mapping.items() if k in config} + self._client = KazooClient(hosts, handler=PatroniSequentialThreadingHandler(config['retry_timeout']), timeout=config['ttl'], connection_retry=KazooRetry(max_delay=1, max_tries=-1, sleep_func=time.sleep), command_retry=KazooRetry(deadline=config['retry_timeout'], - max_delay=1, max_tries=-1, sleep_func=time.sleep)) + max_delay=1, max_tries=-1, sleep_func=time.sleep), **kwargs) self._client.add_listener(self.session_listener) self._fetch_cluster = True