From 08b2d93226071c74ce8bf85265429511e417af73 Mon Sep 17 00:00:00 2001 From: JP Bourget Date: Wed, 8 Apr 2020 11:19:34 -0400 Subject: [PATCH] Add support for Consul ACL token parameter. When running Consul with ACLs enabled, the best practice is to default deny all. In order to support applying a policy to the tooz coordination KV store path, passing through an ACL is needed. Closes-Bug: #1752205 Change-Id: I98fc96468b21368ce66365e3fc38c495b1f2918a --- ...d-consul-acl-support-2f0869681129f7e7.yaml | 12 +++++++ setup.cfg | 2 +- tooz/drivers/consul.py | 33 ++++++++++++++----- 3 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/add-consul-acl-support-2f0869681129f7e7.yaml diff --git a/releasenotes/notes/add-consul-acl-support-2f0869681129f7e7.yaml b/releasenotes/notes/add-consul-acl-support-2f0869681129f7e7.yaml new file mode 100644 index 00000000..417e1b93 --- /dev/null +++ b/releasenotes/notes/add-consul-acl-support-2f0869681129f7e7.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + Added support for Consul ACL tokens. Consul ACL tokens can be passed in as + a parameterized argument. Requires python-consul2 +upgrade: + - | + Updated the python-consul library to python-consul2. The original library is + no longer maintained while python-consul2 is reasonably maintained. Consul 1.4 + has a new ACL implementation, and Consul 1.7 includes some standards enforcement + which break python-consul. + diff --git a/setup.cfg b/setup.cfg index f1b01cf6..91aac094 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,7 +44,7 @@ tooz.backends = [extras] consul = - python-consul>=0.4.7 # MIT License + python-consul2>=0.0.16 # MIT License etcd = requests>=2.10.0 # Apache-2.0 etcd3 = diff --git a/tooz/drivers/consul.py b/tooz/drivers/consul.py index 26a72e0f..2b9c23bb 100644 --- a/tooz/drivers/consul.py +++ b/tooz/drivers/consul.py @@ -27,11 +27,12 @@ from tooz import utils class ConsulLock(locking.Lock): - def __init__(self, name, node, address, session_id, client): + def __init__(self, name, node, address, session_id, client, token=None): super(ConsulLock, self).__init__(name) self._name = name self._node = node self._address = address + self._acl_token = token self._session_id = session_id self._client = client self.acquired = False @@ -45,7 +46,8 @@ class ConsulLock(locking.Lock): # Check if we are the owner and if we are simulate # blocking (because consul will not block a second # acquisition attempt by the same owner). - _index, value = self._client.kv.get(key=self._name) + _index, value = self._client.kv.get(key=self._name, + token=self._acl_token) if value and value.get('Session') == self._session_id: if blocking is False: return False @@ -55,7 +57,8 @@ class ConsulLock(locking.Lock): # The value can be anything. gotten = self._client.kv.put(key=self._name, value=u"I got it!", - acquire=self._session_id) + acquire=self._session_id, + token=self._acl_token) if gotten: self.acquired = True return True @@ -70,14 +73,16 @@ class ConsulLock(locking.Lock): if not self.acquired: return False # Get the lock to verify the session ID's are same - _index, contents = self._client.kv.get(key=self._name) + _index, contents = self._client.kv.get(key=self._name, + token=self._acl_token) if not contents: return False owner = contents.get('Session') if owner == self._session_id: removed = self._client.kv.put(key=self._name, value=self._session_id, - release=self._session_id) + release=self._session_id, + token=self._acl_token) if removed: self.acquired = False return True @@ -103,6 +108,7 @@ class ConsulDriver(coordination.CoordinationDriver): ================== ======= ttl 15 namespace tooz + acl_token None ================== ======= For details on the available options, refer to @@ -121,6 +127,9 @@ class ConsulDriver(coordination.CoordinationDriver): #: Default consul port if not provided. DEFAULT_PORT = 8500 + #: Consul ACL Token if not provided + ACL_TOKEN = None + def __init__(self, member_id, parsed_url, options): super(ConsulDriver, self).__init__(member_id, parsed_url, options) options = utils.collapse(options) @@ -131,13 +140,15 @@ class ConsulDriver(coordination.CoordinationDriver): self._ttl = int(options.get('ttl', self.DEFAULT_TTL)) namespace = options.get('namespace', self.TOOZ_NAMESPACE) self._namespace = encodeutils.safe_decode(namespace) + self._acl_token = options.get('acl_token', self.ACL_TOKEN) self._client = None def _start(self): """Create a client, register a node and create a session.""" # Create a consul client if self._client is None: - self._client = consul.Consul(host=self._host, port=self._port) + self._client = consul.Consul(host=self._host, port=self._port, + token=self._acl_token) local_agent = self._client.agent.self() self._node = local_agent['Member']['Name'] @@ -145,11 +156,15 @@ class ConsulDriver(coordination.CoordinationDriver): # Register a Node self._client.catalog.register(node=self._node, - address=self._address) + address=self._address, + token=self._acl_token) # Create a session self._session_id = self._client.session.create( - name=self._session_name, node=self._node, ttl=self._ttl) + name=self._session_name, + node=self._node, + ttl=self._ttl, + token=self._acl_token) def _stop(self): if self._client is not None: @@ -175,7 +190,7 @@ class ConsulDriver(coordination.CoordinationDriver): real_name = self._paths_join(self._namespace, u"locks", name) return ConsulLock(real_name, self._node, self._address, session_id=self._session_id, - client=self._client) + client=self._client, token=self._acl_token) @staticmethod def _paths_join(*args):