diff --git a/CODEOWNERS b/CODEOWNERS index 04ebb5831..c86a4e0aa 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -10,7 +10,6 @@ /builtin/credential/okta/ @hashicorp/vault-ecosystem # Secrets engines (pki, ssh, totp and transit omitted) -/builtin/logical/cassandra/ @hashicorp/vault-ecosystem /builtin/logical/consul/ @hashicorp/vault-ecosystem /builtin/logical/database/ @hashicorp/vault-ecosystem /builtin/logical/mysql/ @hashicorp/vault-ecosystem diff --git a/Makefile b/Makefile index b55ec0445..af12f50d0 100644 --- a/Makefile +++ b/Makefile @@ -307,9 +307,6 @@ mysql-database-plugin: mysql-legacy-database-plugin: @CGO_ENABLED=0 $(GO_CMD) build -o bin/mysql-legacy-database-plugin ./plugins/database/mysql/mysql-legacy-database-plugin -cassandra-database-plugin: - @CGO_ENABLED=0 $(GO_CMD) build -o bin/cassandra-database-plugin ./plugins/database/cassandra/cassandra-database-plugin - influxdb-database-plugin: @CGO_ENABLED=0 $(GO_CMD) build -o bin/influxdb-database-plugin ./plugins/database/influxdb/influxdb-database-plugin @@ -366,7 +363,7 @@ ci-copywriteheaders: cd sdk && $(CURDIR)/scripts/copywrite-exceptions.sh cd shamir && $(CURDIR)/scripts/copywrite-exceptions.sh -.PHONY: all bin default prep test vet bootstrap fmt fmtcheck mysql-database-plugin mysql-legacy-database-plugin cassandra-database-plugin influxdb-database-plugin postgresql-database-plugin ember-dist ember-dist-dev static-dist static-dist-dev assetcheck check-vault-in-path packages build build-ci semgrep semgrep-ci vet-codechecker ci-vet-codechecker clean dev +.PHONY: all bin default prep test vet bootstrap fmt fmtcheck mysql-database-plugin mysql-legacy-database-plugin influxdb-database-plugin postgresql-database-plugin ember-dist ember-dist-dev static-dist static-dist-dev assetcheck check-vault-in-path packages build build-ci semgrep semgrep-ci vet-codechecker ci-vet-codechecker clean dev .NOTPARALLEL: ember-dist ember-dist-dev diff --git a/command/commands.go b/command/commands.go index 874322e77..625be1e72 100644 --- a/command/commands.go +++ b/command/commands.go @@ -42,7 +42,6 @@ import ( logicalDb "github.com/hashicorp/vault/builtin/logical/database" physAerospike "github.com/hashicorp/vault/physical/aerospike" - physCassandra "github.com/hashicorp/vault/physical/cassandra" physCockroachDB "github.com/hashicorp/vault/physical/cockroachdb" physConsul "github.com/hashicorp/vault/physical/consul" physFoundationDB "github.com/hashicorp/vault/physical/foundationdb" @@ -171,7 +170,6 @@ var ( physicalBackends = map[string]physical.Factory{ "aerospike": physAerospike.NewAerospikeBackend, - "cassandra": physCassandra.NewCassandraBackend, "cockroachdb": physCockroachDB.NewCockroachDBBackend, "consul": physConsul.NewConsulBackend, "file_transactional": physFile.NewTransactionalFileBackend, diff --git a/go.mod b/go.mod index 6fa31a81e..8647ea141 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,6 @@ require ( github.com/armon/go-radix v1.0.0 github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a - github.com/cenkalti/backoff/v3 v3.2.2 github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0 github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf @@ -47,7 +46,6 @@ require ( github.com/go-ldap/ldap/v3 v3.4.4 github.com/go-sql-driver/mysql v1.6.0 github.com/go-test/deep v1.1.0 - github.com/gocql/gocql v1.0.0 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.3 github.com/google/go-cmp v0.6.0 @@ -216,6 +214,7 @@ require ( github.com/bgentry/speakeasy v0.1.0 // indirect github.com/boombuler/barcode v1.0.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect + github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/centrify/cloud-golang-sdk v0.0.0-20210923165758-a8c48d049166 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -274,7 +273,6 @@ require ( github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/cronexpr v1.1.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-msgpack/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 0acaa39d1..0bb5c4ab5 100644 --- a/go.sum +++ b/go.sum @@ -1007,15 +1007,12 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= @@ -1639,8 +1636,6 @@ github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c= -github.com/gocql/gocql v1.0.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -1854,8 +1849,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4Zs github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp-forge/bbolt v1.3.8-hc3 h1:iTWR3RDPj0TGChAvJ8QjHFcNFWAUVgNQV73IE6gAX4E= github.com/hashicorp-forge/bbolt v1.3.8-hc3/go.mod h1:sQBu5UIJ+rcUFU4Fo9rpTHNV935jwmGWS3dQ/MV8810= github.com/hashicorp/cap v0.3.0 h1:zFzVxuWy78lO6QRLHu/ONkjx/Jh0lpfvPgmpDGri43E= diff --git a/helper/builtinplugins/registry.go b/helper/builtinplugins/registry.go index 9e1e7c54b..fa836f912 100644 --- a/helper/builtinplugins/registry.go +++ b/helper/builtinplugins/registry.go @@ -31,7 +31,6 @@ import ( logicalSsh "github.com/hashicorp/vault/builtin/logical/ssh" logicalTotp "github.com/hashicorp/vault/builtin/logical/totp" logicalTransit "github.com/hashicorp/vault/builtin/logical/transit" - dbCass "github.com/hashicorp/vault/plugins/database/cassandra" dbInflux "github.com/hashicorp/vault/plugins/database/influxdb" dbMysql "github.com/hashicorp/vault/plugins/database/mysql" dbPostgres "github.com/hashicorp/vault/plugins/database/postgresql" @@ -110,7 +109,6 @@ func newRegistry() *registry { "mysql-rds-database-plugin": {Factory: dbMysql.New(dbMysql.DefaultLegacyUserNameTemplate)}, "mysql-legacy-database-plugin": {Factory: dbMysql.New(dbMysql.DefaultLegacyUserNameTemplate)}, - "cassandra-database-plugin": {Factory: dbCass.New}, "influxdb-database-plugin": {Factory: dbInflux.New}, "postgresql-database-plugin": {Factory: dbPostgres.New}, }, @@ -119,10 +117,6 @@ func newRegistry() *registry { Factory: logicalAd.Factory, DeprecationStatus: consts.Deprecated, }, - "cassandra": { - Factory: removedFactory, - DeprecationStatus: consts.Removed, - }, "consul": {Factory: logicalConsul.Factory}, "kubernetes": {Factory: logicalKube.Factory}, "kv": {Factory: logicalKv.Factory}, diff --git a/helper/testhelpers/cassandra/cassandrahelper.go b/helper/testhelpers/cassandra/cassandrahelper.go deleted file mode 100644 index f6ee1f2c2..000000000 --- a/helper/testhelpers/cassandra/cassandrahelper.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "context" - "fmt" - "net" - "os" - "path/filepath" - "testing" - "time" - - "github.com/gocql/gocql" - "github.com/hashicorp/vault/sdk/helper/docker" -) - -type containerConfig struct { - containerName string - imageName string - version string - copyFromTo map[string]string - env []string - - sslOpts *gocql.SslOptions -} - -type ContainerOpt func(*containerConfig) - -func ContainerName(name string) ContainerOpt { - return func(cfg *containerConfig) { - cfg.containerName = name - } -} - -func Image(imageName string, version string) ContainerOpt { - return func(cfg *containerConfig) { - cfg.imageName = imageName - cfg.version = version - - // Reset the environment because there's a very good chance the default environment doesn't apply to the - // non-default image being used - cfg.env = nil - } -} - -func Version(version string) ContainerOpt { - return func(cfg *containerConfig) { - cfg.version = version - } -} - -func CopyFromTo(copyFromTo map[string]string) ContainerOpt { - return func(cfg *containerConfig) { - cfg.copyFromTo = copyFromTo - } -} - -func Env(keyValue string) ContainerOpt { - return func(cfg *containerConfig) { - cfg.env = append(cfg.env, keyValue) - } -} - -func SslOpts(sslOpts *gocql.SslOptions) ContainerOpt { - return func(cfg *containerConfig) { - cfg.sslOpts = sslOpts - } -} - -type Host struct { - Name string - Port string -} - -func (h Host) ConnectionURL() string { - return net.JoinHostPort(h.Name, h.Port) -} - -func PrepareTestContainer(t *testing.T, opts ...ContainerOpt) (Host, func()) { - t.Helper() - if os.Getenv("CASSANDRA_HOSTS") != "" { - host, port, err := net.SplitHostPort(os.Getenv("CASSANDRA_HOSTS")) - if err != nil { - t.Fatalf("Failed to split host & port from CASSANDRA_HOSTS (%s): %s", os.Getenv("CASSANDRA_HOSTS"), err) - } - h := Host{ - Name: host, - Port: port, - } - return h, func() {} - } - - containerCfg := &containerConfig{ - imageName: "docker.mirror.hashicorp.services/library/cassandra", - containerName: "cassandra", - version: "3.11", - env: []string{"CASSANDRA_BROADCAST_ADDRESS=127.0.0.1"}, - } - - for _, opt := range opts { - opt(containerCfg) - } - - copyFromTo := map[string]string{} - for from, to := range containerCfg.copyFromTo { - absFrom, err := filepath.Abs(from) - if err != nil { - t.Fatalf("Unable to get absolute path for file %s", from) - } - copyFromTo[absFrom] = to - } - - runOpts := docker.RunOptions{ - ContainerName: containerCfg.containerName, - ImageRepo: containerCfg.imageName, - ImageTag: containerCfg.version, - Ports: []string{"9042/tcp"}, - CopyFromTo: copyFromTo, - Env: containerCfg.env, - } - runner, err := docker.NewServiceRunner(runOpts) - if err != nil { - t.Fatalf("Could not start docker cassandra: %s", err) - } - - svc, err := runner.StartService(context.Background(), func(ctx context.Context, host string, port int) (docker.ServiceConfig, error) { - cfg := docker.NewServiceHostPort(host, port) - clusterConfig := gocql.NewCluster(cfg.Address()) - clusterConfig.Authenticator = gocql.PasswordAuthenticator{ - Username: "cassandra", - Password: "cassandra", - } - clusterConfig.Timeout = 30 * time.Second - clusterConfig.ProtoVersion = 4 - clusterConfig.Port = port - - clusterConfig.SslOpts = containerCfg.sslOpts - - session, err := clusterConfig.CreateSession() - if err != nil { - return nil, fmt.Errorf("error creating session: %s", err) - } - defer session.Close() - - // Create keyspace - query := session.Query(`CREATE KEYSPACE "vault" WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };`) - if err := query.Exec(); err != nil { - t.Fatalf("could not create cassandra keyspace: %v", err) - } - - // Create table - query = session.Query(`CREATE TABLE "vault"."entries" ( - bucket text, - key text, - value blob, - PRIMARY KEY (bucket, key) - ) WITH CLUSTERING ORDER BY (key ASC);`) - if err := query.Exec(); err != nil { - t.Fatalf("could not create cassandra table: %v", err) - } - return cfg, nil - }) - if err != nil { - t.Fatalf("Could not start docker cassandra: %s", err) - } - - host, port, err := net.SplitHostPort(svc.Config.Address()) - if err != nil { - t.Fatalf("Failed to split host & port from address (%s): %s", svc.Config.Address(), err) - } - h := Host{ - Name: host, - Port: port, - } - return h, svc.Cleanup -} diff --git a/helper/testhelpers/corehelpers/corehelpers.go b/helper/testhelpers/corehelpers/corehelpers.go index d43ef60ac..5c38b66a9 100644 --- a/helper/testhelpers/corehelpers/corehelpers.go +++ b/helper/testhelpers/corehelpers/corehelpers.go @@ -155,7 +155,6 @@ func (m *mockBuiltinRegistry) Keys(pluginType consts.PluginType) []string { "mysql-rds-database-plugin", "mysql-legacy-database-plugin", - "cassandra-database-plugin", "influxdb-database-plugin", "postgresql-database-plugin", } diff --git a/physical/cassandra/cassandra.go b/physical/cassandra/cassandra.go deleted file mode 100644 index 1c592ee33..000000000 --- a/physical/cassandra/cassandra.go +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "context" - "crypto/tls" - "fmt" - "io/ioutil" - "net" - "strconv" - "strings" - "time" - - metrics "github.com/armon/go-metrics" - "github.com/gocql/gocql" - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/sdk/helper/certutil" - "github.com/hashicorp/vault/sdk/physical" -) - -// CassandraBackend is a physical backend that stores data in Cassandra. -type CassandraBackend struct { - sess *gocql.Session - table string - - logger log.Logger -} - -// Verify CassandraBackend satisfies the correct interfaces -var _ physical.Backend = (*CassandraBackend)(nil) - -// NewCassandraBackend constructs a Cassandra backend using a pre-existing -// keyspace and table. -func NewCassandraBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) { - splitArray := func(v string) []string { - return strings.FieldsFunc(v, func(r rune) bool { - return r == ',' - }) - } - - var ( - hosts = splitArray(conf["hosts"]) - port = 9042 - explicitPort = false - keyspace = conf["keyspace"] - table = conf["table"] - consistency = gocql.LocalQuorum - ) - - if len(hosts) == 0 { - hosts = []string{"localhost"} - } - for i, hp := range hosts { - h, ps, err := net.SplitHostPort(hp) - if err != nil { - continue - } - p, err := strconv.Atoi(ps) - if err != nil { - return nil, err - } - - if explicitPort && p != port { - return nil, fmt.Errorf("all hosts must have the same port") - } - hosts[i], port = h, p - explicitPort = true - } - - if keyspace == "" { - keyspace = "vault" - } - if table == "" { - table = "entries" - } - if cs, ok := conf["consistency"]; ok { - switch cs { - case "ANY": - consistency = gocql.Any - case "ONE": - consistency = gocql.One - case "TWO": - consistency = gocql.Two - case "THREE": - consistency = gocql.Three - case "QUORUM": - consistency = gocql.Quorum - case "ALL": - consistency = gocql.All - case "LOCAL_QUORUM": - consistency = gocql.LocalQuorum - case "EACH_QUORUM": - consistency = gocql.EachQuorum - case "LOCAL_ONE": - consistency = gocql.LocalOne - default: - return nil, fmt.Errorf("'consistency' must be one of {ANY, ONE, TWO, THREE, QUORUM, ALL, LOCAL_QUORUM, EACH_QUORUM, LOCAL_ONE}") - } - } - - connectStart := time.Now() - cluster := gocql.NewCluster(hosts...) - cluster.Port = port - cluster.Keyspace = keyspace - - if retryCountStr, ok := conf["simple_retry_policy_retries"]; ok { - retryCount, err := strconv.Atoi(retryCountStr) - if err != nil || retryCount <= 0 { - return nil, fmt.Errorf("'simple_retry_policy_retries' must be a positive integer") - } - cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: retryCount} - } - - cluster.ProtoVersion = 2 - if protoVersionStr, ok := conf["protocol_version"]; ok { - protoVersion, err := strconv.Atoi(protoVersionStr) - if err != nil { - return nil, fmt.Errorf("'protocol_version' must be an integer") - } - cluster.ProtoVersion = protoVersion - } - - if username, ok := conf["username"]; ok { - if cluster.ProtoVersion < 2 { - return nil, fmt.Errorf("authentication is not supported with protocol version < 2") - } - authenticator := gocql.PasswordAuthenticator{Username: username} - if password, ok := conf["password"]; ok { - authenticator.Password = password - } - cluster.Authenticator = authenticator - } - - if initialConnectionTimeoutStr, ok := conf["initial_connection_timeout"]; ok { - initialConnectionTimeout, err := strconv.Atoi(initialConnectionTimeoutStr) - if err != nil || initialConnectionTimeout <= 0 { - return nil, fmt.Errorf("'initial_connection_timeout' must be a positive integer") - } - cluster.ConnectTimeout = time.Duration(initialConnectionTimeout) * time.Second - } - - if connTimeoutStr, ok := conf["connection_timeout"]; ok { - connectionTimeout, err := strconv.Atoi(connTimeoutStr) - if err != nil || connectionTimeout <= 0 { - return nil, fmt.Errorf("'connection_timeout' must be a positive integer") - } - cluster.Timeout = time.Duration(connectionTimeout) * time.Second - } - - if err := setupCassandraTLS(conf, cluster); err != nil { - return nil, err - } - - sess, err := cluster.CreateSession() - if err != nil { - return nil, err - } - metrics.MeasureSince([]string{"cassandra", "connect"}, connectStart) - sess.SetConsistency(consistency) - - impl := &CassandraBackend{ - sess: sess, - table: table, - logger: logger, - } - return impl, nil -} - -func setupCassandraTLS(conf map[string]string, cluster *gocql.ClusterConfig) error { - tlsOnStr, ok := conf["tls"] - if !ok { - return nil - } - - tlsOn, err := strconv.Atoi(tlsOnStr) - if err != nil { - return fmt.Errorf("'tls' must be an integer (0 or 1)") - } - - if tlsOn == 0 { - return nil - } - - tlsConfig := &tls.Config{} - if pemBundlePath, ok := conf["pem_bundle_file"]; ok { - pemBundleData, err := ioutil.ReadFile(pemBundlePath) - if err != nil { - return fmt.Errorf("error reading pem bundle from %q: %w", pemBundlePath, err) - } - pemBundle, err := certutil.ParsePEMBundle(string(pemBundleData)) - if err != nil { - return fmt.Errorf("error parsing 'pem_bundle': %w", err) - } - tlsConfig, err = pemBundle.GetTLSConfig(certutil.TLSClient) - if err != nil { - return err - } - } else if pemJSONPath, ok := conf["pem_json_file"]; ok { - pemJSONData, err := ioutil.ReadFile(pemJSONPath) - if err != nil { - return fmt.Errorf("error reading json bundle from %q: %w", pemJSONPath, err) - } - pemJSON, err := certutil.ParsePKIJSON([]byte(pemJSONData)) - if err != nil { - return err - } - tlsConfig, err = pemJSON.GetTLSConfig(certutil.TLSClient) - if err != nil { - return err - } - } - - if tlsSkipVerifyStr, ok := conf["tls_skip_verify"]; ok { - tlsSkipVerify, err := strconv.Atoi(tlsSkipVerifyStr) - if err != nil { - return fmt.Errorf("'tls_skip_verify' must be an integer (0 or 1)") - } - if tlsSkipVerify == 0 { - tlsConfig.InsecureSkipVerify = false - } else { - tlsConfig.InsecureSkipVerify = true - } - } - - if tlsMinVersion, ok := conf["tls_min_version"]; ok { - switch tlsMinVersion { - case "tls10": - tlsConfig.MinVersion = tls.VersionTLS10 - case "tls11": - tlsConfig.MinVersion = tls.VersionTLS11 - case "tls12": - tlsConfig.MinVersion = tls.VersionTLS12 - case "tls13": - tlsConfig.MinVersion = tls.VersionTLS13 - default: - return fmt.Errorf("'tls_min_version' must be one of `tls10`, `tls11`, `tls12` or `tls13`") - } - } - - cluster.SslOpts = &gocql.SslOptions{ - Config: tlsConfig, - EnableHostVerification: !tlsConfig.InsecureSkipVerify, - } - return nil -} - -// bucketName sanitises a bucket name for Cassandra -func (c *CassandraBackend) bucketName(name string) string { - if name == "" { - name = "." - } - return strings.TrimRight(name, "/") -} - -// bucket returns all the prefix buckets the key should be stored at -func (c *CassandraBackend) buckets(key string) []string { - vals := append([]string{""}, physical.Prefixes(key)...) - for i, v := range vals { - vals[i] = c.bucketName(v) - } - return vals -} - -// bucket returns the most specific bucket for the key -func (c *CassandraBackend) bucket(key string) string { - bs := c.buckets(key) - return bs[len(bs)-1] -} - -// Put is used to insert or update an entry -func (c *CassandraBackend) Put(ctx context.Context, entry *physical.Entry) error { - defer metrics.MeasureSince([]string{"cassandra", "put"}, time.Now()) - - // Execute inserts to each key prefix simultaneously - stmt := fmt.Sprintf(`INSERT INTO "%s" (bucket, key, value) VALUES (?, ?, ?)`, c.table) - buckets := c.buckets(entry.Key) - results := make(chan error, len(buckets)) - for i, _bucket := range buckets { - go func(i int, bucket string) { - var value []byte - if i == len(buckets)-1 { - // Only store the full value if this is the leaf bucket where the entry will actually be read - // otherwise this write is just to allow for list operations - value = entry.Value - } - results <- c.sess.Query(stmt, bucket, entry.Key, value).Exec() - }(i, _bucket) - } - for i := 0; i < len(buckets); i++ { - if err := <-results; err != nil { - return err - } - } - return nil -} - -// Get is used to fetch an entry -func (c *CassandraBackend) Get(ctx context.Context, key string) (*physical.Entry, error) { - defer metrics.MeasureSince([]string{"cassandra", "get"}, time.Now()) - - v := []byte(nil) - stmt := fmt.Sprintf(`SELECT value FROM "%s" WHERE bucket = ? AND key = ? LIMIT 1`, c.table) - q := c.sess.Query(stmt, c.bucket(key), key) - if err := q.Scan(&v); err != nil { - if err == gocql.ErrNotFound { - return nil, nil - } - return nil, err - } - - return &physical.Entry{ - Key: key, - Value: v, - }, nil -} - -// Delete is used to permanently delete an entry -func (c *CassandraBackend) Delete(ctx context.Context, key string) error { - defer metrics.MeasureSince([]string{"cassandra", "delete"}, time.Now()) - - stmt := fmt.Sprintf(`DELETE FROM "%s" WHERE bucket = ? AND key = ?`, c.table) - buckets := c.buckets(key) - results := make(chan error, len(buckets)) - - for _, bucket := range buckets { - go func(bucket string) { - results <- c.sess.Query(stmt, bucket, key).Exec() - }(bucket) - } - - for i := 0; i < len(buckets); i++ { - if err := <-results; err != nil { - return err - } - } - return nil -} - -// List is used ot list all the keys under a given -// prefix, up to the next prefix. -func (c *CassandraBackend) List(ctx context.Context, prefix string) ([]string, error) { - defer metrics.MeasureSince([]string{"cassandra", "list"}, time.Now()) - - stmt := fmt.Sprintf(`SELECT key FROM "%s" WHERE bucket = ?`, c.table) - q := c.sess.Query(stmt, c.bucketName(prefix)) - iter := q.Iter() - k, keys := "", []string{} - for iter.Scan(&k) { - // Only return the next "component" (with a trailing slash if it has children) - k = strings.TrimPrefix(k, prefix) - if parts := strings.SplitN(k, "/", 2); len(parts) > 1 { - k = parts[0] + "/" - } else { - k = parts[0] - } - - // Deduplicate; this works because the keys are sorted - if len(keys) > 0 && keys[len(keys)-1] == k { - continue - } - keys = append(keys, k) - } - return keys, iter.Close() -} diff --git a/physical/cassandra/cassandra_test.go b/physical/cassandra/cassandra_test.go deleted file mode 100644 index 3370c3947..000000000 --- a/physical/cassandra/cassandra_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "os" - "reflect" - "testing" - - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/helper/testhelpers/cassandra" - "github.com/hashicorp/vault/sdk/helper/logging" - "github.com/hashicorp/vault/sdk/physical" -) - -func TestCassandraBackend(t *testing.T) { - if testing.Short() { - t.Skipf("skipping in short mode") - } - if os.Getenv("VAULT_CI_GO_TEST_RACE") != "" { - t.Skip("skipping race test in CI pending https://github.com/gocql/gocql/pull/1474") - } - - host, cleanup := cassandra.PrepareTestContainer(t) - defer cleanup() - - // Run vault tests - logger := logging.NewVaultLogger(log.Debug) - b, err := NewCassandraBackend(map[string]string{ - "hosts": host.ConnectionURL(), - "protocol_version": "3", - "connection_timeout": "5", - "initial_connection_timeout": "5", - "simple_retry_policy_retries": "3", - }, logger) - if err != nil { - t.Fatalf("Failed to create new backend: %v", err) - } - - physical.ExerciseBackend(t, b) - physical.ExerciseBackend_ListPrefix(t, b) -} - -func TestCassandraBackendBuckets(t *testing.T) { - expectations := map[string][]string{ - "": {"."}, - "a": {"."}, - "a/b": {".", "a"}, - "a/b/c/d/e": {".", "a", "a/b", "a/b/c", "a/b/c/d"}, - } - - b := &CassandraBackend{} - for input, expected := range expectations { - actual := b.buckets(input) - if !reflect.DeepEqual(actual, expected) { - t.Errorf("bad: %v expected: %v", actual, expected) - } - } -} diff --git a/plugins/database/cassandra/cassandra-database-plugin/main.go b/plugins/database/cassandra/cassandra-database-plugin/main.go deleted file mode 100644 index 6f9f7af95..000000000 --- a/plugins/database/cassandra/cassandra-database-plugin/main.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package main - -import ( - "log" - "os" - - "github.com/hashicorp/vault/plugins/database/cassandra" - "github.com/hashicorp/vault/sdk/database/dbplugin/v5" -) - -func main() { - err := Run() - if err != nil { - log.Println(err) - os.Exit(1) - } -} - -// Run instantiates a Cassandra object, and runs the RPC server for the plugin -func Run() error { - dbplugin.ServeMultiplex(cassandra.New) - - return nil -} diff --git a/plugins/database/cassandra/cassandra.go b/plugins/database/cassandra/cassandra.go deleted file mode 100644 index 01a4c406c..000000000 --- a/plugins/database/cassandra/cassandra.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "context" - "fmt" - "strings" - - "github.com/hashicorp/vault/sdk/helper/template" - - "github.com/gocql/gocql" - multierror "github.com/hashicorp/go-multierror" - "github.com/hashicorp/go-secure-stdlib/strutil" - dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" - "github.com/hashicorp/vault/sdk/database/helper/dbutil" -) - -const ( - defaultUserCreationCQL = `CREATE USER '{{username}}' WITH PASSWORD '{{password}}' NOSUPERUSER;` - defaultUserDeletionCQL = `DROP USER '{{username}}';` - defaultChangePasswordCQL = `ALTER USER '{{username}}' WITH PASSWORD '{{password}}';` - cassandraTypeName = "cassandra" - - defaultUserNameTemplate = `{{ printf "v_%s_%s_%s_%s" (.DisplayName | truncate 15) (.RoleName | truncate 15) (random 20) (unix_time) | truncate 100 | replace "-" "_" | lowercase }}` -) - -var _ dbplugin.Database = &Cassandra{} - -// Cassandra is an implementation of Database interface -type Cassandra struct { - *cassandraConnectionProducer - - usernameProducer template.StringTemplate -} - -// New returns a new Cassandra instance -func New() (interface{}, error) { - db := new() - dbType := dbplugin.NewDatabaseErrorSanitizerMiddleware(db, db.secretValues) - - return dbType, nil -} - -func new() *Cassandra { - connProducer := &cassandraConnectionProducer{} - connProducer.Type = cassandraTypeName - - return &Cassandra{ - cassandraConnectionProducer: connProducer, - } -} - -// Type returns the TypeName for this backend -func (c *Cassandra) Type() (string, error) { - return cassandraTypeName, nil -} - -func (c *Cassandra) getConnection(ctx context.Context) (*gocql.Session, error) { - session, err := c.Connection(ctx) - if err != nil { - return nil, err - } - - return session.(*gocql.Session), nil -} - -func (c *Cassandra) Initialize(ctx context.Context, req dbplugin.InitializeRequest) (dbplugin.InitializeResponse, error) { - usernameTemplate, err := strutil.GetString(req.Config, "username_template") - if err != nil { - return dbplugin.InitializeResponse{}, fmt.Errorf("failed to retrieve username_template: %w", err) - } - if usernameTemplate == "" { - usernameTemplate = defaultUserNameTemplate - } - - up, err := template.NewTemplate(template.Template(usernameTemplate)) - if err != nil { - return dbplugin.InitializeResponse{}, fmt.Errorf("unable to initialize username template: %w", err) - } - c.usernameProducer = up - - _, err = c.usernameProducer.Generate(dbplugin.UsernameMetadata{}) - if err != nil { - return dbplugin.InitializeResponse{}, fmt.Errorf("invalid username template: %w", err) - } - - err = c.cassandraConnectionProducer.Initialize(ctx, req) - if err != nil { - return dbplugin.InitializeResponse{}, fmt.Errorf("failed to initialize: %w", err) - } - - resp := dbplugin.InitializeResponse{ - Config: req.Config, - } - return resp, nil -} - -// NewUser generates the username/password on the underlying Cassandra secret backend as instructed by -// the statements provided. -func (c *Cassandra) NewUser(ctx context.Context, req dbplugin.NewUserRequest) (dbplugin.NewUserResponse, error) { - c.Lock() - defer c.Unlock() - - session, err := c.getConnection(ctx) - if err != nil { - return dbplugin.NewUserResponse{}, err - } - - creationCQL := req.Statements.Commands - if len(creationCQL) == 0 { - creationCQL = []string{defaultUserCreationCQL} - } - - rollbackCQL := req.RollbackStatements.Commands - if len(rollbackCQL) == 0 { - rollbackCQL = []string{defaultUserDeletionCQL} - } - - username, err := c.usernameProducer.Generate(req.UsernameConfig) - if err != nil { - return dbplugin.NewUserResponse{}, err - } - - for _, stmt := range creationCQL { - for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") { - query = strings.TrimSpace(query) - if len(query) == 0 { - continue - } - - m := map[string]string{ - "username": username, - "password": req.Password, - } - err = session. - Query(dbutil.QueryHelper(query, m)). - WithContext(ctx). - Exec() - if err != nil { - rollbackErr := rollbackUser(ctx, session, username, rollbackCQL) - if rollbackErr != nil { - err = multierror.Append(err, rollbackErr) - } - return dbplugin.NewUserResponse{}, err - } - } - } - - resp := dbplugin.NewUserResponse{ - Username: username, - } - return resp, nil -} - -func rollbackUser(ctx context.Context, session *gocql.Session, username string, rollbackCQL []string) error { - for _, stmt := range rollbackCQL { - for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") { - query = strings.TrimSpace(query) - if len(query) == 0 { - continue - } - - m := map[string]string{ - "username": username, - } - err := session. - Query(dbutil.QueryHelper(query, m)). - WithContext(ctx). - Exec() - if err != nil { - return fmt.Errorf("failed to roll back user %s: %w", username, err) - } - } - } - return nil -} - -func (c *Cassandra) UpdateUser(ctx context.Context, req dbplugin.UpdateUserRequest) (dbplugin.UpdateUserResponse, error) { - if req.Password == nil && req.Expiration == nil { - return dbplugin.UpdateUserResponse{}, fmt.Errorf("no changes requested") - } - - if req.Password != nil { - err := c.changeUserPassword(ctx, req.Username, req.Password) - return dbplugin.UpdateUserResponse{}, err - } - // Expiration is no-op - return dbplugin.UpdateUserResponse{}, nil -} - -func (c *Cassandra) changeUserPassword(ctx context.Context, username string, changePass *dbplugin.ChangePassword) error { - session, err := c.getConnection(ctx) - if err != nil { - return err - } - - rotateCQL := changePass.Statements.Commands - if len(rotateCQL) == 0 { - rotateCQL = []string{defaultChangePasswordCQL} - } - - var result *multierror.Error - for _, stmt := range rotateCQL { - for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") { - query = strings.TrimSpace(query) - if len(query) == 0 { - continue - } - - m := map[string]string{ - "username": username, - "password": changePass.NewPassword, - } - err := session. - Query(dbutil.QueryHelper(query, m)). - WithContext(ctx). - Exec() - result = multierror.Append(result, err) - } - } - - return result.ErrorOrNil() -} - -// DeleteUser attempts to drop the specified user. -func (c *Cassandra) DeleteUser(ctx context.Context, req dbplugin.DeleteUserRequest) (dbplugin.DeleteUserResponse, error) { - c.Lock() - defer c.Unlock() - - session, err := c.getConnection(ctx) - if err != nil { - return dbplugin.DeleteUserResponse{}, err - } - - revocationCQL := req.Statements.Commands - if len(revocationCQL) == 0 { - revocationCQL = []string{defaultUserDeletionCQL} - } - - var result *multierror.Error - for _, stmt := range revocationCQL { - for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") { - query = strings.TrimSpace(query) - if len(query) == 0 { - continue - } - - m := map[string]string{ - "username": req.Username, - } - err := session. - Query(dbutil.QueryHelper(query, m)). - WithContext(ctx). - Exec() - - result = multierror.Append(result, err) - } - } - - return dbplugin.DeleteUserResponse{}, result.ErrorOrNil() -} diff --git a/plugins/database/cassandra/cassandra_test.go b/plugins/database/cassandra/cassandra_test.go deleted file mode 100644 index 9162c467a..000000000 --- a/plugins/database/cassandra/cassandra_test.go +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "context" - "reflect" - "testing" - "time" - - "github.com/stretchr/testify/require" - - backoff "github.com/cenkalti/backoff/v3" - "github.com/gocql/gocql" - "github.com/hashicorp/vault/helper/testhelpers/cassandra" - dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" - dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing" -) - -func getCassandra(t *testing.T, protocolVersion interface{}) (*Cassandra, func()) { - host, cleanup := cassandra.PrepareTestContainer(t, - cassandra.Version("3.11"), - cassandra.CopyFromTo(insecureFileMounts), - ) - - db := new() - initReq := dbplugin.InitializeRequest{ - Config: map[string]interface{}{ - "hosts": host.ConnectionURL(), - "port": host.Port, - "username": "cassandra", - "password": "cassandra", - "protocol_version": protocolVersion, - "connect_timeout": "20s", - }, - VerifyConnection: true, - } - - expectedConfig := map[string]interface{}{ - "hosts": host.ConnectionURL(), - "port": host.Port, - "username": "cassandra", - "password": "cassandra", - "protocol_version": protocolVersion, - "connect_timeout": "20s", - } - - initResp := dbtesting.AssertInitialize(t, db, initReq) - if !reflect.DeepEqual(initResp.Config, expectedConfig) { - t.Fatalf("Initialize response config actual: %#v\nExpected: %#v", initResp.Config, expectedConfig) - } - - if !db.Initialized { - t.Fatal("Database should be initialized") - } - return db, cleanup -} - -func TestInitialize(t *testing.T) { - t.Run("integer protocol version", func(t *testing.T) { - // getCassandra performs an Initialize call - db, cleanup := getCassandra(t, 4) - t.Cleanup(cleanup) - - err := db.Close() - if err != nil { - t.Fatalf("err: %s", err) - } - }) - - t.Run("string protocol version", func(t *testing.T) { - // getCassandra performs an Initialize call - db, cleanup := getCassandra(t, "4") - t.Cleanup(cleanup) - - err := db.Close() - if err != nil { - t.Fatalf("err: %s", err) - } - }) -} - -func TestCreateUser(t *testing.T) { - type testCase struct { - // Config will have the hosts & port added to it during the test - config map[string]interface{} - newUserReq dbplugin.NewUserRequest - expectErr bool - expectedUsernameRegex string - assertCreds func(t testing.TB, address string, port int, username, password string, sslOpts *gocql.SslOptions, timeout time.Duration) - } - - tests := map[string]testCase{ - "default username_template": { - config: map[string]interface{}{ - "username": "cassandra", - "password": "cassandra", - "protocol_version": "4", - "connect_timeout": "20s", - }, - newUserReq: dbplugin.NewUserRequest{ - UsernameConfig: dbplugin.UsernameMetadata{ - DisplayName: "token", - RoleName: "mylongrolenamewithmanycharacters", - }, - Statements: dbplugin.Statements{ - Commands: []string{createUserStatements}, - }, - Password: "bfn985wjAHIh6t", - Expiration: time.Now().Add(1 * time.Minute), - }, - expectErr: false, - expectedUsernameRegex: `^v_token_mylongrolenamew_[a-z0-9]{20}_[0-9]{10}$`, - assertCreds: assertCreds, - }, - "custom username_template": { - config: map[string]interface{}{ - "username": "cassandra", - "password": "cassandra", - "protocol_version": "4", - "connect_timeout": "20s", - "username_template": `foo_{{random 20}}_{{.RoleName | replace "e" "3"}}_{{unix_time}}`, - }, - newUserReq: dbplugin.NewUserRequest{ - UsernameConfig: dbplugin.UsernameMetadata{ - DisplayName: "token", - RoleName: "mylongrolenamewithmanycharacters", - }, - Statements: dbplugin.Statements{ - Commands: []string{createUserStatements}, - }, - Password: "bfn985wjAHIh6t", - Expiration: time.Now().Add(1 * time.Minute), - }, - expectErr: false, - expectedUsernameRegex: `^foo_[a-zA-Z0-9]{20}_mylongrol3nam3withmanycharact3rs_[0-9]{10}$`, - assertCreds: assertCreds, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - host, cleanup := cassandra.PrepareTestContainer(t, - cassandra.Version("3.11"), - cassandra.CopyFromTo(insecureFileMounts), - ) - defer cleanup() - - db := new() - - config := test.config - config["hosts"] = host.ConnectionURL() - config["port"] = host.Port - - initReq := dbplugin.InitializeRequest{ - Config: config, - VerifyConnection: true, - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - dbtesting.AssertInitialize(t, db, initReq) - - require.True(t, db.Initialized, "Database is not initialized") - - ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - newUserResp, err := db.NewUser(ctx, test.newUserReq) - if test.expectErr && err == nil { - t.Fatalf("err expected, got nil") - } - if !test.expectErr && err != nil { - t.Fatalf("no error expected, got: %s", err) - } - require.Regexp(t, test.expectedUsernameRegex, newUserResp.Username) - test.assertCreds(t, db.Hosts, db.Port, newUserResp.Username, test.newUserReq.Password, nil, 5*time.Second) - }) - } -} - -func TestUpdateUserPassword(t *testing.T) { - db, cleanup := getCassandra(t, 4) - defer cleanup() - - password := "myreallysecurepassword" - createReq := dbplugin.NewUserRequest{ - UsernameConfig: dbplugin.UsernameMetadata{ - DisplayName: "test", - RoleName: "test", - }, - Statements: dbplugin.Statements{ - Commands: []string{createUserStatements}, - }, - Password: password, - Expiration: time.Now().Add(1 * time.Minute), - } - - createResp := dbtesting.AssertNewUser(t, db, createReq) - - assertCreds(t, db.Hosts, db.Port, createResp.Username, password, nil, 5*time.Second) - - newPassword := "somenewpassword" - updateReq := dbplugin.UpdateUserRequest{ - Username: createResp.Username, - Password: &dbplugin.ChangePassword{ - NewPassword: newPassword, - Statements: dbplugin.Statements{}, - }, - Expiration: nil, - } - - dbtesting.AssertUpdateUser(t, db, updateReq) - - assertCreds(t, db.Hosts, db.Port, createResp.Username, newPassword, nil, 5*time.Second) -} - -func TestDeleteUser(t *testing.T) { - db, cleanup := getCassandra(t, 4) - defer cleanup() - - password := "myreallysecurepassword" - createReq := dbplugin.NewUserRequest{ - UsernameConfig: dbplugin.UsernameMetadata{ - DisplayName: "test", - RoleName: "test", - }, - Statements: dbplugin.Statements{ - Commands: []string{createUserStatements}, - }, - Password: password, - Expiration: time.Now().Add(1 * time.Minute), - } - - createResp := dbtesting.AssertNewUser(t, db, createReq) - - assertCreds(t, db.Hosts, db.Port, createResp.Username, password, nil, 5*time.Second) - - deleteReq := dbplugin.DeleteUserRequest{ - Username: createResp.Username, - } - - dbtesting.AssertDeleteUser(t, db, deleteReq) - - assertNoCreds(t, db.Hosts, db.Port, createResp.Username, password, nil, 5*time.Second) -} - -func assertCreds(t testing.TB, address string, port int, username, password string, sslOpts *gocql.SslOptions, timeout time.Duration) { - t.Helper() - op := func() error { - return connect(t, address, port, username, password, sslOpts) - } - bo := backoff.NewExponentialBackOff() - bo.MaxElapsedTime = timeout - bo.InitialInterval = 500 * time.Millisecond - bo.MaxInterval = bo.InitialInterval - bo.RandomizationFactor = 0.0 - - err := backoff.Retry(op, bo) - if err != nil { - t.Fatalf("failed to connect after %s: %s", timeout, err) - } -} - -func connect(t testing.TB, address string, port int, username, password string, sslOpts *gocql.SslOptions) error { - t.Helper() - clusterConfig := gocql.NewCluster(address) - clusterConfig.Authenticator = gocql.PasswordAuthenticator{ - Username: username, - Password: password, - } - clusterConfig.ProtoVersion = 4 - clusterConfig.Port = port - clusterConfig.SslOpts = sslOpts - - session, err := clusterConfig.CreateSession() - if err != nil { - return err - } - defer session.Close() - return nil -} - -func assertNoCreds(t testing.TB, address string, port int, username, password string, sslOpts *gocql.SslOptions, timeout time.Duration) { - t.Helper() - - op := func() error { - // "Invert" the error so the backoff logic sees a failure to connect as a success - err := connect(t, address, port, username, password, sslOpts) - if err != nil { - return nil - } - return nil - } - bo := backoff.NewExponentialBackOff() - bo.MaxElapsedTime = timeout - bo.InitialInterval = 500 * time.Millisecond - bo.MaxInterval = bo.InitialInterval - bo.RandomizationFactor = 0.0 - - err := backoff.Retry(op, bo) - if err != nil { - t.Fatalf("successfully connected after %s when it shouldn't", timeout) - } -} - -const createUserStatements = `CREATE USER '{{username}}' WITH PASSWORD '{{password}}' NOSUPERUSER; -GRANT ALL PERMISSIONS ON ALL KEYSPACES TO '{{username}}';` diff --git a/plugins/database/cassandra/connection_producer.go b/plugins/database/cassandra/connection_producer.go deleted file mode 100644 index 78f8311fb..000000000 --- a/plugins/database/cassandra/connection_producer.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "context" - "crypto/tls" - "fmt" - "strings" - "sync" - "time" - - "github.com/gocql/gocql" - "github.com/hashicorp/go-secure-stdlib/parseutil" - "github.com/hashicorp/go-secure-stdlib/tlsutil" - dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" - "github.com/hashicorp/vault/sdk/database/helper/connutil" - "github.com/hashicorp/vault/sdk/database/helper/dbutil" - "github.com/mitchellh/mapstructure" -) - -// cassandraConnectionProducer implements ConnectionProducer and provides an -// interface for cassandra databases to make connections. -type cassandraConnectionProducer struct { - Hosts string `json:"hosts" structs:"hosts" mapstructure:"hosts"` - Port int `json:"port" structs:"port" mapstructure:"port"` - Username string `json:"username" structs:"username" mapstructure:"username"` - Password string `json:"password" structs:"password" mapstructure:"password"` - TLS bool `json:"tls" structs:"tls" mapstructure:"tls"` - InsecureTLS bool `json:"insecure_tls" structs:"insecure_tls" mapstructure:"insecure_tls"` - TLSServerName string `json:"tls_server_name" structs:"tls_server_name" mapstructure:"tls_server_name"` - ProtocolVersion int `json:"protocol_version" structs:"protocol_version" mapstructure:"protocol_version"` - ConnectTimeoutRaw interface{} `json:"connect_timeout" structs:"connect_timeout" mapstructure:"connect_timeout"` - SocketKeepAliveRaw interface{} `json:"socket_keep_alive" structs:"socket_keep_alive" mapstructure:"socket_keep_alive"` - TLSMinVersion string `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` - Consistency string `json:"consistency" structs:"consistency" mapstructure:"consistency"` - LocalDatacenter string `json:"local_datacenter" structs:"local_datacenter" mapstructure:"local_datacenter"` - PemBundle string `json:"pem_bundle" structs:"pem_bundle" mapstructure:"pem_bundle"` - PemJSON string `json:"pem_json" structs:"pem_json" mapstructure:"pem_json"` - SkipVerification bool `json:"skip_verification" structs:"skip_verification" mapstructure:"skip_verification"` - - connectTimeout time.Duration - socketKeepAlive time.Duration - sslOpts *gocql.SslOptions - rawConfig map[string]interface{} - - Initialized bool - Type string - session *gocql.Session - sync.Mutex -} - -func (c *cassandraConnectionProducer) Initialize(ctx context.Context, req dbplugin.InitializeRequest) error { - c.Lock() - defer c.Unlock() - - c.rawConfig = req.Config - - err := mapstructure.WeakDecode(req.Config, c) - if err != nil { - return err - } - - if c.ConnectTimeoutRaw == nil { - c.ConnectTimeoutRaw = "5s" - } - c.connectTimeout, err = parseutil.ParseDurationSecond(c.ConnectTimeoutRaw) - if err != nil { - return fmt.Errorf("invalid connect_timeout: %w", err) - } - - if c.SocketKeepAliveRaw == nil { - c.SocketKeepAliveRaw = "0s" - } - c.socketKeepAlive, err = parseutil.ParseDurationSecond(c.SocketKeepAliveRaw) - if err != nil { - return fmt.Errorf("invalid socket_keep_alive: %w", err) - } - - switch { - case len(c.Hosts) == 0: - return fmt.Errorf("hosts cannot be empty") - case len(c.Username) == 0: - return fmt.Errorf("username cannot be empty") - case len(c.Password) == 0: - return fmt.Errorf("password cannot be empty") - case len(c.PemJSON) > 0 && len(c.PemBundle) > 0: - return fmt.Errorf("cannot specify both pem_json and pem_bundle") - } - - var tlsMinVersion uint16 = tls.VersionTLS12 - if c.TLSMinVersion != "" { - ver, exists := tlsutil.TLSLookup[c.TLSMinVersion] - if !exists { - return fmt.Errorf("unrecognized TLS version [%s]", c.TLSMinVersion) - } - tlsMinVersion = ver - } - - switch { - case len(c.PemJSON) != 0: - cfg, err := jsonBundleToTLSConfig(c.PemJSON, tlsMinVersion, c.TLSServerName, c.InsecureTLS) - if err != nil { - return fmt.Errorf("failed to parse pem_json: %w", err) - } - c.sslOpts = &gocql.SslOptions{ - Config: cfg, - EnableHostVerification: !cfg.InsecureSkipVerify, - } - c.TLS = true - - case len(c.PemBundle) != 0: - cfg, err := pemBundleToTLSConfig(c.PemBundle, tlsMinVersion, c.TLSServerName, c.InsecureTLS) - if err != nil { - return fmt.Errorf("failed to parse pem_bundle: %w", err) - } - c.sslOpts = &gocql.SslOptions{ - Config: cfg, - EnableHostVerification: !cfg.InsecureSkipVerify, - } - c.TLS = true - - case c.InsecureTLS: - c.sslOpts = &gocql.SslOptions{ - EnableHostVerification: !c.InsecureTLS, - } - } - - // Set initialized to true at this point since all fields are set, - // and the connection can be established at a later time. - c.Initialized = true - - if req.VerifyConnection { - if _, err := c.Connection(ctx); err != nil { - return fmt.Errorf("error verifying connection: %w", err) - } - } - - return nil -} - -func (c *cassandraConnectionProducer) Connection(ctx context.Context) (interface{}, error) { - if !c.Initialized { - return nil, connutil.ErrNotInitialized - } - - // If we already have a DB, return it - if c.session != nil && !c.session.Closed() { - return c.session, nil - } - - session, err := c.createSession(ctx) - if err != nil { - return nil, err - } - - // Store the session in backend for reuse - c.session = session - - return session, nil -} - -func (c *cassandraConnectionProducer) Close() error { - c.Lock() - defer c.Unlock() - - if c.session != nil { - c.session.Close() - } - - c.session = nil - - return nil -} - -func (c *cassandraConnectionProducer) createSession(ctx context.Context) (*gocql.Session, error) { - hosts := strings.Split(c.Hosts, ",") - clusterConfig := gocql.NewCluster(hosts...) - clusterConfig.Authenticator = gocql.PasswordAuthenticator{ - Username: c.Username, - Password: c.Password, - } - - if c.Port != 0 { - clusterConfig.Port = c.Port - } - - clusterConfig.ProtoVersion = c.ProtocolVersion - if clusterConfig.ProtoVersion == 0 { - clusterConfig.ProtoVersion = 2 - } - - clusterConfig.Timeout = c.connectTimeout - clusterConfig.ConnectTimeout = c.connectTimeout - clusterConfig.SocketKeepalive = c.socketKeepAlive - clusterConfig.SslOpts = c.sslOpts - - if c.LocalDatacenter != "" { - clusterConfig.PoolConfig.HostSelectionPolicy = gocql.DCAwareRoundRobinPolicy(c.LocalDatacenter) - } - - session, err := clusterConfig.CreateSession() - if err != nil { - return nil, fmt.Errorf("error creating session: %w", err) - } - - if c.Consistency != "" { - consistencyValue, err := gocql.ParseConsistencyWrapper(c.Consistency) - if err != nil { - session.Close() - return nil, err - } - - session.SetConsistency(consistencyValue) - } - - if !c.SkipVerification { - err = session.Query(`LIST ALL`).WithContext(ctx).Exec() - if err != nil && len(c.Username) != 0 && strings.Contains(err.Error(), "not authorized") { - rowNum := session.Query(dbutil.QueryHelper(`LIST CREATE ON ALL ROLES OF '{{username}}';`, map[string]string{ - "username": c.Username, - })).Iter().NumRows() - - if rowNum < 1 { - session.Close() - return nil, fmt.Errorf("error validating connection info: No role create permissions found, previous error: %w", err) - } - } else if err != nil { - session.Close() - return nil, fmt.Errorf("error validating connection info: %w", err) - } - } - - return session, nil -} - -func (c *cassandraConnectionProducer) secretValues() map[string]string { - return map[string]string{ - c.Password: "[password]", - c.PemBundle: "[pem_bundle]", - c.PemJSON: "[pem_json]", - } -} diff --git a/plugins/database/cassandra/connection_producer_test.go b/plugins/database/cassandra/connection_producer_test.go deleted file mode 100644 index 306b444f4..000000000 --- a/plugins/database/cassandra/connection_producer_test.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "io/ioutil" - "testing" - "time" - - "github.com/gocql/gocql" - "github.com/hashicorp/vault/helper/testhelpers/cassandra" - "github.com/hashicorp/vault/sdk/database/dbplugin/v5" - dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing" - "github.com/hashicorp/vault/sdk/helper/certutil" - "github.com/stretchr/testify/require" -) - -var insecureFileMounts = map[string]string{ - "test-fixtures/no_tls/cassandra.yaml": "/etc/cassandra/cassandra.yaml", -} - -func TestSelfSignedCA(t *testing.T) { - copyFromTo := map[string]string{ - "test-fixtures/with_tls/stores": "/bitnami/cassandra/secrets/", - "test-fixtures/with_tls/cqlshrc": "/.cassandra/cqlshrc", - } - - tlsConfig := loadServerCA(t, "test-fixtures/with_tls/ca.pem") - // Note about CI behavior: when running these tests locally, they seem to pass without issue. However, if the - // ServerName is not set, the tests fail within CI. It's not entirely clear to me why they are failing in CI - // however by manually setting the ServerName we can get around the hostname/DNS issue and get them passing. - // Setting the ServerName isn't the ideal solution, but it was the only reliable one I was able to find - tlsConfig.ServerName = "cassandra" - sslOpts := &gocql.SslOptions{ - Config: tlsConfig, - EnableHostVerification: true, - } - - host, cleanup := cassandra.PrepareTestContainer(t, - cassandra.ContainerName("cassandra"), - cassandra.Image("bitnami/cassandra", "3.11.11"), - cassandra.CopyFromTo(copyFromTo), - cassandra.SslOpts(sslOpts), - cassandra.Env("CASSANDRA_KEYSTORE_PASSWORD=cassandra"), - cassandra.Env("CASSANDRA_TRUSTSTORE_PASSWORD=cassandra"), - cassandra.Env("CASSANDRA_INTERNODE_ENCRYPTION=none"), - cassandra.Env("CASSANDRA_CLIENT_ENCRYPTION=true"), - ) - t.Cleanup(cleanup) - - type testCase struct { - config map[string]interface{} - expectErr bool - } - - caPEM := loadFile(t, "test-fixtures/with_tls/ca.pem") - badCAPEM := loadFile(t, "test-fixtures/with_tls/bad_ca.pem") - - tests := map[string]testCase{ - // /////////////////////// - // pem_json tests - "pem_json/ca only": { - config: map[string]interface{}{ - "pem_json": toJSON(t, certutil.CertBundle{ - CAChain: []string{caPEM}, - }), - }, - expectErr: false, - }, - "pem_json/bad ca": { - config: map[string]interface{}{ - "pem_json": toJSON(t, certutil.CertBundle{ - CAChain: []string{badCAPEM}, - }), - }, - expectErr: true, - }, - "pem_json/missing ca": { - config: map[string]interface{}{ - "pem_json": "", - }, - expectErr: true, - }, - - // /////////////////////// - // pem_bundle tests - "pem_bundle/ca only": { - config: map[string]interface{}{ - "pem_bundle": caPEM, - }, - expectErr: false, - }, - "pem_bundle/unrecognized CA": { - config: map[string]interface{}{ - "pem_bundle": badCAPEM, - }, - expectErr: true, - }, - "pem_bundle/missing ca": { - config: map[string]interface{}{ - "pem_bundle": "", - }, - expectErr: true, - }, - - // /////////////////////// - // no cert data provided - "no cert data/tls=true": { - config: map[string]interface{}{ - "tls": "true", - }, - expectErr: true, - }, - "no cert data/tls=false": { - config: map[string]interface{}{ - "tls": "false", - }, - expectErr: true, - }, - "no cert data/insecure_tls": { - config: map[string]interface{}{ - "insecure_tls": "true", - }, - expectErr: false, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - // Set values that we don't know until the cassandra container is started - config := map[string]interface{}{ - "hosts": host.Name, - "port": host.Port, - "username": "cassandra", - "password": "cassandra", - "protocol_version": "4", - "connect_timeout": "30s", - "tls": "true", - - // Note about CI behavior: when running these tests locally, they seem to pass without issue. However, if the - // tls_server_name is not set, the tests fail within CI. It's not entirely clear to me why they are failing in CI - // however by manually setting the tls_server_name we can get around the hostname/DNS issue and get them passing. - // Setting the tls_server_name isn't the ideal solution, but it was the only reliable one I was able to find - "tls_server_name": "cassandra", - } - - // Apply the generated & common fields to the config to be sent to the DB - for k, v := range test.config { - config[k] = v - } - - db := new() - initReq := dbplugin.InitializeRequest{ - Config: config, - VerifyConnection: true, - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - _, err := db.Initialize(ctx, initReq) - if test.expectErr && err == nil { - t.Fatalf("err expected, got nil") - } - if !test.expectErr && err != nil { - t.Fatalf("no error expected, got: %s", err) - } - - // If no error expected, run a NewUser query to make sure the connection - // actually works in case Initialize doesn't catch it - if !test.expectErr { - assertNewUser(t, db, sslOpts) - } - }) - } -} - -func assertNewUser(t *testing.T, db *Cassandra, sslOpts *gocql.SslOptions) { - newUserReq := dbplugin.NewUserRequest{ - UsernameConfig: dbplugin.UsernameMetadata{ - DisplayName: "dispname", - RoleName: "rolename", - }, - Statements: dbplugin.Statements{ - Commands: []string{ - "create user '{{username}}' with password '{{password}}'", - }, - }, - RollbackStatements: dbplugin.Statements{}, - Password: "gh8eruajASDFAsgy89svn", - Expiration: time.Now().Add(5 * time.Second), - } - - newUserResp := dbtesting.AssertNewUser(t, db, newUserReq) - t.Logf("Username: %s", newUserResp.Username) - - assertCreds(t, db.Hosts, db.Port, newUserResp.Username, newUserReq.Password, sslOpts, 5*time.Second) -} - -func loadServerCA(t *testing.T, file string) *tls.Config { - t.Helper() - - pemData, err := ioutil.ReadFile(file) - require.NoError(t, err) - - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(pemData) - - config := &tls.Config{ - RootCAs: pool, - } - return config -} - -func loadFile(t *testing.T, filename string) string { - t.Helper() - - contents, err := ioutil.ReadFile(filename) - require.NoError(t, err) - return string(contents) -} - -func toJSON(t *testing.T, val interface{}) string { - t.Helper() - b, err := json.Marshal(val) - require.NoError(t, err) - return string(b) -} diff --git a/plugins/database/cassandra/test-fixtures/no_tls/cassandra.yaml b/plugins/database/cassandra/test-fixtures/no_tls/cassandra.yaml deleted file mode 100644 index a55afc693..000000000 --- a/plugins/database/cassandra/test-fixtures/no_tls/cassandra.yaml +++ /dev/null @@ -1,1149 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: BUSL-1.1 - -# Cassandra storage config YAML - -# NOTE: -# See http://wiki.apache.org/cassandra/StorageConfiguration for -# full explanations of configuration directives -# /NOTE - -# The name of the cluster. This is mainly used to prevent machines in -# one logical cluster from joining another. -cluster_name: 'Test Cluster' - -# This defines the number of tokens randomly assigned to this node on the ring -# The more tokens, relative to other nodes, the larger the proportion of data -# that this node will store. You probably want all nodes to have the same number -# of tokens assuming they have equal hardware capability. -# -# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility, -# and will use the initial_token as described below. -# -# Specifying initial_token will override this setting on the node's initial start, -# on subsequent starts, this setting will apply even if initial token is set. -# -# If you already have a cluster with 1 token per node, and wish to migrate to -# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations -num_tokens: 256 - -# Triggers automatic allocation of num_tokens tokens for this node. The allocation -# algorithm attempts to choose tokens in a way that optimizes replicated load over -# the nodes in the datacenter for the replication strategy used by the specified -# keyspace. -# -# The load assigned to each node will be close to proportional to its number of -# vnodes. -# -# Only supported with the Murmur3Partitioner. -# allocate_tokens_for_keyspace: KEYSPACE - -# initial_token allows you to specify tokens manually. While you can use it with -# vnodes (num_tokens > 1, above) -- in which case you should provide a -# comma-separated list -- it's primarily used when adding nodes to legacy clusters -# that do not have vnodes enabled. -# initial_token: - -# See http://wiki.apache.org/cassandra/HintedHandoff -# May either be "true" or "false" to enable globally -hinted_handoff_enabled: true - -# When hinted_handoff_enabled is true, a black list of data centers that will not -# perform hinted handoff -# hinted_handoff_disabled_datacenters: -# - DC1 -# - DC2 - -# this defines the maximum amount of time a dead host will have hints -# generated. After it has been dead this long, new hints for it will not be -# created until it has been seen alive and gone down again. -max_hint_window_in_ms: 10800000 # 3 hours - -# Maximum throttle in KBs per second, per delivery thread. This will be -# reduced proportionally to the number of nodes in the cluster. (If there -# are two nodes in the cluster, each delivery thread will use the maximum -# rate; if there are three, each will throttle to half of the maximum, -# since we expect two nodes to be delivering hints simultaneously.) -hinted_handoff_throttle_in_kb: 1024 - -# Number of threads with which to deliver hints; -# Consider increasing this number when you have multi-dc deployments, since -# cross-dc handoff tends to be slower -max_hints_delivery_threads: 2 - -# Directory where Cassandra should store hints. -# If not set, the default directory is $CASSANDRA_HOME/data/hints. -# hints_directory: /var/lib/cassandra/hints - -# How often hints should be flushed from the internal buffers to disk. -# Will *not* trigger fsync. -hints_flush_period_in_ms: 10000 - -# Maximum size for a single hints file, in megabytes. -max_hints_file_size_in_mb: 128 - -# Compression to apply to the hint files. If omitted, hints files -# will be written uncompressed. LZ4, Snappy, and Deflate compressors -# are supported. -#hints_compression: -# - class_name: LZ4Compressor -# parameters: -# - - -# Maximum throttle in KBs per second, total. This will be -# reduced proportionally to the number of nodes in the cluster. -batchlog_replay_throttle_in_kb: 1024 - -# Authentication backend, implementing IAuthenticator; used to identify users -# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator, -# PasswordAuthenticator}. -# -# - AllowAllAuthenticator performs no checks - set it to disable authentication. -# - PasswordAuthenticator relies on username/password pairs to authenticate -# users. It keeps usernames and hashed passwords in system_auth.credentials table. -# Please increase system_auth keyspace replication factor if you use this authenticator. -# If using PasswordAuthenticator, CassandraRoleManager must also be used (see below) -authenticator: PasswordAuthenticator - -# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions -# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer, -# CassandraAuthorizer}. -# -# - AllowAllAuthorizer allows any action to any user - set it to disable authorization. -# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please -# increase system_auth keyspace replication factor if you use this authorizer. -authorizer: CassandraAuthorizer - -# Part of the Authentication & Authorization backend, implementing IRoleManager; used -# to maintain grants and memberships between roles. -# Out of the box, Cassandra provides org.apache.cassandra.auth.CassandraRoleManager, -# which stores role information in the system_auth keyspace. Most functions of the -# IRoleManager require an authenticated login, so unless the configured IAuthenticator -# actually implements authentication, most of this functionality will be unavailable. -# -# - CassandraRoleManager stores role data in the system_auth keyspace. Please -# increase system_auth keyspace replication factor if you use this role manager. -role_manager: CassandraRoleManager - -# Validity period for roles cache (fetching granted roles can be an expensive -# operation depending on the role manager, CassandraRoleManager is one example) -# Granted roles are cached for authenticated sessions in AuthenticatedUser and -# after the period specified here, become eligible for (async) reload. -# Defaults to 2000, set to 0 to disable caching entirely. -# Will be disabled automatically for AllowAllAuthenticator. -roles_validity_in_ms: 2000 - -# Refresh interval for roles cache (if enabled). -# After this interval, cache entries become eligible for refresh. Upon next -# access, an async reload is scheduled and the old value returned until it -# completes. If roles_validity_in_ms is non-zero, then this must be -# also. -# Defaults to the same value as roles_validity_in_ms. -# roles_update_interval_in_ms: 2000 - -# Validity period for permissions cache (fetching permissions can be an -# expensive operation depending on the authorizer, CassandraAuthorizer is -# one example). Defaults to 2000, set to 0 to disable. -# Will be disabled automatically for AllowAllAuthorizer. -permissions_validity_in_ms: 2000 - -# Refresh interval for permissions cache (if enabled). -# After this interval, cache entries become eligible for refresh. Upon next -# access, an async reload is scheduled and the old value returned until it -# completes. If permissions_validity_in_ms is non-zero, then this must be -# also. -# Defaults to the same value as permissions_validity_in_ms. -# permissions_update_interval_in_ms: 2000 - -# Validity period for credentials cache. This cache is tightly coupled to -# the provided PasswordAuthenticator implementation of IAuthenticator. If -# another IAuthenticator implementation is configured, this cache will not -# be automatically used and so the following settings will have no effect. -# Please note, credentials are cached in their encrypted form, so while -# activating this cache may reduce the number of queries made to the -# underlying table, it may not bring a significant reduction in the -# latency of individual authentication attempts. -# Defaults to 2000, set to 0 to disable credentials caching. -credentials_validity_in_ms: 2000 - -# Refresh interval for credentials cache (if enabled). -# After this interval, cache entries become eligible for refresh. Upon next -# access, an async reload is scheduled and the old value returned until it -# completes. If credentials_validity_in_ms is non-zero, then this must be -# also. -# Defaults to the same value as credentials_validity_in_ms. -# credentials_update_interval_in_ms: 2000 - -# The partitioner is responsible for distributing groups of rows (by -# partition key) across nodes in the cluster. You should leave this -# alone for new clusters. The partitioner can NOT be changed without -# reloading all data, so when upgrading you should set this to the -# same partitioner you were already using. -# -# Besides Murmur3Partitioner, partitioners included for backwards -# compatibility include RandomPartitioner, ByteOrderedPartitioner, and -# OrderPreservingPartitioner. -# -partitioner: org.apache.cassandra.dht.Murmur3Partitioner - -# Directories where Cassandra should store data on disk. Cassandra -# will spread data evenly across them, subject to the granularity of -# the configured compaction strategy. -# If not set, the default directory is $CASSANDRA_HOME/data/data. -data_file_directories: - - /var/lib/cassandra/data - -# commit log. when running on magnetic HDD, this should be a -# separate spindle than the data directories. -# If not set, the default directory is $CASSANDRA_HOME/data/commitlog. -commitlog_directory: /var/lib/cassandra/commitlog - -# Enable / disable CDC functionality on a per-node basis. This modifies the logic used -# for write path allocation rejection (standard: never reject. cdc: reject Mutation -# containing a CDC-enabled table if at space limit in cdc_raw_directory). -cdc_enabled: false - -# CommitLogSegments are moved to this directory on flush if cdc_enabled: true and the -# segment contains mutations for a CDC-enabled table. This should be placed on a -# separate spindle than the data directories. If not set, the default directory is -# $CASSANDRA_HOME/data/cdc_raw. -# cdc_raw_directory: /var/lib/cassandra/cdc_raw - -# Policy for data disk failures: -# -# die -# shut down gossip and client transports and kill the JVM for any fs errors or -# single-sstable errors, so the node can be replaced. -# -# stop_paranoid -# shut down gossip and client transports even for single-sstable errors, -# kill the JVM for errors during startup. -# -# stop -# shut down gossip and client transports, leaving the node effectively dead, but -# can still be inspected via JMX, kill the JVM for errors during startup. -# -# best_effort -# stop using the failed disk and respond to requests based on -# remaining available sstables. This means you WILL see obsolete -# data at CL.ONE! -# -# ignore -# ignore fatal errors and let requests fail, as in pre-1.2 Cassandra -disk_failure_policy: stop - -# Policy for commit disk failures: -# -# die -# shut down gossip and Thrift and kill the JVM, so the node can be replaced. -# -# stop -# shut down gossip and Thrift, leaving the node effectively dead, but -# can still be inspected via JMX. -# -# stop_commit -# shutdown the commit log, letting writes collect but -# continuing to service reads, as in pre-2.0.5 Cassandra -# -# ignore -# ignore fatal errors and let the batches fail -commit_failure_policy: stop - -# Maximum size of the native protocol prepared statement cache -# -# Valid values are either "auto" (omitting the value) or a value greater 0. -# -# Note that specifying a too large value will result in long running GCs and possibly -# out-of-memory errors. Keep the value at a small fraction of the heap. -# -# If you constantly see "prepared statements discarded in the last minute because -# cache limit reached" messages, the first step is to investigate the root cause -# of these messages and check whether prepared statements are used correctly - -# i.e. use bind markers for variable parts. -# -# Do only change the default value, if you really have more prepared statements than -# fit in the cache. In most cases it is not necessary to change this value. -# Constantly re-preparing statements is a performance penalty. -# -# Default value ("auto") is 1/256th of the heap or 10MB, whichever is greater -prepared_statements_cache_size_mb: - -# Maximum size of the Thrift prepared statement cache -# -# If you do not use Thrift at all, it is safe to leave this value at "auto". -# -# See description of 'prepared_statements_cache_size_mb' above for more information. -# -# Default value ("auto") is 1/256th of the heap or 10MB, whichever is greater -thrift_prepared_statements_cache_size_mb: - -# Maximum size of the key cache in memory. -# -# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the -# minimum, sometimes more. The key cache is fairly tiny for the amount of -# time it saves, so it's worthwhile to use it at large numbers. -# The row cache saves even more time, but must contain the entire row, -# so it is extremely space-intensive. It's best to only use the -# row cache if you have hot rows or static rows. -# -# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup. -# -# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache. -key_cache_size_in_mb: - -# Duration in seconds after which Cassandra should -# save the key cache. Caches are saved to saved_caches_directory as -# specified in this configuration file. -# -# Saved caches greatly improve cold-start speeds, and is relatively cheap in -# terms of I/O for the key cache. Row cache saving is much more expensive and -# has limited use. -# -# Default is 14400 or 4 hours. -key_cache_save_period: 14400 - -# Number of keys from the key cache to save -# Disabled by default, meaning all keys are going to be saved -# key_cache_keys_to_save: 100 - -# Row cache implementation class name. Available implementations: -# -# org.apache.cassandra.cache.OHCProvider -# Fully off-heap row cache implementation (default). -# -# org.apache.cassandra.cache.SerializingCacheProvider -# This is the row cache implementation availabile -# in previous releases of Cassandra. -# row_cache_class_name: org.apache.cassandra.cache.OHCProvider - -# Maximum size of the row cache in memory. -# Please note that OHC cache implementation requires some additional off-heap memory to manage -# the map structures and some in-flight memory during operations before/after cache entries can be -# accounted against the cache capacity. This overhead is usually small compared to the whole capacity. -# Do not specify more memory that the system can afford in the worst usual situation and leave some -# headroom for OS block level cache. Do never allow your system to swap. -# -# Default value is 0, to disable row caching. -row_cache_size_in_mb: 0 - -# Duration in seconds after which Cassandra should save the row cache. -# Caches are saved to saved_caches_directory as specified in this configuration file. -# -# Saved caches greatly improve cold-start speeds, and is relatively cheap in -# terms of I/O for the key cache. Row cache saving is much more expensive and -# has limited use. -# -# Default is 0 to disable saving the row cache. -row_cache_save_period: 0 - -# Number of keys from the row cache to save. -# Specify 0 (which is the default), meaning all keys are going to be saved -# row_cache_keys_to_save: 100 - -# Maximum size of the counter cache in memory. -# -# Counter cache helps to reduce counter locks' contention for hot counter cells. -# In case of RF = 1 a counter cache hit will cause Cassandra to skip the read before -# write entirely. With RF > 1 a counter cache hit will still help to reduce the duration -# of the lock hold, helping with hot counter cell updates, but will not allow skipping -# the read entirely. Only the local (clock, count) tuple of a counter cell is kept -# in memory, not the whole counter, so it's relatively cheap. -# -# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup. -# -# Default value is empty to make it "auto" (min(2.5% of Heap (in MB), 50MB)). Set to 0 to disable counter cache. -# NOTE: if you perform counter deletes and rely on low gcgs, you should disable the counter cache. -counter_cache_size_in_mb: - -# Duration in seconds after which Cassandra should -# save the counter cache (keys only). Caches are saved to saved_caches_directory as -# specified in this configuration file. -# -# Default is 7200 or 2 hours. -counter_cache_save_period: 7200 - -# Number of keys from the counter cache to save -# Disabled by default, meaning all keys are going to be saved -# counter_cache_keys_to_save: 100 - -# saved caches -# If not set, the default directory is $CASSANDRA_HOME/data/saved_caches. -saved_caches_directory: /var/lib/cassandra/saved_caches - -# commitlog_sync may be either "periodic" or "batch." -# -# When in batch mode, Cassandra won't ack writes until the commit log -# has been fsynced to disk. It will wait -# commitlog_sync_batch_window_in_ms milliseconds between fsyncs. -# This window should be kept short because the writer threads will -# be unable to do extra work while waiting. (You may need to increase -# concurrent_writes for the same reason.) -# -# commitlog_sync: batch -# commitlog_sync_batch_window_in_ms: 2 -# -# the other option is "periodic" where writes may be acked immediately -# and the CommitLog is simply synced every commitlog_sync_period_in_ms -# milliseconds. -commitlog_sync: periodic -commitlog_sync_period_in_ms: 10000 - -# The size of the individual commitlog file segments. A commitlog -# segment may be archived, deleted, or recycled once all the data -# in it (potentially from each columnfamily in the system) has been -# flushed to sstables. -# -# The default size is 32, which is almost always fine, but if you are -# archiving commitlog segments (see commitlog_archiving.properties), -# then you probably want a finer granularity of archiving; 8 or 16 MB -# is reasonable. -# Max mutation size is also configurable via max_mutation_size_in_kb setting in -# cassandra.yaml. The default is half the size commitlog_segment_size_in_mb * 1024. -# -# NOTE: If max_mutation_size_in_kb is set explicitly then commitlog_segment_size_in_mb must -# be set to at least twice the size of max_mutation_size_in_kb / 1024 -# -commitlog_segment_size_in_mb: 32 - -# Compression to apply to the commit log. If omitted, the commit log -# will be written uncompressed. LZ4, Snappy, and Deflate compressors -# are supported. -# commitlog_compression: -# - class_name: LZ4Compressor -# parameters: -# - - -# any class that implements the SeedProvider interface and has a -# constructor that takes a Map of parameters will do. -seed_provider: - # Addresses of hosts that are deemed contact points. - # Cassandra nodes use this list of hosts to find each other and learn - # the topology of the ring. You must change this if you are running - # multiple nodes! - - class_name: org.apache.cassandra.locator.SimpleSeedProvider - parameters: - # seeds is actually a comma-delimited list of addresses. - # Ex: ",," - - seeds: "127.0.0.1" - -# For workloads with more data than can fit in memory, Cassandra's -# bottleneck will be reads that need to fetch data from -# disk. "concurrent_reads" should be set to (16 * number_of_drives) in -# order to allow the operations to enqueue low enough in the stack -# that the OS and drives can reorder them. Same applies to -# "concurrent_counter_writes", since counter writes read the current -# values before incrementing and writing them back. -# -# On the other hand, since writes are almost never IO bound, the ideal -# number of "concurrent_writes" is dependent on the number of cores in -# your system; (8 * number_of_cores) is a good rule of thumb. -concurrent_reads: 32 -concurrent_writes: 32 -concurrent_counter_writes: 32 - -# For materialized view writes, as there is a read involved, so this should -# be limited by the less of concurrent reads or concurrent writes. -concurrent_materialized_view_writes: 32 - -# Maximum memory to use for sstable chunk cache and buffer pooling. -# 32MB of this are reserved for pooling buffers, the rest is used as an -# cache that holds uncompressed sstable chunks. -# Defaults to the smaller of 1/4 of heap or 512MB. This pool is allocated off-heap, -# so is in addition to the memory allocated for heap. The cache also has on-heap -# overhead which is roughly 128 bytes per chunk (i.e. 0.2% of the reserved size -# if the default 64k chunk size is used). -# Memory is only allocated when needed. -# file_cache_size_in_mb: 512 - -# Flag indicating whether to allocate on or off heap when the sstable buffer -# pool is exhausted, that is when it has exceeded the maximum memory -# file_cache_size_in_mb, beyond which it will not cache buffers but allocate on request. - -# buffer_pool_use_heap_if_exhausted: true - -# The strategy for optimizing disk read -# Possible values are: -# ssd (for solid state disks, the default) -# spinning (for spinning disks) -# disk_optimization_strategy: ssd - -# Total permitted memory to use for memtables. Cassandra will stop -# accepting writes when the limit is exceeded until a flush completes, -# and will trigger a flush based on memtable_cleanup_threshold -# If omitted, Cassandra will set both to 1/4 the size of the heap. -# memtable_heap_space_in_mb: 2048 -# memtable_offheap_space_in_mb: 2048 - -# Ratio of occupied non-flushing memtable size to total permitted size -# that will trigger a flush of the largest memtable. Larger mct will -# mean larger flushes and hence less compaction, but also less concurrent -# flush activity which can make it difficult to keep your disks fed -# under heavy write load. -# -# memtable_cleanup_threshold defaults to 1 / (memtable_flush_writers + 1) -# memtable_cleanup_threshold: 0.11 - -# Specify the way Cassandra allocates and manages memtable memory. -# Options are: -# -# heap_buffers -# on heap nio buffers -# -# offheap_buffers -# off heap (direct) nio buffers -# -# offheap_objects -# off heap objects -memtable_allocation_type: heap_buffers - -# Total space to use for commit logs on disk. -# -# If space gets above this value, Cassandra will flush every dirty CF -# in the oldest segment and remove it. So a small total commitlog space -# will tend to cause more flush activity on less-active columnfamilies. -# -# The default value is the smaller of 8192, and 1/4 of the total space -# of the commitlog volume. -# -# commitlog_total_space_in_mb: 8192 - -# This sets the amount of memtable flush writer threads. These will -# be blocked by disk io, and each one will hold a memtable in memory -# while blocked. -# -# memtable_flush_writers defaults to one per data_file_directory. -# -# If your data directories are backed by SSD, you can increase this, but -# avoid having memtable_flush_writers * data_file_directories > number of cores -#memtable_flush_writers: 1 - -# Total space to use for change-data-capture logs on disk. -# -# If space gets above this value, Cassandra will throw WriteTimeoutException -# on Mutations including tables with CDC enabled. A CDCCompactor is responsible -# for parsing the raw CDC logs and deleting them when parsing is completed. -# -# The default value is the min of 4096 mb and 1/8th of the total space -# of the drive where cdc_raw_directory resides. -# cdc_total_space_in_mb: 4096 - -# When we hit our cdc_raw limit and the CDCCompactor is either running behind -# or experiencing backpressure, we check at the following interval to see if any -# new space for cdc-tracked tables has been made available. Default to 250ms -# cdc_free_space_check_interval_ms: 250 - -# A fixed memory pool size in MB for for SSTable index summaries. If left -# empty, this will default to 5% of the heap size. If the memory usage of -# all index summaries exceeds this limit, SSTables with low read rates will -# shrink their index summaries in order to meet this limit. However, this -# is a best-effort process. In extreme conditions Cassandra may need to use -# more than this amount of memory. -index_summary_capacity_in_mb: - -# How frequently index summaries should be resampled. This is done -# periodically to redistribute memory from the fixed-size pool to sstables -# proportional their recent read rates. Setting to -1 will disable this -# process, leaving existing index summaries at their current sampling level. -index_summary_resize_interval_in_minutes: 60 - -# Whether to, when doing sequential writing, fsync() at intervals in -# order to force the operating system to flush the dirty -# buffers. Enable this to avoid sudden dirty buffer flushing from -# impacting read latencies. Almost always a good idea on SSDs; not -# necessarily on platters. -trickle_fsync: false -trickle_fsync_interval_in_kb: 10240 - -# TCP port, for commands and data -# For security reasons, you should not expose this port to the internet. Firewall it if needed. -storage_port: 7000 - -# SSL port, for encrypted communication. Unused unless enabled in -# encryption_options -# For security reasons, you should not expose this port to the internet. Firewall it if needed. -ssl_storage_port: 7001 - -# Address or interface to bind to and tell other Cassandra nodes to connect to. -# You _must_ change this if you want multiple nodes to be able to communicate! -# -# Set listen_address OR listen_interface, not both. -# -# Leaving it blank leaves it up to InetAddress.getLocalHost(). This -# will always do the Right Thing _if_ the node is properly configured -# (hostname, name resolution, etc), and the Right Thing is to use the -# address associated with the hostname (it might not be). -# -# Setting listen_address to 0.0.0.0 is always wrong. -# -listen_address: 172.17.0.3 - -# Set listen_address OR listen_interface, not both. Interfaces must correspond -# to a single address, IP aliasing is not supported. -# listen_interface: eth0 - -# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address -# you can specify which should be chosen using listen_interface_prefer_ipv6. If false the first ipv4 -# address will be used. If true the first ipv6 address will be used. Defaults to false preferring -# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6. -# listen_interface_prefer_ipv6: false - -# Address to broadcast to other Cassandra nodes -# Leaving this blank will set it to the same value as listen_address -broadcast_address: 127.0.0.1 - -# When using multiple physical network interfaces, set this -# to true to listen on broadcast_address in addition to -# the listen_address, allowing nodes to communicate in both -# interfaces. -# Ignore this property if the network configuration automatically -# routes between the public and private networks such as EC2. -# listen_on_broadcast_address: false - -# Internode authentication backend, implementing IInternodeAuthenticator; -# used to allow/disallow connections from peer nodes. -# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator - -# Whether to start the native transport server. -# Please note that the address on which the native transport is bound is the -# same as the rpc_address. The port however is different and specified below. -start_native_transport: true -# port for the CQL native transport to listen for clients on -# For security reasons, you should not expose this port to the internet. Firewall it if needed. -native_transport_port: 9042 -# Enabling native transport encryption in client_encryption_options allows you to either use -# encryption for the standard port or to use a dedicated, additional port along with the unencrypted -# standard native_transport_port. -# Enabling client encryption and keeping native_transport_port_ssl disabled will use encryption -# for native_transport_port. Setting native_transport_port_ssl to a different value -# from native_transport_port will use encryption for native_transport_port_ssl while -# keeping native_transport_port unencrypted. -# native_transport_port_ssl: 9142 -# The maximum threads for handling requests when the native transport is used. -# This is similar to rpc_max_threads though the default differs slightly (and -# there is no native_transport_min_threads, idle threads will always be stopped -# after 30 seconds). -# native_transport_max_threads: 128 -# -# The maximum size of allowed frame. Frame (requests) larger than this will -# be rejected as invalid. The default is 256MB. If you're changing this parameter, -# you may want to adjust max_value_size_in_mb accordingly. -# native_transport_max_frame_size_in_mb: 256 - -# The maximum number of concurrent client connections. -# The default is -1, which means unlimited. -# native_transport_max_concurrent_connections: -1 - -# The maximum number of concurrent client connections per source ip. -# The default is -1, which means unlimited. -# native_transport_max_concurrent_connections_per_ip: -1 - -# Whether to start the thrift rpc server. -start_rpc: false - -# The address or interface to bind the Thrift RPC service and native transport -# server to. -# -# Set rpc_address OR rpc_interface, not both. -# -# Leaving rpc_address blank has the same effect as on listen_address -# (i.e. it will be based on the configured hostname of the node). -# -# Note that unlike listen_address, you can specify 0.0.0.0, but you must also -# set broadcast_rpc_address to a value other than 0.0.0.0. -# -# For security reasons, you should not expose this port to the internet. Firewall it if needed. -rpc_address: 0.0.0.0 - -# Set rpc_address OR rpc_interface, not both. Interfaces must correspond -# to a single address, IP aliasing is not supported. -# rpc_interface: eth1 - -# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address -# you can specify which should be chosen using rpc_interface_prefer_ipv6. If false the first ipv4 -# address will be used. If true the first ipv6 address will be used. Defaults to false preferring -# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6. -# rpc_interface_prefer_ipv6: false - -# port for Thrift to listen for clients on -rpc_port: 9160 - -# RPC address to broadcast to drivers and other Cassandra nodes. This cannot -# be set to 0.0.0.0. If left blank, this will be set to the value of -# rpc_address. If rpc_address is set to 0.0.0.0, broadcast_rpc_address must -# be set. -broadcast_rpc_address: 127.0.0.1 - -# enable or disable keepalive on rpc/native connections -rpc_keepalive: true - -# Cassandra provides two out-of-the-box options for the RPC Server: -# -# sync -# One thread per thrift connection. For a very large number of clients, memory -# will be your limiting factor. On a 64 bit JVM, 180KB is the minimum stack size -# per thread, and that will correspond to your use of virtual memory (but physical memory -# may be limited depending on use of stack space). -# -# hsha -# Stands for "half synchronous, half asynchronous." All thrift clients are handled -# asynchronously using a small number of threads that does not vary with the amount -# of thrift clients (and thus scales well to many clients). The rpc requests are still -# synchronous (one thread per active request). If hsha is selected then it is essential -# that rpc_max_threads is changed from the default value of unlimited. -# -# The default is sync because on Windows hsha is about 30% slower. On Linux, -# sync/hsha performance is about the same, with hsha of course using less memory. -# -# Alternatively, can provide your own RPC server by providing the fully-qualified class name -# of an o.a.c.t.TServerFactory that can create an instance of it. -rpc_server_type: sync - -# Uncomment rpc_min|max_thread to set request pool size limits. -# -# Regardless of your choice of RPC server (see above), the number of maximum requests in the -# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync -# RPC server, it also dictates the number of clients that can be connected at all). -# -# The default is unlimited and thus provides no protection against clients overwhelming the server. You are -# encouraged to set a maximum that makes sense for you in production, but do keep in mind that -# rpc_max_threads represents the maximum number of client requests this server may execute concurrently. -# -# rpc_min_threads: 16 -# rpc_max_threads: 2048 - -# uncomment to set socket buffer sizes on rpc connections -# rpc_send_buff_size_in_bytes: -# rpc_recv_buff_size_in_bytes: - -# Uncomment to set socket buffer size for internode communication -# Note that when setting this, the buffer size is limited by net.core.wmem_max -# and when not setting it it is defined by net.ipv4.tcp_wmem -# See also: -# /proc/sys/net/core/wmem_max -# /proc/sys/net/core/rmem_max -# /proc/sys/net/ipv4/tcp_wmem -# /proc/sys/net/ipv4/tcp_wmem -# and 'man tcp' -# internode_send_buff_size_in_bytes: - -# Uncomment to set socket buffer size for internode communication -# Note that when setting this, the buffer size is limited by net.core.wmem_max -# and when not setting it it is defined by net.ipv4.tcp_wmem -# internode_recv_buff_size_in_bytes: - -# Frame size for thrift (maximum message length). -thrift_framed_transport_size_in_mb: 15 - -# Set to true to have Cassandra create a hard link to each sstable -# flushed or streamed locally in a backups/ subdirectory of the -# keyspace data. Removing these links is the operator's -# responsibility. -incremental_backups: false - -# Whether or not to take a snapshot before each compaction. Be -# careful using this option, since Cassandra won't clean up the -# snapshots for you. Mostly useful if you're paranoid when there -# is a data format change. -snapshot_before_compaction: false - -# Whether or not a snapshot is taken of the data before keyspace truncation -# or dropping of column families. The STRONGLY advised default of true -# should be used to provide data safety. If you set this flag to false, you will -# lose data on truncation or drop. -auto_snapshot: true - -# Granularity of the collation index of rows within a partition. -# Increase if your rows are large, or if you have a very large -# number of rows per partition. The competing goals are these: -# -# - a smaller granularity means more index entries are generated -# and looking up rows withing the partition by collation column -# is faster -# - but, Cassandra will keep the collation index in memory for hot -# rows (as part of the key cache), so a larger granularity means -# you can cache more hot rows -column_index_size_in_kb: 64 - -# Per sstable indexed key cache entries (the collation index in memory -# mentioned above) exceeding this size will not be held on heap. -# This means that only partition information is held on heap and the -# index entries are read from disk. -# -# Note that this size refers to the size of the -# serialized index information and not the size of the partition. -column_index_cache_size_in_kb: 2 - -# Number of simultaneous compactions to allow, NOT including -# validation "compactions" for anti-entropy repair. Simultaneous -# compactions can help preserve read performance in a mixed read/write -# workload, by mitigating the tendency of small sstables to accumulate -# during a single long running compactions. The default is usually -# fine and if you experience problems with compaction running too -# slowly or too fast, you should look at -# compaction_throughput_mb_per_sec first. -# -# concurrent_compactors defaults to the smaller of (number of disks, -# number of cores), with a minimum of 2 and a maximum of 8. -# -# If your data directories are backed by SSD, you should increase this -# to the number of cores. -#concurrent_compactors: 1 - -# Throttles compaction to the given total throughput across the entire -# system. The faster you insert data, the faster you need to compact in -# order to keep the sstable count down, but in general, setting this to -# 16 to 32 times the rate you are inserting data is more than sufficient. -# Setting this to 0 disables throttling. Note that this account for all types -# of compaction, including validation compaction. -compaction_throughput_mb_per_sec: 16 - -# When compacting, the replacement sstable(s) can be opened before they -# are completely written, and used in place of the prior sstables for -# any range that has been written. This helps to smoothly transfer reads -# between the sstables, reducing page cache churn and keeping hot rows hot -sstable_preemptive_open_interval_in_mb: 50 - -# Throttles all outbound streaming file transfers on this node to the -# given total throughput in Mbps. This is necessary because Cassandra does -# mostly sequential IO when streaming data during bootstrap or repair, which -# can lead to saturating the network connection and degrading rpc performance. -# When unset, the default is 200 Mbps or 25 MB/s. -# stream_throughput_outbound_megabits_per_sec: 200 - -# Throttles all streaming file transfer between the datacenters, -# this setting allows users to throttle inter dc stream throughput in addition -# to throttling all network stream traffic as configured with -# stream_throughput_outbound_megabits_per_sec -# When unset, the default is 200 Mbps or 25 MB/s -# inter_dc_stream_throughput_outbound_megabits_per_sec: 200 - -# How long the coordinator should wait for read operations to complete -read_request_timeout_in_ms: 5000 -# How long the coordinator should wait for seq or index scans to complete -range_request_timeout_in_ms: 10000 -# How long the coordinator should wait for writes to complete -write_request_timeout_in_ms: 2000 -# How long the coordinator should wait for counter writes to complete -counter_write_request_timeout_in_ms: 5000 -# How long a coordinator should continue to retry a CAS operation -# that contends with other proposals for the same row -cas_contention_timeout_in_ms: 1000 -# How long the coordinator should wait for truncates to complete -# (This can be much longer, because unless auto_snapshot is disabled -# we need to flush first so we can snapshot before removing the data.) -truncate_request_timeout_in_ms: 60000 -# The default timeout for other, miscellaneous operations -request_timeout_in_ms: 10000 - -# Enable operation timeout information exchange between nodes to accurately -# measure request timeouts. If disabled, replicas will assume that requests -# were forwarded to them instantly by the coordinator, which means that -# under overload conditions we will waste that much extra time processing -# already-timed-out requests. -# -# Warning: before enabling this property make sure to ntp is installed -# and the times are synchronized between the nodes. -cross_node_timeout: false - -# Set socket timeout for streaming operation. -# The stream session is failed if no data/ack is received by any of the participants -# within that period, which means this should also be sufficient to stream a large -# sstable or rebuild table indexes. -# Default value is 86400000ms, which means stale streams timeout after 24 hours. -# A value of zero means stream sockets should never time out. -# streaming_socket_timeout_in_ms: 86400000 - -# phi value that must be reached for a host to be marked down. -# most users should never need to adjust this. -# phi_convict_threshold: 8 - -# endpoint_snitch -- Set this to a class that implements -# IEndpointSnitch. The snitch has two functions: -# -# - it teaches Cassandra enough about your network topology to route -# requests efficiently -# - it allows Cassandra to spread replicas around your cluster to avoid -# correlated failures. It does this by grouping machines into -# "datacenters" and "racks." Cassandra will do its best not to have -# more than one replica on the same "rack" (which may not actually -# be a physical location) -# -# CASSANDRA WILL NOT ALLOW YOU TO SWITCH TO AN INCOMPATIBLE SNITCH -# ONCE DATA IS INSERTED INTO THE CLUSTER. This would cause data loss. -# This means that if you start with the default SimpleSnitch, which -# locates every node on "rack1" in "datacenter1", your only options -# if you need to add another datacenter are GossipingPropertyFileSnitch -# (and the older PFS). From there, if you want to migrate to an -# incompatible snitch like Ec2Snitch you can do it by adding new nodes -# under Ec2Snitch (which will locate them in a new "datacenter") and -# decommissioning the old ones. -# -# Out of the box, Cassandra provides: -# -# SimpleSnitch: -# Treats Strategy order as proximity. This can improve cache -# locality when disabling read repair. Only appropriate for -# single-datacenter deployments. -# -# GossipingPropertyFileSnitch -# This should be your go-to snitch for production use. The rack -# and datacenter for the local node are defined in -# cassandra-rackdc.properties and propagated to other nodes via -# gossip. If cassandra-topology.properties exists, it is used as a -# fallback, allowing migration from the PropertyFileSnitch. -# -# PropertyFileSnitch: -# Proximity is determined by rack and data center, which are -# explicitly configured in cassandra-topology.properties. -# -# Ec2Snitch: -# Appropriate for EC2 deployments in a single Region. Loads Region -# and Availability Zone information from the EC2 API. The Region is -# treated as the datacenter, and the Availability Zone as the rack. -# Only private IPs are used, so this will not work across multiple -# Regions. -# -# Ec2MultiRegionSnitch: -# Uses public IPs as broadcast_address to allow cross-region -# connectivity. (Thus, you should set seed addresses to the public -# IP as well.) You will need to open the storage_port or -# ssl_storage_port on the public IP firewall. (For intra-Region -# traffic, Cassandra will switch to the private IP after -# establishing a connection.) -# -# RackInferringSnitch: -# Proximity is determined by rack and data center, which are -# assumed to correspond to the 3rd and 2nd octet of each node's IP -# address, respectively. Unless this happens to match your -# deployment conventions, this is best used as an example of -# writing a custom Snitch class and is provided in that spirit. -# -# You can use a custom Snitch by setting this to the full class name -# of the snitch, which will be assumed to be on your classpath. -endpoint_snitch: SimpleSnitch - -# controls how often to perform the more expensive part of host score -# calculation -dynamic_snitch_update_interval_in_ms: 100 -# controls how often to reset all host scores, allowing a bad host to -# possibly recover -dynamic_snitch_reset_interval_in_ms: 600000 -# if set greater than zero and read_repair_chance is < 1.0, this will allow -# 'pinning' of replicas to hosts in order to increase cache capacity. -# The badness threshold will control how much worse the pinned host has to be -# before the dynamic snitch will prefer other replicas over it. This is -# expressed as a double which represents a percentage. Thus, a value of -# 0.2 means Cassandra would continue to prefer the static snitch values -# until the pinned host was 20% worse than the fastest. -dynamic_snitch_badness_threshold: 0.1 - -# request_scheduler -- Set this to a class that implements -# RequestScheduler, which will schedule incoming client requests -# according to the specific policy. This is useful for multi-tenancy -# with a single Cassandra cluster. -# NOTE: This is specifically for requests from the client and does -# not affect inter node communication. -# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place -# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of -# client requests to a node with a separate queue for each -# request_scheduler_id. The scheduler is further customized by -# request_scheduler_options as described below. -request_scheduler: org.apache.cassandra.scheduler.NoScheduler - -# Scheduler Options vary based on the type of scheduler -# -# NoScheduler -# Has no options -# -# RoundRobin -# throttle_limit -# The throttle_limit is the number of in-flight -# requests per client. Requests beyond -# that limit are queued up until -# running requests can complete. -# The value of 80 here is twice the number of -# concurrent_reads + concurrent_writes. -# default_weight -# default_weight is optional and allows for -# overriding the default which is 1. -# weights -# Weights are optional and will default to 1 or the -# overridden default_weight. The weight translates into how -# many requests are handled during each turn of the -# RoundRobin, based on the scheduler id. -# -# request_scheduler_options: -# throttle_limit: 80 -# default_weight: 5 -# weights: -# Keyspace1: 1 -# Keyspace2: 5 - -# request_scheduler_id -- An identifier based on which to perform -# the request scheduling. Currently the only valid option is keyspace. -# request_scheduler_id: keyspace - -# Enable or disable inter-node encryption -# JVM defaults for supported SSL socket protocols and cipher suites can -# be replaced using custom encryption options. This is not recommended -# unless you have policies in place that dictate certain settings, or -# need to disable vulnerable ciphers or protocols in case the JVM cannot -# be updated. -# FIPS compliant settings can be configured at JVM level and should not -# involve changing encryption settings here: -# https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/FIPS.html -# *NOTE* No custom encryption options are enabled at the moment -# The available internode options are : all, none, dc, rack -# -# If set to dc cassandra will encrypt the traffic between the DCs -# If set to rack cassandra will encrypt the traffic between the racks -# -# The passwords used in these options must match the passwords used when generating -# the keystore and truststore. For instructions on generating these files, see: -# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore -# -server_encryption_options: - internode_encryption: none - keystore: conf/.keystore - keystore_password: cassandra - truststore: conf/.truststore - truststore_password: cassandra - # More advanced defaults below: - # protocol: TLS - # algorithm: SunX509 - # store_type: JKS - # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] - # require_client_auth: false - # require_endpoint_verification: false - -# enable or disable client/server encryption. -client_encryption_options: - enabled: false - # If enabled and optional is set to true encrypted and unencrypted connections are handled. - optional: false - keystore: conf/.keystore - keystore_password: cassandra - # require_client_auth: false - # Set truststore and truststore_password if require_client_auth is true - # truststore: conf/.truststore - # truststore_password: cassandra - # More advanced defaults below: - # protocol: TLS - # algorithm: SunX509 - # store_type: JKS - # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] - -# internode_compression controls whether traffic between nodes is -# compressed. -# Can be: -# -# all -# all traffic is compressed -# -# dc -# traffic between different datacenters is compressed -# -# none -# nothing is compressed. -internode_compression: dc - -# Enable or disable tcp_nodelay for inter-dc communication. -# Disabling it will result in larger (but fewer) network packets being sent, -# reducing overhead from the TCP protocol itself, at the cost of increasing -# latency if you block for cross-datacenter responses. -inter_dc_tcp_nodelay: false - -# TTL for different trace types used during logging of the repair process. -tracetype_query_ttl: 86400 -tracetype_repair_ttl: 604800 - -# By default, Cassandra logs GC Pauses greater than 200 ms at INFO level -# This threshold can be adjusted to minimize logging if necessary -# gc_log_threshold_in_ms: 200 - -# If unset, all GC Pauses greater than gc_log_threshold_in_ms will log at -# INFO level -# UDFs (user defined functions) are disabled by default. -# As of Cassandra 3.0 there is a sandbox in place that should prevent execution of evil code. -enable_user_defined_functions: false - -# Enables scripted UDFs (JavaScript UDFs). -# Java UDFs are always enabled, if enable_user_defined_functions is true. -# Enable this option to be able to use UDFs with "language javascript" or any custom JSR-223 provider. -# This option has no effect, if enable_user_defined_functions is false. -enable_scripted_user_defined_functions: false - -# The default Windows kernel timer and scheduling resolution is 15.6ms for power conservation. -# Lowering this value on Windows can provide much tighter latency and better throughput, however -# some virtualized environments may see a negative performance impact from changing this setting -# below their system default. The sysinternals 'clockres' tool can confirm your system's default -# setting. -windows_timer_interval: 1 - - -# Enables encrypting data at-rest (on disk). Different key providers can be plugged in, but the default reads from -# a JCE-style keystore. A single keystore can hold multiple keys, but the one referenced by -# the "key_alias" is the only key that will be used for encrypt operations; previously used keys -# can still (and should!) be in the keystore and will be used on decrypt operations -# (to handle the case of key rotation). -# -# It is strongly recommended to download and install Java Cryptography Extension (JCE) -# Unlimited Strength Jurisdiction Policy Files for your version of the JDK. -# (current link: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) -# -# Currently, only the following file types are supported for transparent data encryption, although -# more are coming in future cassandra releases: commitlog, hints -transparent_data_encryption_options: - enabled: false - chunk_length_kb: 64 - cipher: AES/CBC/PKCS5Padding - key_alias: testing:1 - # CBC IV length for AES needs to be 16 bytes (which is also the default size) - # iv_length: 16 - key_provider: - - class_name: org.apache.cassandra.security.JKSKeyProvider - parameters: - - keystore: conf/.keystore - keystore_password: cassandra - store_type: JCEKS - key_password: cassandra - - -##################### -# SAFETY THRESHOLDS # -##################### - -# When executing a scan, within or across a partition, we need to keep the -# tombstones seen in memory so we can return them to the coordinator, which -# will use them to make sure other replicas also know about the deleted rows. -# With workloads that generate a lot of tombstones, this can cause performance -# problems and even exhaust the server heap. -# (http://www.datastax.com/dev/blog/cassandra-anti-patterns-queues-and-queue-like-datasets) -# Adjust the thresholds here if you understand the dangers and want to -# scan more tombstones anyway. These thresholds may also be adjusted at runtime -# using the StorageService mbean. -tombstone_warn_threshold: 1000 -tombstone_failure_threshold: 100000 - -# Log WARN on any batch size exceeding this value. 5kb per batch by default. -# Caution should be taken on increasing the size of this threshold as it can lead to node instability. -batch_size_warn_threshold_in_kb: 5 - -# Fail any batch exceeding this value. 50kb (10x warn threshold) by default. -batch_size_fail_threshold_in_kb: 50 - -# Log WARN on any batches not of type LOGGED than span across more partitions than this limit -unlogged_batch_across_partitions_warn_threshold: 10 - -# Log a warning when compacting partitions larger than this value -compaction_large_partition_warning_threshold_mb: 100 - -# GC Pauses greater than gc_warn_threshold_in_ms will be logged at WARN level -# Adjust the threshold based on your application throughput requirement -# By default, Cassandra logs GC Pauses greater than 200 ms at INFO level -gc_warn_threshold_in_ms: 1000 - -# Maximum size of any value in SSTables. Safety measure to detect SSTable corruption -# early. Any value size larger than this threshold will result into marking an SSTable -# as corrupted. -# max_value_size_in_mb: 256 diff --git a/plugins/database/cassandra/test-fixtures/with_tls/bad_ca.pem b/plugins/database/cassandra/test-fixtures/with_tls/bad_ca.pem deleted file mode 100644 index 6674fa7fa..000000000 --- a/plugins/database/cassandra/test-fixtures/with_tls/bad_ca.pem +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEFjCCAv6gAwIBAgIUHNknw0iUWaMC5UCpiribG8DQhZYwDQYJKoZIhvcNAQEL -BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH -Ew1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKEwlIYXNoaUNvcnAxIzAhBgNVBAsTGlRl -c3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MS0wKwYDVQQDEyRQcm90b3R5cGUgVGVz -dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjEwNjE0MjAyNDAwWhcNMjYwNjEz -MjAyNDAwWjCBojELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAU -BgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUhhc2hpQ29ycDEjMCEGA1UE -CxMaVGVzdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxLTArBgNVBAMTJFByb3RvdHlw -ZSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBANc0MEZOJ7xm4JrCceerX0kWcdPIczXFIIZTJYdTB7YPHTiL -PFSZ9ugu8W6R7wOMLUazcD7Ugw0hjt+JkiRIY1AOvuZRX7DR3Q0sGy9qFb1y2kOk -lTSAFOV96FxxAg9Fn23mcvjV1TDO1dlxvOuAo0NMjk82TzHk7LVuYOKuJ/Sc9i8a -Ba4vndbiwkSGpytymCu0X4T4ZEARLUZ4feGhr5RbYRehq2Nb8kw/KNLZZyzlzJbr -8OkVizW796bkVJwRfCFubZPl8EvRslxZ2+sMFSozoofoFlB1FsGAvlnEfkxqTJJo -WafmsYnOVnbNfwOogDP0+bp8WAZrAxJqTAWm/LMCAwEAAaNCMEAwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHyfBUnvAULGlcFSljTI -DegUVLB5MA0GCSqGSIb3DQEBCwUAA4IBAQBOdVqZpMCKq+X2TBi3nJmz6kjePVBh -ocHUG02nRkL533x+PUxRpDG3AMzWF3niPxtMuVIZDfpi27zlm2QCh9b3sQi83w+9 -UX1/j3dUoUyiVi/U0iZeZmuDY3ne59DNFdOgGY9p3FvJ+b9WfPg8+v2w26rGoSMz -21XKNZcRFcjOJ5LJ3i9+liaCkpXLfErA+AtqNeraHOorJ5UO4mA7OlFowV8adOQq -SinFIoXCExBTxqMv0lVzEhGN6Wd261CmKY5e4QLqASCO+s7zwGhHyzwjdA0pCNtI -PmHIk13m0p56G8hpz+M/5hBQFb0MIIR3Je6QVzfRty2ipUO91E9Ydm7C ------END CERTIFICATE----- diff --git a/plugins/database/cassandra/test-fixtures/with_tls/ca.pem b/plugins/database/cassandra/test-fixtures/with_tls/ca.pem deleted file mode 100644 index fdcfb23fe..000000000 --- a/plugins/database/cassandra/test-fixtures/with_tls/ca.pem +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEFjCCAv6gAwIBAgIUWd8FZSev3ygjhWE7O8orqHPQ4IEwDQYJKoZIhvcNAQEL -BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH -Ew1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKEwlIYXNoaUNvcnAxIzAhBgNVBAsTGlRl -c3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MS0wKwYDVQQDEyRQcm90b3R5cGUgVGVz -dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjEwNjEwMjAwNDAwWhcNMjYwNjA5 -MjAwNDAwWjCBojELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAU -BgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUhhc2hpQ29ycDEjMCEGA1UE -CxMaVGVzdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxLTArBgNVBAMTJFByb3RvdHlw -ZSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAMXTnIDpOXXiHuKyI9EZxv7qg81DmelOB+iAzhvRsigMSuka -qZH29Aaf4PBvKLlSVN6sVP16cXRvk48qa0C78tP0kTPKWdEyE1xQUZb270SZ6Tm3 -T7sNRTRwWTsgeC1n6SHlBUn3MviQgA1dZM1CbZIXQpBxtuPg+p9eu3YP/CZJFJjT -LYVKT6kRumBQEX/UUesNfUnUpVIOxxOwbVeF6a/wGxeLY6/fOQ+TJhVUjSy/pvaI -6NnycrwD/4ck6gusV5HKakidCID9MwV610Vc7AFi070VGYCjKfiv6EYMMnjycYqi -KHz623Ca4rO4qtWWvT1K/+GkryDKXeI3KHuEsdsCAwEAAaNCMEAwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIy8cvyabFclVWwcZ4rl -ADoLEdyAMA0GCSqGSIb3DQEBCwUAA4IBAQCzn9QbsOpBuvhhgdH/Jk0q7H0kmpVS -rbLhcQyWv9xiyopYbbUfh0Hud15rnqAkyT9nd2Kvo8T/X9rc1OXa6oDO6aoXjIm1 -aKOFikET8fc/81rT81E7TVPO7TZW5s9Cej30zCOJQWZ+ibHNyequuyihtImNacXF -+1pAAldj/JMu+Ky1YFrs2iccGOpGCGbsWfLQt+wYKwya7dpSz1ceqigKavIJSOMV -CNsyC59UtFbvdk139FyEvCmecsCbWuo0JVg3do5n6upwqrgvLRNP8EHzm17DWu5T -aNtsBbv85uUgMmF7kzxr+t6VdtG9u+q0HCmW1/1VVK3ZsA+UTB7UBddD ------END CERTIFICATE----- diff --git a/plugins/database/cassandra/test-fixtures/with_tls/ca.pem.json b/plugins/database/cassandra/test-fixtures/with_tls/ca.pem.json deleted file mode 100644 index a28e2ed69..000000000 --- a/plugins/database/cassandra/test-fixtures/with_tls/ca.pem.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ca_chain": ["-----BEGIN CERTIFICATE-----\nMIIEFjCCAv6gAwIBAgIUWd8FZSev3ygjhWE7O8orqHPQ4IEwDQYJKoZIhvcNAQEL\nBQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKEwlIYXNoaUNvcnAxIzAhBgNVBAsTGlRl\nc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MS0wKwYDVQQDEyRQcm90b3R5cGUgVGVz\ndCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjEwNjEwMjAwNDAwWhcNMjYwNjA5\nMjAwNDAwWjCBojELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAU\nBgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUhhc2hpQ29ycDEjMCEGA1UE\nCxMaVGVzdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxLTArBgNVBAMTJFByb3RvdHlw\nZSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBAMXTnIDpOXXiHuKyI9EZxv7qg81DmelOB+iAzhvRsigMSuka\nqZH29Aaf4PBvKLlSVN6sVP16cXRvk48qa0C78tP0kTPKWdEyE1xQUZb270SZ6Tm3\nT7sNRTRwWTsgeC1n6SHlBUn3MviQgA1dZM1CbZIXQpBxtuPg+p9eu3YP/CZJFJjT\nLYVKT6kRumBQEX/UUesNfUnUpVIOxxOwbVeF6a/wGxeLY6/fOQ+TJhVUjSy/pvaI\n6NnycrwD/4ck6gusV5HKakidCID9MwV610Vc7AFi070VGYCjKfiv6EYMMnjycYqi\nKHz623Ca4rO4qtWWvT1K/+GkryDKXeI3KHuEsdsCAwEAAaNCMEAwDgYDVR0PAQH/\nBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIy8cvyabFclVWwcZ4rl\nADoLEdyAMA0GCSqGSIb3DQEBCwUAA4IBAQCzn9QbsOpBuvhhgdH/Jk0q7H0kmpVS\nrbLhcQyWv9xiyopYbbUfh0Hud15rnqAkyT9nd2Kvo8T/X9rc1OXa6oDO6aoXjIm1\naKOFikET8fc/81rT81E7TVPO7TZW5s9Cej30zCOJQWZ+ibHNyequuyihtImNacXF\n+1pAAldj/JMu+Ky1YFrs2iccGOpGCGbsWfLQt+wYKwya7dpSz1ceqigKavIJSOMV\nCNsyC59UtFbvdk139FyEvCmecsCbWuo0JVg3do5n6upwqrgvLRNP8EHzm17DWu5T\naNtsBbv85uUgMmF7kzxr+t6VdtG9u+q0HCmW1/1VVK3ZsA+UTB7UBddD\n-----END CERTIFICATE-----\n"] -} diff --git a/plugins/database/cassandra/test-fixtures/with_tls/cqlshrc b/plugins/database/cassandra/test-fixtures/with_tls/cqlshrc deleted file mode 100644 index 6a226e4b6..000000000 --- a/plugins/database/cassandra/test-fixtures/with_tls/cqlshrc +++ /dev/null @@ -1,3 +0,0 @@ -[ssl] -validate = false -version = SSLv23 diff --git a/plugins/database/cassandra/test-fixtures/with_tls/stores/keystore b/plugins/database/cassandra/test-fixtures/with_tls/stores/keystore deleted file mode 100644 index fce8a7771..000000000 Binary files a/plugins/database/cassandra/test-fixtures/with_tls/stores/keystore and /dev/null differ diff --git a/plugins/database/cassandra/test-fixtures/with_tls/stores/server.p12 b/plugins/database/cassandra/test-fixtures/with_tls/stores/server.p12 deleted file mode 100644 index c775b5432..000000000 Binary files a/plugins/database/cassandra/test-fixtures/with_tls/stores/server.p12 and /dev/null differ diff --git a/plugins/database/cassandra/test-fixtures/with_tls/stores/truststore b/plugins/database/cassandra/test-fixtures/with_tls/stores/truststore deleted file mode 100644 index e29c3fe1d..000000000 Binary files a/plugins/database/cassandra/test-fixtures/with_tls/stores/truststore and /dev/null differ diff --git a/plugins/database/cassandra/tls.go b/plugins/database/cassandra/tls.go deleted file mode 100644 index e8ad90723..000000000 --- a/plugins/database/cassandra/tls.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package cassandra - -import ( - "crypto/tls" - "crypto/x509" - "encoding/json" - "encoding/pem" - "fmt" - - "github.com/hashicorp/vault/sdk/helper/certutil" - "github.com/hashicorp/vault/sdk/helper/errutil" -) - -func jsonBundleToTLSConfig(rawJSON string, tlsMinVersion uint16, serverName string, insecureSkipVerify bool) (*tls.Config, error) { - var certBundle certutil.CertBundle - err := json.Unmarshal([]byte(rawJSON), &certBundle) - if err != nil { - return nil, fmt.Errorf("failed to parse JSON: %w", err) - } - - if certBundle.IssuingCA != "" && len(certBundle.CAChain) > 0 { - return nil, fmt.Errorf("issuing_ca and ca_chain cannot both be specified") - } - if certBundle.IssuingCA != "" { - certBundle.CAChain = []string{certBundle.IssuingCA} - certBundle.IssuingCA = "" - } - - return toClientTLSConfig(certBundle.Certificate, certBundle.PrivateKey, certBundle.CAChain, tlsMinVersion, serverName, insecureSkipVerify) -} - -func pemBundleToTLSConfig(pemBundle string, tlsMinVersion uint16, serverName string, insecureSkipVerify bool) (*tls.Config, error) { - if len(pemBundle) == 0 { - return nil, errutil.UserError{Err: "empty pem bundle"} - } - - pemBytes := []byte(pemBundle) - var pemBlock *pem.Block - - certificate := "" - privateKey := "" - caChain := []string{} - - for len(pemBytes) > 0 { - pemBlock, pemBytes = pem.Decode(pemBytes) - if pemBlock == nil { - return nil, errutil.UserError{Err: "no data found in PEM block"} - } - blockBytes := pem.EncodeToMemory(pemBlock) - - switch pemBlock.Type { - case "CERTIFICATE": - // Parse the cert so we know if it's a CA or not - cert, err := x509.ParseCertificate(pemBlock.Bytes) - if err != nil { - return nil, fmt.Errorf("failed to parse certificate: %w", err) - } - if cert.IsCA { - caChain = append(caChain, string(blockBytes)) - continue - } - - // Only one leaf certificate supported - if certificate != "" { - return nil, errutil.UserError{Err: "multiple leaf certificates not supported"} - } - certificate = string(blockBytes) - - case "RSA PRIVATE KEY", "EC PRIVATE KEY", "PRIVATE KEY": - if privateKey != "" { - return nil, errutil.UserError{Err: "multiple private keys not supported"} - } - privateKey = string(blockBytes) - default: - return nil, fmt.Errorf("unsupported PEM block type [%s]", pemBlock.Type) - } - } - - return toClientTLSConfig(certificate, privateKey, caChain, tlsMinVersion, serverName, insecureSkipVerify) -} - -func toClientTLSConfig(certificatePEM string, privateKeyPEM string, caChainPEMs []string, tlsMinVersion uint16, serverName string, insecureSkipVerify bool) (*tls.Config, error) { - if certificatePEM != "" && privateKeyPEM == "" { - return nil, fmt.Errorf("found certificate for client-side TLS authentication but no private key") - } else if certificatePEM == "" && privateKeyPEM != "" { - return nil, fmt.Errorf("found private key for client-side TLS authentication but no certificate") - } - - var certificates []tls.Certificate - if certificatePEM != "" { - certificate, err := tls.X509KeyPair([]byte(certificatePEM), []byte(privateKeyPEM)) - if err != nil { - return nil, fmt.Errorf("failed to parse certificate and private key pair: %w", err) - } - certificates = append(certificates, certificate) - } - - var rootCAs *x509.CertPool - if len(caChainPEMs) > 0 { - rootCAs = x509.NewCertPool() - for _, caBlock := range caChainPEMs { - ok := rootCAs.AppendCertsFromPEM([]byte(caBlock)) - if !ok { - return nil, fmt.Errorf("failed to add CA certificate to certificate pool: it may be malformed or empty") - } - } - } - - config := &tls.Config{ - Certificates: certificates, - RootCAs: rootCAs, - ServerName: serverName, - InsecureSkipVerify: insecureSkipVerify, - MinVersion: tlsMinVersion, - } - return config, nil -}