redis / node-redis

Redis Node.js client
https://redis.js.org/
MIT License
16.89k stars 1.88k forks source link

TypeError when "DENIED Redis is running in protected mode ..." #2417

Open JohnnyRacer opened 1 year ago

JohnnyRacer commented 1 year ago

Description

I am getting a very strange type error everytime I try to use docker compose up to launch any script that connects to redis with the following error message:

redis-test /node_modules/@redis/client/dist/lib/client/commands-queue.js:62
redis-test                 const { resolve, reject } = __classPrivateFieldGet(this, _RedisCommandsQueue_waitingForReply, "f").shift();
redis-test                         ^
redis-test 
redis-test TypeError: Cannot destructure property 'resolve' of '__classPrivateFieldGet(...).shift(...)' as it is undefined.
redis-test     at Object.onReply (/node_modules/@redis/client/dist/lib/client/commands-queue.js:62:25)
redis-test     at RESP2Decoder.write (/node_modules/@redis/client/dist/lib/client/RESP2/decoder.js:119:26)
redis-test     at RedisCommandsQueue.onReplyChunk (/node_modules/@redis/client/dist/lib/client/commands-queue.js:154:72)
redis-test     at RedisSocket.<anonymous> (/node_modules/@redis/client/dist/lib/client/index.js:388:84)
redis-test     at RedisSocket.emit (node:events:512:28)
redis-test     at Socket.<anonymous> (/node_modules/@redis/client/dist/lib/client/socket.js:202:42)
redis-test     at Socket.emit (node:events:512:28)
redis-test     at addChunk (node:internal/streams/readable:324:12)
redis-test     at readableAddChunk (node:internal/streams/readable:297:9)
redis-test     at Readable.push (node:internal/streams/readable:234:10)
redis-test 
redis-test Node.js v19.6.1
redis-test exited with code 1

This error does not happen when I connect to redis using the node.js REPL without docker.

This is the docker-compose.yml file I am using to create the redis container:

  redis:
    image: redis/redis-stack:latest
    ports:
      - 6379:6379
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis-data:/data

And the code I am using in node.js to connect to redis


import { createClient } from "redis";
(async function () {
    const rdb = createClient({
        url: 'redis://redis:6379'
        });

    await rdb.connect();
})();

Node.js Version

19.6.1

Redis Server Version

6.2.8

Node Redis Version

4.6.4

Platform

Linux

Logs

No response

leibale commented 1 year ago

@JohnnyRacer as far as I can tell, this error means that the client got a reply from redis-server, but there are no commands in the queue, which is indeed very strange.

  1. Are you using the client? executing any commands?
  2. Does the error throws immediately?
  3. Can you please add console.log(reply); before node_modules/@redis/client/dist/lib/client/commands-queue.js:62?

If you want, I'll be happy to debug it with you, let me know.. :)

JohnnyRacer commented 1 year ago

@JohnnyRacer as far as I can tell, this error means that the client got a reply from redis-server, but there are no commands in the queue, which is indeed very strange.

1. Are you using the client? executing any commands?

2. Does the error throws immediately?

3. Can you please add `console.log(reply);` before `node_modules/@redis/client/dist/lib/client/commands-queue.js:62`?

If you want, I'll be happy to debug it with you, let me know.. :)

@leibale

  1. This error occurs before any commands are sent to the client, this happens whenever await client.connect() is called.
  2. Yes.
  3. With console.log(reply) before node_modules/@redis/client/dist/lib/client/commands-queue.js:62.
    [ErrorReply: DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.]
JohnnyRacer commented 1 year ago

@leibale

Seems like node-redis does not throw a clear exception when --protected-mode no and a password is not set, I just set this flag in my docker-compose.yml file and now it connects fine with no errors. Not sure if this has any implications for security in production though since I am only accessing redis from my local network, but for testing/development it works fine.

leibale commented 1 year ago

@JohnnyRacer thanks for the information, I'll investigate that. Glad you found a workaround :)

dtikhonov-iex commented 1 year ago

@leibale I ran into this issue as well. The Redis server will send -DENIED or -ERR in some circumstances unconditionally and close the connection: See acceptCommonHandler() in https://github.com/redis/redis/blob/e7a3d3d152c251cc25aed3d89a47a525812e72de/src/networking.c#L1283

I think the library should be able to handle this.

leibale commented 1 year ago

@dtikhonov-iex the library should be able to handle these errors, that's why the issue is still open.. :)

hosein commented 1 year ago

I have found another instance of this happening when using legacyMode: true:

When using pub/subs in legacy mode, be sure to use the v4 functions.

In legacyMode, this will throw the same error as OP.

const heartbeat = redis_client.duplicate();
heartbeat.on('error', err => handleError(err));
heartbeat.connect()
.then(() => {
    const listener = (incoming) => {
        doListenerThings(incoming)
    }
    heartbeat.subscribe('key:key:key', listener);
})
.catch(err => handleError(err))

This works fine.

const heartbeat = redis_client.duplicate();
heartbeat.on('error', err => handleError(err));
heartbeat.connect()
.then(() => {
    const listener = (incoming) => {
        doListenerThings(incoming)
    }
    //THIS IS THE DIFFERENCE
    heartbeat.v4.subscribe('key:key:key', listener);
})
.catch(err => handleError(err))
benjaminaronsson34 commented 11 months ago

Adding this here incase more information is wanted:

Running this using the latest version of node 18 seems to resolve the issue, where as the latest LTS seems to cause issue. Recently migrated a program using this package to node 20 and we started finding this issue everytime we tried to start it. After reverting it back to node 18, it was fine again.

Also using redis-stack-server in a docker container

fsepulveda commented 11 months ago

Hi, same issue with nodejs version 18.17.1

TypeError: Cannot destructure property 'resolve' of '__classPrivateFieldGet(...).shift(...)' as it is undefined.
    at Object.onReply (/opt/def/def-puresocial-services/node_modules/@redis/client/dist/lib/client/commands-queue.js:62:25)
    at RESP2Decoder.write (/opt/def/def-puresocial-services/node_modules/@redis/client/dist/lib/client/RESP2/decoder.js:119:26)
    at RedisCommandsQueue.onReplyChunk (/opt/def/def-puresocial-services/node_modules/@redis/client/dist/lib/client/commands-queue.js:154:72)
    at RedisSocket.<anonymous> (/opt/def/def-puresocial-services/node_modules/@redis/client/dist/lib/client/index.js:394:84)
    at RedisSocket.emit (node:events:514:28)
    at Socket.<anonymous> (/opt/def/def-puresocial-services/node_modules/@redis/client/dist/lib/client/socket.js:201:42)
    at Socket.emit (node:events:514:28)
    at addChunk (node:internal/streams/readable:324:12)
    at readableAddChunk (node:internal/streams/readable:297:9)
    at Readable.push (node:internal/streams/readable:234:10)

Node.js v18.17.1

Version of redis package is 4.6.10

ZhoufangErqiangu commented 10 months ago

i got this problem. then i found this only happens when connect to remote redis.

i try redis-cli, got (error) DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients.

so this problem may not be this package's issue, but it is still makes people confuse.

mriabchenko commented 10 months ago

node 18.15.0, "cache-manager-redis-store": "^3.0.1", It happens when using remote redis while local development. After a couple of minutes this error appears: `/Users/user/WebstormProjects/monolytics/node_modules/@redis/client/dist/lib/client/socket.js:194 __classPrivateFieldGet(this, _RedisSocket_instances, "m", _RedisSocket_onSocketError).call(this, new errors_1.SocketClosedUnexpectedlyError()); ^

Error: Socket closed unexpectedly at TLSSocket. (/Users/user/WebstormProjects/monolytics/node_modules/@redis/client/dist/lib/client/socket.js:194:118) at Object.onceWrapper (node:events:628:26) at TLSSocket.emit (node:events:525:35) at TLSSocket.emit (node:domain:489:12) at node:net:322:12 at TCP.done (node:_tls_wrap:588:7)`

rsbmkJ commented 10 months ago

I have found another instance of this happening when using legacyMode: true:

When using pub/subs in legacy mode, be sure to use the v4 functions.

In legacyMode, this will throw the same error as OP.

const heartbeat = redis_client.duplicate();
heartbeat.on('error', err => handleError(err));
heartbeat.connect()
.then(() => {
    const listener = (incoming) => {
        doListenerThings(incoming)
    }
    heartbeat.subscribe('key:key:key', listener);
})
.catch(err => handleError(err))

This works fine.

const heartbeat = redis_client.duplicate();
heartbeat.on('error', err => handleError(err));
heartbeat.connect()
.then(() => {
    const listener = (incoming) => {
        doListenerThings(incoming)
    }
    //THIS IS THE DIFFERENCE
    heartbeat.v4.subscribe('key:key:key', listener);
})
.catch(err => handleError(err))

Thanks for your response, 🙌 it's work for me ❤️

botondev commented 8 months ago

I have the same issue, using it as described on the socket.io Redis adapter page. I use the import { createAdapter } from '@socket.io/redis-adapter',

nillock-error commented 4 months ago

This occurs in my case when the client is waiting for the server to start up for the first time. As the queue returns undefined because there is nothing on the queue yet.

Hacking in a solution into the transpiled javascript code, I've come to the following fix. In @redis/client/dist/lib/client/commands-queue.js of my node modules directory.

I replace

const { resolve, reject } = __classPrivateFieldGet(this, _RedisCommandsQueue_waitingForReply, "f").shift()

with

const { resolve, reject } = __classPrivateFieldGet(this, _RedisCommandsQueue_waitingForReply, "f").shift() || {resolve:()=>{}, reject:()=>{}};

and this solves my issue.

Again this is a hack. The real issue stems from the non-null assertion operator found here in the source code. https://github.com/redis/node-redis/blob/d5355d43275fedf1b2afc4db8a926f72b05f79c5/packages/client/lib/client/commands-queue.ts#L90

UPDATE

Looks like this PR has a fix for the issue #2549

NBMSacha commented 4 months ago

Had to hack this also. Would be great to have lib support.