Skip to content

Conversation

jlledom
Copy link
Contributor

@jlledom jlledom commented Aug 19, 2025

When having some sentinels and a master behind them, and the master is protected by a password including some special characters, the client is not able to connect to master, the error is: bad password component

I investigated and this happens because the sentinels client calls Enpoint#for:

return Endpoint.for(nil, address[0], port: address[1], **options) if address

which ends up sending the credentials to a new URI::Generic instance:

userinfo: credentials&.join(":"),

This results in the above password error unless the password is properly URL encoded, however, in my local tests, Redis will reject the password as incorrect when receiving it encoded. To solve this I think the best solution is to not send the credentials to the URI instance at all, instead, send the credentials directly to the Endpoint initializer, that way there's no need to encode the password and Redis accepts it.

This works for me. Here is the script I use to make my tests locally:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  #gem 'async-redis'
  gem 'async-redis', git: 'https://github.com/jlledom/async-redis', branch: 'jlledom-sentinel-master-credentials'
end

def main
  Async do
    sentinels = [{ host: 'localhost', port: 56380 },
                 { host: 'localhost', port: 56381 },
                 { host: 'localhost', port: 56382 }]

    ssl_params = {
      ca_file: '/path/to/ca-root-cert.pem',
    }
    ssl_context = OpenSSL::SSL::SSLContext.new.tap do |context|
      context.set_params(ssl_params)
    end

    database = '6'
    credentials = %w[username secret#Passw0rd]
    master_options = {database:, credentials:, ssl_context: }.compact

    sentinel_credentials = %w[sentinel Passw0rd]
    master_name = 'redis-master'
    role = :master

    endpoints = sentinels.map do |sentinel|
      scheme = ssl_context ? 'rediss' : 'redis'
      host = sentinel[:host]
      port = sentinel[:port]
      sentinel_uri = URI::Generic.build(scheme:, host:, port:)
      Async::Redis::Endpoint.new(sentinel_uri, nil, credentials: sentinel_credentials, ssl_context:)
    end
    client = Async::Redis::SentinelClient.new(endpoints, master_name:, master_options:, role:)

    while true
      begin
        sleep 1
        result = client.ping 'test'
        puts result
      rescue StandardError => e
        puts e.message
        retry
      end
    end
  end
end

main

Types of Changes

  • Bug fix.

Contribution

@jlledom jlledom changed the title Sentinels: Fix password encoding for master Sentinels: Fix password with specials characters for master Aug 19, 2025
@jlledom jlledom changed the title Sentinels: Fix password with specials characters for master Sentinels: Fix password with special characters for master Aug 19, 2025
@ioquatix
Copy link
Member

Are you able to fix the tests?

@jlledom
Copy link
Contributor Author

jlledom commented Aug 20, 2025

I think I did: 53808c7

@ioquatix ioquatix merged commit 842756e into socketry:main Aug 20, 2025
18 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants