gorabbitmqsaslqpid-proton

Using SASL EXTERNAL in Go using qpid-proton client library to connect to AMQP 1.0 RabbitMQ


I am trying to make a TLS connection to RabbitMQ with authentication provided by self-signed certificates through the SASL EXTERNAL mechanism using the golang implementation provided by https://github.com/apache/qpid-proton. The goal is to be able to connect to RabbitMQ without specifying the username and password in the URI.

RabbitMQ is running with the following configuration:

      auth_mechanisms.1 = EXTERNAL
      auth_mechanisms.2 = PLAIN
      auth_mechanisms.3 = AMQPLAIN

and plugins:

I have confirmed that I am able to connect with SASL EXTERNAL using a Node.js library (https://github.com/amqp/rhea) and I have confirmed that connecting with PLAIN and ANONYMOUS works with Go in the qpid-proton library but have been unable to connect with SASL EXTERNAL with Go.

My client code does not return any errors, but the RabbitMQ error logs tell me that the client closed the TCP connection

2021-06-24 18:57:22.029 [info] <0.16358.106> accepting AMQP connection <0.16358.106> (127.0.0.1:50610 -> 127.0.0.1:5671)
2021-06-24 18:57:23.030 [warning] <0.16358.106> closing AMQP connection <0.16358.106> (127.0.0.1:50610 -> 127.0.0.1:5671):
client unexpectedly closed TCP connection

My client code is as follows:

package main

import (
        "fmt"
        "github.com/apache/qpid-proton/go/pkg/amqp"
        "github.com/apache/qpid-proton/go/pkg/electron"
        "os"
        "crypto/tls"
        "io/ioutil"
        "crypto/x509"
        "time"
)

func main() {
        keyPair, err := tls.LoadX509KeyPair("client.crt", "client.key")

        if err != nil {
                fmt.Println("Failed to load certificate:", err)
                os.Exit(1)
        }

        rootCa, err := ioutil.ReadFile("rootCA.crt")
        if err != nil {
                fmt.Println("Failed to read root CA:", err)
                os.Exit(1)
        }
        certPool := x509.NewCertPool()
        certPool.AppendCertsFromPEM(rootCa)

        tlsConfig := &tls.Config{
                RootCAs: certPool,
                InsecureSkipVerify: true,
                Certificates: []tls.Certificate{keyPair},
        }

        container := electron.NewContainer("myContainer")

        tlsConn, err := tls.Dial("tcp", "rabbitmq.default.svc.cluster.local:5671", tlsConfig)
        if err != nil {
                fmt.Println("Failed to open TLS connection:", err)
                os.Exit(1)
        }
        defer tlsConn.Close()

        conn, err := container.Connection(
                tlsConn,
                electron.SASLEnable(),
                electron.SASLAllowedMechs("EXTERNAL"),
        )
        defer conn.Close(err)

        if err != nil {
                fmt.Println("Failed to open AMQP connection", err)
                os.Exit(1)
        }

        sess, err := conn.Session()

        sender, err := sess.Sender(electron.Target("demo-queue"))

        if err != nil {
                fmt.Println("Creating sender failed:", err)
                os.Exit(1)
        }

        for i := int64(0); i < 100000 ; i++ {
                msg := amqp.NewMessage()
                body := fmt.Sprintf("Test message %d", i)
                msg.Marshal(body)
                sender.SendSync(msg)
                time.Sleep(1*time.Second)
        }
}

Solution

  • This isn't a solution for using the qpid-proton client library but I ended up using https://github.com/Azure/go-amqp to connect to RabbitMQ through SASL EXTERNAL. This library recently had the functionality added.