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
This commit is contained in:
gengchc2 2017-12-25 19:00:10 -08:00
parent 764bbb97dd
commit 856884641f
5 changed files with 58 additions and 11 deletions

View File

@ -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.

View File

@ -73,9 +73,9 @@ commands =
[flake8] [flake8]
exclude = .venv*,.git,.tox,dist,doc,*lib/python*,*.egg,.update-venv 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. # 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. # [H904] Delay string interpolations at logging calls.
enable-extensions=H904 enable-extensions=H904

View File

@ -53,6 +53,13 @@ class ConnectionURI(object):
path, sep, query = path.partition('?') path, sep, query = path.partition('?')
else: else:
query = parsed_url.query 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)) 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 # NOTE(kgriffs): Have to parse list of sentinel hosts ourselves
# since urllib doesn't support it. # 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(':') name, sep, port = each_host.partition(':')
if port: if port:
@ -101,8 +108,8 @@ class ConnectionURI(object):
'sentinel hosts') 'sentinel hosts')
raise errors.ConfigurationError(msg) raise errors.ConfigurationError(msg)
elif parsed_url.netloc: elif netloc:
if ',' in parsed_url.netloc: if ',' in netloc:
# NOTE(kgriffs): They probably were specifying # NOTE(kgriffs): They probably were specifying
# a list of sentinel hostnames, but forgot to # a list of sentinel hostnames, but forgot to
# add 'master' to the query string. # add 'master' to the query string.
@ -276,6 +283,8 @@ def _get_redis_client(driver):
if connection_uri.strategy == STRATEGY_SENTINEL: if connection_uri.strategy == STRATEGY_SENTINEL:
sentinel = redis.sentinel.Sentinel( sentinel = redis.sentinel.Sentinel(
connection_uri.sentinels, connection_uri.sentinels,
db=connection_uri.dbid,
password=connection_uri.password,
socket_timeout=connection_uri.socket_timeout) socket_timeout=connection_uri.socket_timeout)
# NOTE(prashanthr_): The socket_timeout parameter being generic # NOTE(prashanthr_): The socket_timeout parameter being generic
@ -288,8 +297,11 @@ def _get_redis_client(driver):
host=connection_uri.hostname, host=connection_uri.hostname,
port=connection_uri.port, port=connection_uri.port,
db=connection_uri.dbid, db=connection_uri.dbid,
password=connection_uri.password,
socket_timeout=connection_uri.socket_timeout) socket_timeout=connection_uri.socket_timeout)
else: else:
return redis.StrictRedis( return redis.StrictRedis(
unix_socket_path=connection_uri.unix_socket_path, unix_socket_path=connection_uri.unix_socket_path,
db=connection_uri.dbid,
password=connection_uri.password,
socket_timeout=connection_uri.socket_timeout) socket_timeout=connection_uri.socket_timeout)

View File

@ -25,10 +25,13 @@ _COMMON_REDIS_OPTIONS = (
group=_deprecated_group), ], group=_deprecated_group), ],
help=('Redis connection URI, taking one of three forms. ' help=('Redis connection URI, taking one of three forms. '
'For a direct connection to a Redis server, use ' 'For a direct connection to a Redis server, use '
'the form "redis://host[:port][?options]", where ' 'the form "redis://[:password]@host[:port][?options]", '
'port defaults to 6379 if not specified. For an ' 'where password is redis-server\'s password, when'
'HA master-slave Redis cluster using Redis Sentinel, ' 'redis-server is set password, the password option'
'use the form "redis://host1[:port1]' '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]", ' '[,host2[:port2],...,hostN[:portN]][?options]", '
'where each host specified corresponds to an ' 'where each host specified corresponds to an '
'instance of redis-sentinel. In this form, the ' 'instance of redis-sentinel. In this form, the '
@ -37,8 +40,8 @@ _COMMON_REDIS_OPTIONS = (
'string as "master=<name>". Finally, to connect ' 'string as "master=<name>". Finally, to connect '
'to a local instance of Redis over a unix socket, ' 'to a local instance of Redis over a unix socket, '
'you may use the form ' 'you may use the form '
'"redis:/path/to/redis.sock[?options]". In all ' '"redis:[:password]@/path/to/redis.sock[?options]".'
'forms, the "socket_timeout" option may be ' ' In all forms, the "socket_timeout" option may be'
'specified in the query string. Its value is ' 'specified in the query string. Its value is '
'given in seconds. If not provided, ' 'given in seconds. If not provided, '
'"socket_timeout" defaults to 0.1 seconds.' '"socket_timeout" defaults to 0.1 seconds.'

View File

@ -244,6 +244,14 @@ class RedisDriverTest(testing.TestBase):
self.assertEqual(7777, uri.port) self.assertEqual(7777, uri.port)
self.assertEqual(1.0, uri.socket_timeout) 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): def test_connection_uri_unix_socket(self):
uri = driver.ConnectionURI('redis:/tmp/redis.sock') uri = driver.ConnectionURI('redis:/tmp/redis.sock')
self.assertEqual(driver.STRATEGY_UNIX, uri.strategy) 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('/tmp/redis.sock', uri.unix_socket_path)
self.assertEqual(1.5, uri.socket_timeout) 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): def test_connection_uri_sentinel(self):
uri = driver.ConnectionURI('redis://s1?master=dumbledore') uri = driver.ConnectionURI('redis://s1?master=dumbledore')
self.assertEqual(driver.STRATEGY_SENTINEL, uri.strategy) self.assertEqual(driver.STRATEGY_SENTINEL, uri.strategy)
@ -281,6 +297,15 @@ class RedisDriverTest(testing.TestBase):
self.assertEqual('dumbledore', uri.master) self.assertEqual('dumbledore', uri.master)
self.assertEqual(0.5, uri.socket_timeout) 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 @testing.requires_redis
class RedisQueuesTest(base.QueueControllerTest): class RedisQueuesTest(base.QueueControllerTest):