Skip to content

Commit

Permalink
fix(config-cache): replace map with explicit CQL and Alternator TLS c…
Browse files Browse the repository at this point in the history
…onfig

Fixes #3815
  • Loading branch information
karol-kokoszka committed May 9, 2024
1 parent fb0e100 commit d5e6eac
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 71 deletions.
45 changes: 45 additions & 0 deletions pkg/service/configcache/nodeconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2024 ScyllaDB

package configcache

import (
"github.com/pkg/errors"
"github.com/scylladb/scylla-manager/v3/pkg/scyllaclient"
"github.com/scylladb/scylla-manager/v3/pkg/service/cluster"
"github.com/scylladb/scylla-manager/v3/pkg/store"
)

// NodeConfig keeps the node current node configuration together with the TLS details per different type of connection.
type NodeConfig struct {
*scyllaclient.NodeInfo

cqlTLSConfig *TLSConfigWithAddress
alternatorTLSConfig *TLSConfigWithAddress
}

// NewNodeConfig creates and initializes new node configuration struct containing TLS configuration of CQL and Alternator.
func NewNodeConfig(c *cluster.Cluster, nodeInfo *scyllaclient.NodeInfo, secretsStore store.Store, host string) (config NodeConfig, err error) {
cqlTLS, err := newCQLTLSConfigIfEnabled(c, nodeInfo, secretsStore, host)
if err != nil {
return NodeConfig{}, errors.Wrap(err, "building node config")
}
alternatorTLS, err := newAlternatorTLSConfigIfEnabled(c, nodeInfo, secretsStore, host)
if err != nil {
return NodeConfig{}, errors.Wrap(err, "building node config")
}
return NodeConfig{
NodeInfo: nodeInfo,
cqlTLSConfig: cqlTLS,
alternatorTLSConfig: alternatorTLS,
}, nil
}

// CQLTLSConfig is a getter of TLS configuration for CQL session.
func (nc NodeConfig) CQLTLSConfig() *TLSConfigWithAddress {
return nc.cqlTLSConfig
}

// AlternatorTLSConfig is a getter of TLS configuration for Alternator session.
func (nc NodeConfig) AlternatorTLSConfig() *TLSConfigWithAddress {
return nc.alternatorTLSConfig
}
72 changes: 4 additions & 68 deletions pkg/service/configcache/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ package configcache

import (
"context"
"crypto/tls"
"sync"
"time"

"github.com/pkg/errors"
"github.com/scylladb/go-log"
"github.com/scylladb/scylla-manager/v3/pkg/scyllaclient"
"github.com/scylladb/scylla-manager/v3/pkg/secrets"
"github.com/scylladb/scylla-manager/v3/pkg/service"
"github.com/scylladb/scylla-manager/v3/pkg/service/cluster"
"github.com/scylladb/scylla-manager/v3/pkg/store"
"github.com/scylladb/scylla-manager/v3/pkg/util/uuid"
Expand All @@ -30,17 +27,6 @@ const (
updateFrequency = 5 * time.Minute
)

type tlsConfigWithAddress struct {
*tls.Config
Address string
}

// NodeConfig keeps the node current node configuration together with the TLS details per different type of connection.
type NodeConfig struct {
*scyllaclient.NodeInfo
TLSConfig map[ConnectionType]*tlsConfigWithAddress
}

// ConfigCacher is the interface defining the cache behavior.
type ConfigCacher interface {
// Read returns either the host configuration that is currently stored in the cache,
Expand Down Expand Up @@ -229,63 +215,13 @@ func (svc *Service) retrieveNodeConfig(ctx context.Context, host string, client

nodeInfoResp, err := client.NodeInfo(ctx, host)
if err != nil {
return config, errors.Wrap(err, "fetch node info")
return config, errors.Wrap(err, "retrieve cluster host configuration")
}

config.NodeInfo = nodeInfoResp
config.TLSConfig = make(map[ConnectionType]*tlsConfigWithAddress, 2)
for _, p := range []ConnectionType{CQL, Alternator} {
var tlsEnabled, clientCertAuth bool
var address string
if p == CQL {
address = nodeInfoResp.CQLAddr(host)
tlsEnabled, clientCertAuth = nodeInfoResp.CQLTLSEnabled()
tlsEnabled = tlsEnabled && !c.ForceTLSDisabled
if tlsEnabled && !c.ForceNonSSLSessionPort {
address = nodeInfoResp.CQLSSLAddr(host)
}
} else if p == Alternator {
tlsEnabled, clientCertAuth = nodeInfoResp.AlternatorTLSEnabled()
address = nodeInfoResp.AlternatorAddr(host)
}
if tlsEnabled {
tlsConfig, err := svc.tlsConfig(c.ID, clientCertAuth)
if err != nil && !errors.Is(err, service.ErrNotFound) {
return config, errors.Wrap(err, "fetch TLS config")
}
if clientCertAuth && errors.Is(err, service.ErrNotFound) {
return config, errors.Wrap(err, "client encryption is enabled, but certificate is missing")
}
config.TLSConfig[p] = &tlsConfigWithAddress{
Config: tlsConfig,
Address: address,
}
} else {
delete(config.TLSConfig, p)
}
config, err = NewNodeConfig(c, nodeInfoResp, svc.secretsStore, host)
if err != nil {
return config, errors.Wrap(err, "retrieve cluster host configuration")
}

return config, nil
}

func (svc *Service) tlsConfig(clusterID uuid.UUID, clientCertAuth bool) (*tls.Config, error) {
cfg := tls.Config{
InsecureSkipVerify: true,
}

if clientCertAuth {
id := &secrets.TLSIdentity{
ClusterID: clusterID,
}
if err := svc.secretsStore.Get(id); err != nil {
return nil, errors.Wrap(err, "get SSL user cert from secrets store")
}
keyPair, err := tls.X509KeyPair(id.Cert, id.PrivateKey)
if err != nil {
return nil, errors.Wrap(err, "invalid SSL user key pair")
}
cfg.Certificates = []tls.Certificate{keyPair}
}

return &cfg, nil
}
90 changes: 90 additions & 0 deletions pkg/service/configcache/tlsconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (C) 2024 ScyllaDB

package configcache

import (
"crypto/tls"

"github.com/pkg/errors"
"github.com/scylladb/scylla-manager/v3/pkg/scyllaclient"
"github.com/scylladb/scylla-manager/v3/pkg/secrets"
"github.com/scylladb/scylla-manager/v3/pkg/service"
"github.com/scylladb/scylla-manager/v3/pkg/service/cluster"
"github.com/scylladb/scylla-manager/v3/pkg/store"
)

// TLSConfigWithAddress is a concatenation of tls.Config and Address.
type TLSConfigWithAddress struct {
*tls.Config
Address string
}

func newCQLTLSConfigIfEnabled(c *cluster.Cluster, nodeInfo *scyllaclient.NodeInfo, secretsStore store.Store,
host string,
) (*TLSConfigWithAddress, error) {
cqlTLSEnabled, cqlClientCertAuth := nodeInfo.CQLTLSEnabled()
if !cqlTLSEnabled || c.ForceTLSDisabled {
return nil, nil // nolint: nilnil
}
cqlAddress := nodeInfo.CQLAddr(host)
if !c.ForceNonSSLSessionPort {
cqlAddress = nodeInfo.CQLSSLAddr(host)
}
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
if cqlClientCertAuth {
cert, err := prepareCertificates(c, secretsStore)
if err != nil {
return nil, errors.Wrap(err, "unable to create TLS configuration for CQL session")
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
return &TLSConfigWithAddress{
Address: cqlAddress,
Config: tlsConfig,
}, nil
}

func newAlternatorTLSConfigIfEnabled(c *cluster.Cluster, nodeInfo *scyllaclient.NodeInfo, secretsStore store.Store,
host string,
) (*TLSConfigWithAddress, error) {
alternatorTLSEnabled, alternatorClientCertAuth := nodeInfo.AlternatorTLSEnabled()
if !alternatorTLSEnabled {
return nil, nil // nolint: nilnil
}
alternatorAddress := nodeInfo.AlternatorAddr(host)

tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
if alternatorClientCertAuth {
cert, err := prepareCertificates(c, secretsStore)
if err != nil {
return nil, errors.Wrap(err, "unable to create TLS configuration for Alternator session")
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
return &TLSConfigWithAddress{
Address: alternatorAddress,
Config: tlsConfig,
}, nil
}

func prepareCertificates(c *cluster.Cluster, secretsStore store.Store) (cert tls.Certificate, err error) {
id := &secrets.TLSIdentity{
ClusterID: c.ID,
}
if err := secretsStore.Get(id); err != nil {
if !errors.Is(err, service.ErrNotFound) {
return tls.Certificate{}, errors.Wrap(err, "fetch TLS config")
}
return tls.Certificate{}, errors.Wrap(err, "client encryption is enabled, but certificate is missing")
}

keyPair, err := tls.X509KeyPair(id.Cert, id.PrivateKey)
if err != nil {
return tls.Certificate{}, errors.Wrap(err, "invalid SSL user key pair")
}
return keyPair, nil
}
6 changes: 3 additions & 3 deletions pkg/service/healthcheck/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func (s *Service) parallelCQLPingFunc(ctx context.Context, clusterID uuid.UUID,
s.logger.Error(ctx, "Unable to fetch node information", "error", err)
o.SSL = false
} else {
o.SSL = ni.TLSConfig[configcache.CQL] != nil
o.SSL = ni.CQLTLSConfig() != nil
}

return nil
Expand Down Expand Up @@ -294,7 +294,7 @@ func (s *Service) pingAlternator(ctx context.Context, clusterID uuid.UUID, host
Timeout: timeout,
}

tlsConfig := ni.TLSConfig[configcache.Alternator]
tlsConfig := ni.AlternatorTLSConfig()
if tlsConfig != nil {
config.TLSConfig = tlsConfig.Clone()
}
Expand All @@ -321,7 +321,7 @@ func (s *Service) pingCQL(ctx context.Context, clusterID uuid.UUID, host string,
Timeout: timeout,
}

tlsConfig := ni.TLSConfig[configcache.CQL]
tlsConfig := ni.CQLTLSConfig()
if tlsConfig != nil {
config.Addr = tlsConfig.Address
config.TLSConfig = tlsConfig.Clone()
Expand Down

0 comments on commit d5e6eac

Please sign in to comment.