django / channels_redis

Redis channel layer backend for Django Channels

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.

OK thanks @ipmb — let's close in favour of #285.

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

@ipmb, right. I saw your comments on #285.