Username support when connecting to REDIS
epicserve opened this issue · comments
I created a simple "hello world" chat application running locally using docker-compose which is running redis:6
. However, when I deployed to a production environment on AWS it didn't work because our ElastiCache Redis Cluster using the 6.2.5
requires a username and password in order to connect.
If you are using docker-compose for development, you can reproduce the error if you configure your Redis service in the following way.
redis:
container_name: redis
image: redis:6
command: ['redis-server', '/etc/redis/redis.conf']
volumes:
- ./redis.conf:/etc/redis/redis.conf
- redis-data:/data
Then in your redis.conf
in your project add the following:
appendonly yes
user myusername +@all allkeys on >my-random-password
Then in your Django settings try the following:
ASGI_APPLICATION = "config.asgi.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": ['redis://myusername:my-random-password@redis:6379'],
},
},
}
Relevant package versions:
Python Version: 3.9.10
channels==3.0.4
channels-redis==3.3.1
Django==3.2.12
django-redis-cache==3.0.0
django-redis-sessions==0.6.2
aioredis==1.3.1
In production, I'm running Channels with gunicorn/uvicorn.
This is a traceback of what is happening locally, which leads me to believe that Channels will need to be upgraded to using aioredis 2.0 as well as a fix to this package. It would also be good if the README and docs could be upgraded to show how you're supposed to configure a Redis host that needs a username and/or password.
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 451, in thread_handler
raise exc_info[1]
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 38, in inner
response = await get_response(request)
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 233, in _get_response_async
response = await wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 414, in __call__
ret = await asyncio.wait_for(future, timeout=None)
File "/usr/local/lib/python3.9/asyncio/tasks.py", line 442, in wait_for
return await fut
File "/usr/local/lib/python3.9/site-packages/asgiref/current_thread_executor.py", line 22, in run
result = self.fn(*self.args, **self.kwargs)
File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 455, in thread_handler
return func(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/contrib/auth/mixins.py", line 128, in dispatch
return super().dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/views/generic/base.py", line 98, in dispatch
return handler(request, *args, **kwargs)
File "/opt/project/apps/accounts/views.py", line 22, in post
async_to_sync(channel_layer.group_send)(f"push_message_{user.username}", data)
File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 204, in __call__
return call_result.result()
File "/usr/local/lib/python3.9/concurrent/futures/_base.py", line 439, in result
return self.__get_result()
File "/usr/local/lib/python3.9/concurrent/futures/_base.py", line 391, in __get_result
raise self._exception
File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 270, in main_wrap
result = await self.awaitable(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/channels_redis/core.py", line 671, in group_send
async with self.connection(self.consistent_hash(group)) as connection:
File "/usr/local/lib/python3.9/site-packages/channels_redis/core.py", line 902, in __aenter__
self.conn = await self.pool.pop()
File "/usr/local/lib/python3.9/site-packages/channels_redis/core.py", line 93, in pop
conn = await self.create_conn(loop)
File "/usr/local/lib/python3.9/site-packages/channels_redis/core.py", line 79, in create_conn
return await aioredis.create_redis_pool(**kwargs)
File "/usr/local/lib/python3.9/site-packages/aioredis/commands/__init__.py", line 188, in create_redis_pool
pool = await create_pool(address, db=db,
File "/usr/local/lib/python3.9/site-packages/aioredis/pool.py", line 58, in create_pool
await pool._fill_free(override_min=False)
File "/usr/local/lib/python3.9/site-packages/aioredis/pool.py", line 383, in _fill_free
conn = await self._create_new_connection(self._address)
File "/usr/local/lib/python3.9/site-packages/aioredis/connection.py", line 133, in create_connection
await conn.auth(password)
File "/usr/local/lib/python3.9/site-packages/aioredis/util.py", line 52, in wait_ok
res = await fut
Exception Type: ReplyError at /accounts/send-push-message/
Exception Value: WRONGPASS invalid username-password pair or user is disabled.
#285 would be required for this afaict
It looks like it is trying to connect:
File "/usr/local/lib/python3.9/site-packages/aioredis/connection.py", line 133, in create_connection
await conn.auth(password)
File "/usr/local/lib/python3.9/site-packages/aioredis/util.py", line 52, in wait_ok
res = await fut
… but I don't know the details of what's possible or not.
This is a traceback of what is happening locally, which leads me to believe that Channels will need to be upgraded to using #285 as well as a fix to this package.
I'm not seeing the connection 100% from the information given. (Is it clear aioredis doesn't support passwords?)
If so though this is a duplicate of #285.
... as well as a fix to this package.
Is there another fix needed?
... show how you're supposed to configure a Redis host that needs a username and/or password.
I'd rather link to the right place than reproduce the connection docs TBH. It's just a pass-through to the underlying library.
But such a link, sure.
My read is that the underlying library doesn't support it. It was added here, but never made it into an official release afaict. The underlying library is now unsupported/unmaintained, so I think #285 is the blocker.
@ipmb and @carltongibson,
Thank you for looking into this. On a second pass, I can confirm channels-redis is just passing the address on to aioredis. If you put a break on here, you'll see that kwargs
is set to something like the following.
{'minsize': 1, 'maxsize': 1, 'address': 'redis://redis-user-name:some-password@redis:6379'}
Yes, that's true, but it doesn't matter. Even if you passed the username down, aioredis wouldn't accept it or do the right thing with it.