Skip to content

add option to configure approved password authenticators in Cassandra #2093

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

Merged
merged 3 commits into from
Feb 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* `--experimental.distributor.user-subring-size`
* [FEATURE] Added flag `-experimental.ruler.enable-api` to enable the ruler api which implements the Prometheus API `/api/v1/rules` and `/api/v1/alerts` endpoints under the configured `-http.prefix`. #1999
* [FEATURE] Added sharding support to compactor when using the experimental TSDB blocks storage. #2113
* [ENHANCEMENT] Cassandra Authentication: added the `custom_authenticators` config option that allows users to authenticate with cassandra clusters using password authenticators that are not approved by default in [gocql](https://github.com/gocql/gocql/blob/81b8263d9fe526782a588ef94d3fa5c6148e5d67/conn.go#L27) #2093
* [ENHANCEMENT] Experimental TSDB: Export TSDB Syncer metrics from Compactor component, they are prefixed with `cortex_compactor_`. #2023
* [ENHANCEMENT] Experimental TSDB: Added dedicated flag `-experimental.tsdb.bucket-store.tenant-sync-concurrency` to configure the maximum number of concurrent tenants for which blocks are synched. #2026
* [ENHANCEMENT] Experimental TSDB: Expose metrics for objstore operations (prefixed with `cortex_<component>_thanos_objstore_`, component being one of `ingester`, `querier` and `compactor`). #2027
Expand Down
5 changes: 5 additions & 0 deletions docs/configuration/config-file-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,11 @@ cassandra:
# CLI flag: -cassandra.password
[password: <string> | default = ""]

# If set, when authenticating with cassandra a custom authenticator will be
# expected during the handshake. This flag can be set multiple times.
# CLI flag: -cassandra.custom-authenticator
[custom_authenticators: <list of string> | default = ]

# Timeout when connecting to cassandra.
# CLI flag: -cassandra.timeout
[timeout: <duration> | default = 2s]
Expand Down
43 changes: 43 additions & 0 deletions pkg/chunk/cassandra/authenticator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cassandra

import (
"fmt"

"github.com/gocql/gocql"
)

// CustomPasswordAuthenticator provides the default behaviour for Username/Password authentication with
// Cassandra while allowing users to specify a non-default Authenticator to accept.
type CustomPasswordAuthenticator struct {
ApprovedAuthenticators []string
Username string
Password string
}

func (p CustomPasswordAuthenticator) approve(authenticator string) bool {
for _, s := range p.ApprovedAuthenticators {
if authenticator == s {
return true
}
}
return false
}

// Challenge verifies the name of the authenticator and formats the provided username and password
// into a response
func (p CustomPasswordAuthenticator) Challenge(req []byte) ([]byte, gocql.Authenticator, error) {
if !p.approve(string(req)) {
return nil, nil, fmt.Errorf("unexpected authenticator %q", req)
}
resp := make([]byte, 2+len(p.Username)+len(p.Password))
resp[0] = 0
copy(resp[1:], p.Username)
resp[len(p.Username)+1] = 0
copy(resp[2+len(p.Username):], p.Password)
return resp, nil, nil
}

// Success returns nil by default, identical to the default PasswordAuthenticator
func (p CustomPasswordAuthenticator) Success(data []byte) error {
return nil
}
45 changes: 28 additions & 17 deletions pkg/chunk/cassandra/storage_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,29 @@ import (

"github.com/cortexproject/cortex/pkg/chunk"
"github.com/cortexproject/cortex/pkg/chunk/util"
"github.com/cortexproject/cortex/pkg/util/flagext"
)

// Config for a StorageClient
type Config struct {
Addresses string `yaml:"addresses,omitempty"`
Port int `yaml:"port,omitempty"`
Keyspace string `yaml:"keyspace,omitempty"`
Consistency string `yaml:"consistency,omitempty"`
ReplicationFactor int `yaml:"replication_factor,omitempty"`
DisableInitialHostLookup bool `yaml:"disable_initial_host_lookup,omitempty"`
SSL bool `yaml:"SSL,omitempty"`
HostVerification bool `yaml:"host_verification,omitempty"`
CAPath string `yaml:"CA_path,omitempty"`
Auth bool `yaml:"auth,omitempty"`
Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"`
Timeout time.Duration `yaml:"timeout,omitempty"`
ConnectTimeout time.Duration `yaml:"connect_timeout,omitempty"`
Retries int `yaml:"max_retries"`
MaxBackoff time.Duration `yaml:"retry_max_backoff"`
MinBackoff time.Duration `yaml:"retry_min_backoff"`
Addresses string `yaml:"addresses,omitempty"`
Port int `yaml:"port,omitempty"`
Keyspace string `yaml:"keyspace,omitempty"`
Consistency string `yaml:"consistency,omitempty"`
ReplicationFactor int `yaml:"replication_factor,omitempty"`
DisableInitialHostLookup bool `yaml:"disable_initial_host_lookup,omitempty"`
SSL bool `yaml:"SSL,omitempty"`
HostVerification bool `yaml:"host_verification,omitempty"`
CAPath string `yaml:"CA_path,omitempty"`
Auth bool `yaml:"auth,omitempty"`
Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"`
CustomAuthenticators flagext.StringSlice `yaml:"custom_authenticators"`
Timeout time.Duration `yaml:"timeout,omitempty"`
ConnectTimeout time.Duration `yaml:"connect_timeout,omitempty"`
Retries int `yaml:"max_retries"`
MaxBackoff time.Duration `yaml:"retry_max_backoff"`
MinBackoff time.Duration `yaml:"retry_min_backoff"`
}

// RegisterFlags adds the flags required to config this to the given FlagSet
Expand All @@ -49,6 +51,7 @@ func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&cfg.Auth, "cassandra.auth", false, "Enable password authentication when connecting to cassandra.")
f.StringVar(&cfg.Username, "cassandra.username", "", "Username to use when connecting to cassandra.")
f.StringVar(&cfg.Password, "cassandra.password", "", "Password to use when connecting to cassandra.")
f.Var(&cfg.CustomAuthenticators, "cassandra.custom-authenticator", "If set, when authenticating with cassandra a custom authenticator will be expected during the handshake. This flag can be set multiple times.")
f.DurationVar(&cfg.Timeout, "cassandra.timeout", 2*time.Second, "Timeout when connecting to cassandra.")
f.DurationVar(&cfg.ConnectTimeout, "cassandra.connect-timeout", 5*time.Second, "Initial connection timeout, used during initial dial to server.")
f.IntVar(&cfg.Retries, "cassandra.max-retries", 0, "Number of retries to perform on a request. (Default is 0: no retries)")
Expand Down Expand Up @@ -107,6 +110,14 @@ func (cfg *Config) setClusterConfig(cluster *gocql.ClusterConfig) {
}
}
if cfg.Auth {
if len(cfg.CustomAuthenticators) != 0 {
cluster.Authenticator = CustomPasswordAuthenticator{
ApprovedAuthenticators: cfg.CustomAuthenticators,
Username: cfg.Username,
Password: cfg.Password,
}
return
}
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: cfg.Username,
Password: cfg.Password,
Expand Down