rebus-org / Rebus.RabbitMq

:bus: RabbitMQ transport for Rebus
https://mookid.dk/category/rebus
Other
62 stars 44 forks source link

BrokerUnreachableException when passwords are Url-encoded #93

Closed brentby closed 2 years ago

brentby commented 2 years ago

This one was hard to diagnose - as I also ran into the same issues reported in #80 when attempting to connect to AmazonMQ - but it should be fairly easy to fix.

The problem is that you'll get the following error when connecting with a password that should be URL-encoded because it has special characters:

RabbitMQ.Client.Exceptions.BrokerUnreachableException
  HResult=0x80131620
  Message=None of the specified endpoints were reachable
  Source=RabbitMQ.Client
  StackTrace:
   at RabbitMQ.Client.ConnectionFactory.CreateConnection(IEndpointResolver endpointResolver, String clientProvidedName)
   at RabbitMQ.Client.ConnectionFactory.CreateConnection(String clientProvidedName)
   at RabbitMQ.Client.ConnectionFactory.CreateConnection()
   at TestRabbitConn.Program.Main(String[] args) in C:\Users\brent\source\repos\TestRabbitConn\Program.cs:line 29

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.

The issue is that the caller SHOULD URL-encode the password when constructing the amqp URI, but the ConnectionManager fails to decode it when parsing the URI in both constructors:

  if (!string.IsNullOrWhiteSpace(uri.UserInfo))
        {
            var parts = uri.UserInfo.Split(':');
            var username = parts.First();
            var password = parts.LastOrDefault() ?? "";
            _connectionFactory.UserName = username;
            _connectionFactory.Password = password;  // the encoded password is passed, NOT the decoded (raw) password!
        }

I wrote a test program using the raw RabbitMQ.Client library and confirmed the behavior. This sample assumes a test virtual host, test_user for username, and p@ssword for a password. See:


        static void ConnectWithUri()
        {
            ConnectionFactory factory = new ConnectionFactory()
            {
                AutomaticRecoveryEnabled = true,
                NetworkRecoveryInterval = TimeSpan.FromSeconds(30),
                Uri = new Uri("amqp://test_user:p%40ssword@localhost/test")
            };

            IConnection conn = factory.CreateConnection();
            var channel = conn.CreateModel();

            try
            {
                //...
            }
            finally
            {
                channel.Close();
                conn.Close();
            }
        }

        static void ConnectWithProps()
        {
            ConnectionFactory factory = new ConnectionFactory()
            {
                AutomaticRecoveryEnabled = true,
                NetworkRecoveryInterval = TimeSpan.FromSeconds(30),
                HostName = "localhost",
                Port = 5672,
                VirtualHost = "test",
                UserName = "test_user",
                Password = "p@ssword"  // this works
                //Password = "p%40ssword"  // this FAILs
            };

            IConnection conn = factory.CreateConnection();
            var channel = conn.CreateModel();

            try
            {
                //...
            }
            finally
            {
                channel.Close();
                conn.Close();
            }
        }

A temporary work-around is to validate passwords to ensure that they don't require URL-encoding, but ideally this will be solved as the special characters are good practice for strong passwords.

mookid8000 commented 2 years ago

Wouldn't a permanent solution simply be for Rebus to URL decode values it had picked out of the URL manually?

brentby commented 2 years ago

Yes that's correct, the fix is very simple. The permanent solution is to URL-decode the values it had picked out of the URL manually.

I mention the workaround as it may be helpful to anyone that wants to wait for the official Nuget fix.

mookid8000 commented 2 years ago

I think I've fixed this issue in Rebus.RabbitMq 7.3.5, which is out on NuGet.org now 🙂

Thanks for reporting this issue! 👍