From 856884641f9be3c6a935742fb74fc0014385f1ff Mon Sep 17 00:00:00 2001 From: gengchc2 Date: Mon, 25 Dec 2017 19:00:10 -0800 Subject: [PATCH] Redis connection support password configure in zaqar Redis connection doesn't support password configure in zaqar, so redis-server can not set a password. If redis service doesn't set a password, it will suffer a large number of attacks. The patch will support password configure in zaqar. In addition, it is needed to add unit test in the previous patch[https://review.openstack.org/#/c/511676/]. Change-Id: I03661584f1c24fdb3e76a819c9eaa0ed32f272c4 --- ...for_redis_connection-6f169db73ca80416.yaml | 7 ++++++ tox.ini | 4 +-- zaqar/storage/redis/driver.py | 18 ++++++++++--- zaqar/storage/redis/options.py | 15 ++++++----- zaqar/tests/unit/storage/test_impl_redis.py | 25 +++++++++++++++++++ 5 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/support_password_configure_for_redis_connection-6f169db73ca80416.yaml diff --git a/releasenotes/notes/support_password_configure_for_redis_connection-6f169db73ca80416.yaml b/releasenotes/notes/support_password_configure_for_redis_connection-6f169db73ca80416.yaml new file mode 100644 index 000000000..8544d2523 --- /dev/null +++ b/releasenotes/notes/support_password_configure_for_redis_connection-6f169db73ca80416.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Redis connection doesn't support password configure in zaqar, + so redis-server can not set a password. If redis service doesn't + set a password, it will suffer a large number of attacks. The + patch will support password configure for redis connection in zaqar. diff --git a/tox.ini b/tox.ini index ebe6da552..516259e0d 100644 --- a/tox.ini +++ b/tox.ini @@ -73,9 +73,9 @@ commands = [flake8] exclude = .venv*,.git,.tox,dist,doc,*lib/python*,*.egg,.update-venv -# NOTE(flaper87): Our currently max-complexity is 15. Not sure what the ideal complexity +# NOTE(flaper87): Our currently max-complexity is 20. Not sure what the ideal complexity # for Zaqar should be but lets keep it to the minimum possible. -max-complexity = 16 +max-complexity = 20 # [H904] Delay string interpolations at logging calls. enable-extensions=H904 diff --git a/zaqar/storage/redis/driver.py b/zaqar/storage/redis/driver.py index 09641831c..1e45e8225 100644 --- a/zaqar/storage/redis/driver.py +++ b/zaqar/storage/redis/driver.py @@ -53,6 +53,13 @@ class ConnectionURI(object): path, sep, query = path.partition('?') else: query = parsed_url.query + # NOTE(gengchc2): Redis connection support password configure. + self.password = None + if '@' in path: + self.password, sep, path = path.partition('@') + netloc = parsed_url.netloc + if '@' in netloc: + self.password, sep, netloc = netloc.partition('@') query_params = dict(urllib.parse.parse_qsl(query)) @@ -80,7 +87,7 @@ class ConnectionURI(object): # NOTE(kgriffs): Have to parse list of sentinel hosts ourselves # since urllib doesn't support it. - for each_host in parsed_url.netloc.split(','): + for each_host in netloc.split(','): name, sep, port = each_host.partition(':') if port: @@ -101,8 +108,8 @@ class ConnectionURI(object): 'sentinel hosts') raise errors.ConfigurationError(msg) - elif parsed_url.netloc: - if ',' in parsed_url.netloc: + elif netloc: + if ',' in netloc: # NOTE(kgriffs): They probably were specifying # a list of sentinel hostnames, but forgot to # add 'master' to the query string. @@ -276,6 +283,8 @@ def _get_redis_client(driver): if connection_uri.strategy == STRATEGY_SENTINEL: sentinel = redis.sentinel.Sentinel( connection_uri.sentinels, + db=connection_uri.dbid, + password=connection_uri.password, socket_timeout=connection_uri.socket_timeout) # NOTE(prashanthr_): The socket_timeout parameter being generic @@ -288,8 +297,11 @@ def _get_redis_client(driver): host=connection_uri.hostname, port=connection_uri.port, db=connection_uri.dbid, + password=connection_uri.password, socket_timeout=connection_uri.socket_timeout) else: return redis.StrictRedis( unix_socket_path=connection_uri.unix_socket_path, + db=connection_uri.dbid, + password=connection_uri.password, socket_timeout=connection_uri.socket_timeout) diff --git a/zaqar/storage/redis/options.py b/zaqar/storage/redis/options.py index 3e4c42ccb..1e9cbd306 100644 --- a/zaqar/storage/redis/options.py +++ b/zaqar/storage/redis/options.py @@ -25,10 +25,13 @@ _COMMON_REDIS_OPTIONS = ( group=_deprecated_group), ], help=('Redis connection URI, taking one of three forms. ' 'For a direct connection to a Redis server, use ' - 'the form "redis://host[:port][?options]", where ' - 'port defaults to 6379 if not specified. For an ' - 'HA master-slave Redis cluster using Redis Sentinel, ' - 'use the form "redis://host1[:port1]' + 'the form "redis://[:password]@host[:port][?options]", ' + 'where password is redis-server\'s password, when' + 'redis-server is set password, the password option' + 'needs to be set. port defaults to 6379 if not' + 'specified. For an HA master-slave Redis cluster using' + ' Redis Sentinel, use the form ' + '"redis://[:password]@host1[:port1]' '[,host2[:port2],...,hostN[:portN]][?options]", ' 'where each host specified corresponds to an ' 'instance of redis-sentinel. In this form, the ' @@ -37,8 +40,8 @@ _COMMON_REDIS_OPTIONS = ( 'string as "master=". Finally, to connect ' 'to a local instance of Redis over a unix socket, ' 'you may use the form ' - '"redis:/path/to/redis.sock[?options]". In all ' - 'forms, the "socket_timeout" option may be ' + '"redis:[:password]@/path/to/redis.sock[?options]".' + ' In all forms, the "socket_timeout" option may be' 'specified in the query string. Its value is ' 'given in seconds. If not provided, ' '"socket_timeout" defaults to 0.1 seconds.' diff --git a/zaqar/tests/unit/storage/test_impl_redis.py b/zaqar/tests/unit/storage/test_impl_redis.py index 87b689e8c..f9f4ee6a9 100644 --- a/zaqar/tests/unit/storage/test_impl_redis.py +++ b/zaqar/tests/unit/storage/test_impl_redis.py @@ -244,6 +244,14 @@ class RedisDriverTest(testing.TestBase): self.assertEqual(7777, uri.port) self.assertEqual(1.0, uri.socket_timeout) + uri = driver.ConnectionURI( + 'redis://test123@example.com:7777?socket_timeout=1&dbid=5') + self.assertEqual(driver.STRATEGY_TCP, uri.strategy) + self.assertEqual(7777, uri.port) + self.assertEqual(1.0, uri.socket_timeout) + self.assertEqual(5, uri.dbid) + self.assertEqual('test123', uri.password) + def test_connection_uri_unix_socket(self): uri = driver.ConnectionURI('redis:/tmp/redis.sock') self.assertEqual(driver.STRATEGY_UNIX, uri.strategy) @@ -255,6 +263,14 @@ class RedisDriverTest(testing.TestBase): self.assertEqual('/tmp/redis.sock', uri.unix_socket_path) self.assertEqual(1.5, uri.socket_timeout) + uri = driver.ConnectionURI( + 'redis:test123@/tmp/redis.sock?socket_timeout=1.5&dbid=5') + self.assertEqual(driver.STRATEGY_UNIX, uri.strategy) + self.assertEqual('/tmp/redis.sock', uri.unix_socket_path) + self.assertEqual(1.5, uri.socket_timeout) + self.assertEqual(5, uri.dbid) + self.assertEqual('test123', uri.password) + def test_connection_uri_sentinel(self): uri = driver.ConnectionURI('redis://s1?master=dumbledore') self.assertEqual(driver.STRATEGY_SENTINEL, uri.strategy) @@ -281,6 +297,15 @@ class RedisDriverTest(testing.TestBase): self.assertEqual('dumbledore', uri.master) self.assertEqual(0.5, uri.socket_timeout) + uri = driver.ConnectionURI( + 'redis://test123@s1?master=dumbledore&socket_timeout=0.5&dbid=5') + self.assertEqual(driver.STRATEGY_SENTINEL, uri.strategy) + self.assertEqual([('s1', 26379)], uri.sentinels) + self.assertEqual('dumbledore', uri.master) + self.assertEqual(0.5, uri.socket_timeout) + self.assertEqual(5, uri.dbid) + self.assertEqual('test123', uri.password) + @testing.requires_redis class RedisQueuesTest(base.QueueControllerTest):