From ad26b0d27f55c40ddb1b37e7f332d773a001482d Mon Sep 17 00:00:00 2001 From: Andy Tolbert <6889771+tolbertam@users.noreply.github.com> Date: Thu, 22 Aug 2024 22:49:15 -0500 Subject: [PATCH] Don't restrict server authenticator in PasswordAuthenticator Currently gocql will only allow authenticating with authenticators defined in defaultApprovedAuthenticators in conn.go. There have been multiple occurrences of implementers needing to update this list, either when a vendor would like to add their authenticator, or a new authenticator being added. It would probably reduce friction to just accept any authenticator provided by the server. From what I know, other drivers behave in this way. If a user wanted to restrict this, they could use the existing configuration PasswordAuthenticator.AllowedAuthenticators. patch by Andy Tolbert; reviewed by Joao Reis, Lukasz Antoniak for CASSGO-19 --- conn.go | 30 +++++++++++------------------- conn_test.go | 27 +++++++++++++++------------ doc.go | 10 ++++++++++ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/conn.go b/conn.go index 3daca6250..ae02bd71c 100644 --- a/conn.go +++ b/conn.go @@ -43,25 +43,11 @@ import ( "github.com/gocql/gocql/internal/streams" ) -var ( - defaultApprovedAuthenticators = []string{ - "org.apache.cassandra.auth.PasswordAuthenticator", - "com.instaclustr.cassandra.auth.SharedSecretAuthenticator", - "com.datastax.bdp.cassandra.auth.DseAuthenticator", - "io.aiven.cassandra.auth.AivenAuthenticator", - "com.ericsson.bss.cassandra.ecaudit.auth.AuditPasswordAuthenticator", - "com.amazon.helenus.auth.HelenusAuthenticator", - "com.ericsson.bss.cassandra.ecaudit.auth.AuditAuthenticator", - "com.scylladb.auth.SaslauthdAuthenticator", - "com.scylladb.auth.TransitionalAuthenticator", - "com.instaclustr.cassandra.auth.InstaclustrPasswordAuthenticator", - } -) - -// approve the authenticator with the list of allowed authenticators or default list if approvedAuthenticators is empty. +// approve the authenticator with the list of allowed authenticators. If the provided list is empty, +// the given authenticator is allowed. func approve(authenticator string, approvedAuthenticators []string) bool { if len(approvedAuthenticators) == 0 { - approvedAuthenticators = defaultApprovedAuthenticators + return true } for _, s := range approvedAuthenticators { if authenticator == s { @@ -86,9 +72,15 @@ type Authenticator interface { Success(data []byte) error } +// PasswordAuthenticator specifies credentials to be used when authenticating. +// It can be configured with an "allow list" of authenticator class names to avoid +// attempting to authenticate with Cassandra if it doesn't provide an expected authenticator. type PasswordAuthenticator struct { - Username string - Password string + Username string + Password string + // Setting this to nil or empty will allow authenticating with any authenticator + // provided by the server. This is the default behavior of most other driver + // implementations. AllowedAuthenticators []string } diff --git a/conn_test.go b/conn_test.go index cab4c2f8f..6cf062d95 100644 --- a/conn_test.go +++ b/conn_test.go @@ -55,18 +55,21 @@ const ( func TestApprove(t *testing.T) { tests := map[bool]bool{ - approve("org.apache.cassandra.auth.PasswordAuthenticator", []string{}): true, - approve("com.instaclustr.cassandra.auth.SharedSecretAuthenticator", []string{}): true, - approve("com.datastax.bdp.cassandra.auth.DseAuthenticator", []string{}): true, - approve("io.aiven.cassandra.auth.AivenAuthenticator", []string{}): true, - approve("com.amazon.helenus.auth.HelenusAuthenticator", []string{}): true, - approve("com.ericsson.bss.cassandra.ecaudit.auth.AuditAuthenticator", []string{}): true, - approve("com.scylladb.auth.SaslauthdAuthenticator", []string{}): true, - approve("com.scylladb.auth.TransitionalAuthenticator", []string{}): true, - approve("com.instaclustr.cassandra.auth.InstaclustrPasswordAuthenticator", []string{}): true, - approve("com.apache.cassandra.auth.FakeAuthenticator", []string{}): false, - approve("com.apache.cassandra.auth.FakeAuthenticator", nil): false, - approve("com.apache.cassandra.auth.FakeAuthenticator", []string{"com.apache.cassandra.auth.FakeAuthenticator"}): true, + approve("org.apache.cassandra.auth.PasswordAuthenticator", []string{}): true, + approve("org.apache.cassandra.auth.MutualTlsWithPasswordFallbackAuthenticator", []string{}): true, + approve("org.apache.cassandra.auth.MutualTlsAuthenticator", []string{}): true, + approve("com.instaclustr.cassandra.auth.SharedSecretAuthenticator", []string{}): true, + approve("com.datastax.bdp.cassandra.auth.DseAuthenticator", []string{}): true, + approve("io.aiven.cassandra.auth.AivenAuthenticator", []string{}): true, + approve("com.amazon.helenus.auth.HelenusAuthenticator", []string{}): true, + approve("com.ericsson.bss.cassandra.ecaudit.auth.AuditAuthenticator", []string{}): true, + approve("com.scylladb.auth.SaslauthdAuthenticator", []string{}): true, + approve("com.scylladb.auth.TransitionalAuthenticator", []string{}): true, + approve("com.instaclustr.cassandra.auth.InstaclustrPasswordAuthenticator", []string{}): true, + approve("com.apache.cassandra.auth.FakeAuthenticator", []string{}): true, + approve("com.apache.cassandra.auth.FakeAuthenticator", nil): true, + approve("com.apache.cassandra.auth.FakeAuthenticator", []string{"com.apache.cassandra.auth.FakeAuthenticator"}): true, + approve("com.apache.cassandra.auth.FakeAuthenticator", []string{"com.apache.cassandra.auth.NotFakeAuthenticator"}): false, } for k, v := range tests { if k != v { diff --git a/doc.go b/doc.go index f23e812c5..236b55e2f 100644 --- a/doc.go +++ b/doc.go @@ -81,6 +81,16 @@ // } // defer session.Close() // +// By default, PasswordAuthenticator will attempt to authenticate regardless of what implementation the server returns +// in its AUTHENTICATE message as its authenticator, (e.g. org.apache.cassandra.auth.PasswordAuthenticator). If you +// wish to restrict this you may use PasswordAuthenticator.AllowedAuthenticators: +// +// cluster.Authenticator = gocql.PasswordAuthenticator { +// Username: "user", +// Password: "password" +// AllowedAuthenticators: []string{"org.apache.cassandra.auth.PasswordAuthenticator"}, +// } +// // # Transport layer security // // It is possible to secure traffic between the client and server with TLS.