vibe-d / vibe.d

Official vibe.d development
MIT License
1.15k stars 284 forks source link

A few problems with Redis authentication #2631

Open wusikijeronii opened 2 years ago

wusikijeronii commented 2 years ago

I have two problems with connecting to Redis using password authentication.

  1. RedisSessionStore. Today I tried to initialize RedisSessionStore according to this API, but this API seems doesn't support password authentication. If so please add the feature.
  2. connectRedisDB I used this manual to connect to my Redis server. But if I use:
    auto redisDB = connectRedisDB("redis://password:localhost/3?maxmemory=10000000");

    I get the error:

    function vibe.db.redis.redis.connectRedisDB(URL url) is not callable using argument types (string) cannot pass argument "redis://password:localhost/3?maxmemory=10000000" of type string to parameter URL url

If I use:

auto redisDB = connectRedisDB(URL("redis://password:localhost/3?maxmemory=10000000"));

I get the error:

std.conv.ConvException@/usr/include/dlang/dmd/std/conv.d(2500): Unexpected 'l' when converting from type string to type uint
----------------
/usr/include/dlang/dmd/std/conv.d:85 pure @safe std.typecons.Tuple!(uint, "data", ulong, "count").Tuple std.conv.parse!(uint, immutable(char)[], 1).parse(ref immutable(char)[]) [0x559f06e04839]
/usr/include/dlang/dmd/std/conv.d:2389 pure @safe ushort std.conv.parse!(ushort, immutable(char)[], 0).parse(ref immutable(char)[]) [0x559f06e0462c]
/usr/include/dlang/dmd/std/conv.d:1970 pure @safe ushort std.conv.toImpl!(ushort, immutable(char)[]).toImpl(immutable(char)[]) [0x559f06dfe630]
/usr/include/dlang/dmd/std/conv.d:224 pure @safe ushort std.conv.to!(ushort).to!(immutable(char)[]).to(immutable(char)[]) [0x559f06dfe60f]
../../../.dub/packages/vibe-d-0.9.4/vibe-d/inet/vibe/inet/url.d:184 pure @safe long vibe.inet.url.URL.__ctor(immutable(char)[]).findPort(immutable(char)[]) [0x559f06f8b436]
../../../.dub/packages/vibe-d-0.9.4/vibe-d/inet/vibe/inet/url.d:199 ref @safe vibe.inet.url.URL vibe.inet.url.URL.__ctor(immutable(char)[]) [0x559f06f8b109]
source/app.d:45 _Dmain [0x559f06d3bc8a

Seems URL thinks localhost is a requested port and tries to convert the host to a port.

P.S.: working code (some override for redis.sessionstore module):

    this(string host, long database, ushort port = RedisClient.defaultPort, string password = string.init)
    {
        URL redisURL = URL("redis", host, port, InetPath(to!string(database)));
        if (password != string.init)
            redisURL.password = password;
        m_db = connectRedisDB(redisURL);
    }
Geod24 commented 2 years ago
auto redisDB = connectRedisDB(URL("redis://password:localhost/3?maxmemory=10000000"));

Shouldn't that be:

auto redisDB = connectRedisDB(URL("redis://:password@localhost/3?maxmemory=10000000"));
wusikijeronii commented 2 years ago
auto redisDB = connectRedisDB(URL("redis://password:localhost/3?maxmemory=10000000"));

Shouldn't that be:

auto redisDB = connectRedisDB(URL("redis://:password@localhost/3?maxmemory=10000000"));

Thank you, but I have tried this way. My code:

auto url = URL("redis://test@localhost/1");
logInfo("Password is %s",url.password);
assert(url.password == "test");

Output:

[main(----) INF] Password is 
core.exception.AssertError@source/app.d(43): Assertion failure

url.password is the uninitialized value. If I add the line before assertions check I will get no error:

url.password = "test"
Geod24 commented 2 years ago

Look at the example again: You need to have a column before the password (to indicate that the username is empty). This is not Vibe.d specific BTW, it's the standard URL syntax.

wusikijeronii commented 2 years ago
auto redisDB = connectRedisDB(URL("redis://:password@localhost/3?maxmemory=10000000"));

Ok. Copied your code. I get the error:

undefinedobject.Exception@../../../.dub/packages/vibe-d-0.9.4/vibe-d/inet/vibe/inet/url.d(174): Empty user name in URL.

If I use (with space):

auto redisDB = connectRedisDB(URL("redis:// :password@localhost/3"));

I get the error:

undefinedobject.Exception@../../../.dub/packages/vibe-d-0.9.4/vibe-d/redis/vibe/db/redis/redis.d(1469): WRONGPASS invalid username-password pair or user is disabled.

How can I skip check about empty user ?

Geod24 commented 2 years ago

Actually, you can't. I went and checked the RFC, and it says explicitly:

there is no way to specify a password without specifying a user name

RFC1738

I'm not familiar with redis myself, but googling this looks like a common issue: https://stackoverflow.com/questions/44344628/how-to-create-a-redis-cloud-connection-url-with-an-auth-password/44353927

It however looks like other URL parsers are more permissive and accept no username. CC @s-ludwig .

wusikijeronii commented 2 years ago

Actually, you can't. I went and checked the RFC, and it says explicitly:

there is no way to specify a password without specifying a user name

RFC1738

I'm not familiar with redis myself, but googling this looks like a common issue: https://stackoverflow.com/questions/44344628/how-to-create-a-redis-cloud-connection-url-with-an-auth-password/44353927

It however looks like other URL parsers are more permissive and accept no username. CC @s-ludwig .

Ok. I can set up ACL (user & password) in my Redis instance. Using ACL in my web application is suitable for me. But if Redis supports the feature to connect to DB without a username the library for working with Redis also has to support this feature. I'm not a vibe-d developer but I think modifying the URL parser is a bad idea 'cos I can't imagine cases no username requires except Redis. I think more an easy, secure, and efficient way - write a correct parser for Redis only. It's a not hard task. The example in the first message in this topic does this in just 4 lines of code.

wusikijeronii commented 2 years ago

Today I've installed a fresh Redis instance in the new node in my home cluster and when I tried to login after failure the redis-cli gave me a tip. The default user is default. Working code:

settings.sessionStore = new RedisSessionStore("default:password@localhost", 1);
final class RedisSessionStore : SessionStore {
.....
this(string host, ulong database)
    {
        m_db = connectRedisDB(URL("redis://" ~ host ~ "/" ~ to!string(database)));
    }
.....
}

But class RedisSessionStore uses connectRedis instead of connectRedisDB and I can't init session store 'cos I can't set a password

vajrabisj commented 2 years ago

Yes, I just added the same issue.

you can connect by first rc = new RedisClient(host,port), and then rc.auth(password). This is success. But no way for RedisSessionstore.