Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Continued) Added support for cert-only login without user and password #498

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ func (auth *AMQPlainAuth) Response() string {
return fmt.Sprintf("LOGIN:%sPASSWORD:%s", auth.Username, auth.Password)
}

// ExternalAuth for RabbitMQ-auth-mechanism-ssl.
type ExternalAuth struct {
}

// Mechanism returns "EXTERNAL"
func (me *ExternalAuth) Mechanism() string {
return "EXTERNAL"
}

// Response returns zero ?
func (me *ExternalAuth) Response() string {
return "\000*\000*"
}

// Finds the first mechanism preferred by the client that the server supports.
func pickSASLMechanism(client []Authentication, serverMechanisms []string) (auth Authentication, ok bool) {
for _, auth = range client {
Expand Down
57 changes: 54 additions & 3 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,50 @@ func DefaultDial(connectionTimeout time.Duration) func(network, addr string) (ne
}
}

// Option type for Dial
type Option func(*Config) error

// SetOptions set amqp connection options
func (a *Config) SetOptions(opts ...Option) error {
for _, opt := range opts {
if err := opt(a); err != nil {
return err
}
}

return nil
}

// TLS is a wrapper for tls.Config to send as a Dial Option
func TLS(val *tls.Config) Option {
return func(t *Config) error {
t.TLSClientConfig = val
return nil
}
}

// Auth is a wrapper for SASL to send as a Dial Option
func Auth(val []Authentication) Option {
return func(t *Config) error {
t.SASL = val
return nil
}
}

// Dial accepts a string in the AMQP URI format and returns a new Connection
// over TCP using PlainAuth. Defaults to a server heartbeat interval of 10
// seconds and sets the handshake deadline to 30 seconds. After handshake,
// deadlines are cleared.
//
// Dial uses the zero value of tls.Config when it encounters an amqps://
// scheme. It is equivalent to calling DialTLS(amqp, nil).
func Dial(url string) (*Connection, error) {
return DialConfig(url, Config{
func Dial(url string, opts ...Option) (*Connection, error) {
config := Config{
Heartbeat: defaultHeartbeat,
Locale: defaultLocale,
})
}
config.SetOptions(opts...)
return DialConfig(url, config)
}

// DialTLS accepts a string in the AMQP URI format and returns a new Connection
Expand All @@ -155,6 +187,25 @@ func DialTLS(url string, amqps *tls.Config) (*Connection, error) {
TLSClientConfig: amqps,
Locale: defaultLocale,
})

}

// DialTLSExternalAuth accepts a string in the AMQP URI format and returns a new
// Connection over TCP using EXTERNAL auth. Defaults to a server heartbeat
// interval of 10 seconds and sets the initial read deadline to 30 seconds.
//
// This mechanism is used, when RabbitMQ is configured for EXTERNAL auth with
// ssl_cert_login plugin for userless/passwordless logons
//
// DialTLS_CertAuth uses the provided tls.Config when encountering an amqps://
// scheme.
func DialTLSExternalAuth(url string, amqps *tls.Config) (*Connection, error) {

return DialConfig(url, Config{
Heartbeat: defaultHeartbeat,
TLSClientConfig: amqps,
SASL: []Authentication{&ExternalAuth{}},
})
}

// DialConfig accepts a string in the AMQP URI format and a configuration for
Expand Down
30 changes: 30 additions & 0 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,36 @@ func ExampleDialTLS() {
log.Printf("conn: %v, err: %v", conn, err)
}

func ExampleDial_withTLSandExternalAuth() {
// This example assumes you enabled the rabbitmq-auth-mechanism-ssl plugin
// and your RabbitMQ server has TLS enabled

// The username will be read from the DN or CN fields of the certificate
// you provide, you can see the more detailed information provided here
// https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl#username-extraction-from-certificate

cfg := new(tls.Config)

// see at the top
cfg.RootCAs = x509.NewCertPool()

if ca, err := ioutil.ReadFile("testca/cacert.pem"); err == nil {
cfg.RootCAs.AppendCertsFromPEM(ca)
}

// Move the client cert and key to a location specific to your application
// and load them here.

if cert, err := tls.LoadX509KeyPair("client/cert.pem", "client/key.pem"); err == nil {
cfg.Certificates = append(cfg.Certificates, cert)
}

// If you don't supply the Auth method as EXTERNAL, connection wouldn't fail and you will be logged in as the guest user.
conn, err := amqp.Dial("amqps://server-name-from-certificate/", amqp.TLS(cfg), amqp.Auth([]amqp.Authentication{&amqp.ExternalAuth{}}))

log.Printf("conn: %v, err: %v", conn, err)
}

func ExampleChannel_Confirm_bridge() {
// This example acts as a bridge, shoveling all messages sent from the source
// exchange "log" to destination exchange "log".
Expand Down