Skip to content

Commit

Permalink
Exposes additional configuration knobs for TLS and updates docker-con…
Browse files Browse the repository at this point in the history
…fig so they can be set using env variables (#869)

- Adds support for specifying Server/Internode related TLS certificates using base64
- Updates docker-compose so TLS and Cassandra can be injected using environment variables

Tested via unit tests and a private deployment
  • Loading branch information
mastermanu committed Oct 16, 2020
1 parent 62c89e1 commit 7d065a2
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 54 deletions.
10 changes: 4 additions & 6 deletions common/auth/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,19 @@ type (
// client certificate
CertFile string `yaml:"certFile"`
KeyFile string `yaml:"keyFile"`
CaFile string `yaml:"caFile"` //optional depending on server config

CaFile string `yaml:"caFile"` //optional depending on server config
// If you want to verify the hostname and server cert (like a wildcard for cass cluster) then you should turn this on
// This option is basically the inverse of InSecureSkipVerify
// See InSecureSkipVerify in http://golang.org/pkg/crypto/tls/ for more info
EnableHostVerification bool `yaml:"enableHostVerification"`

ServerName string `yaml:"serverName"`

// optional inline base64 encoded versions of the above files
// Either BOTH CertData and KeyData must be supplied as base64 encoded values,
// or NEITHER of them should be supplied as base64 encoded values.
// (e.g. it is not supported to specify 'CertFile' and 'KeyData' or vice-versa)
// Base64 equivalents of the above artifacts.
// You cannot specify both a Data and a File for the same artifact (e.g. setting CertFile and CertData)
CertData string `yaml:"certData"`
KeyData string `yaml:"keyData"`
CaData string `yaml:"caData"`
CaData string `yaml:"caData"` // optional depending on server config
}
)
45 changes: 37 additions & 8 deletions common/cassandra/cassandraCluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"io/ioutil"

"errors"
"fmt"
Expand Down Expand Up @@ -60,26 +61,53 @@ func NewCassandraCluster(cfg config.Cassandra) (*gocql.ClusterConfig, error) {
cluster.HostFilter = gocql.DataCentreHostFilter(cfg.Datacenter)
}
if cfg.TLS != nil && cfg.TLS.Enabled {
if cfg.TLS.CertData != "" && cfg.TLS.CertFile != "" {
return nil, errors.New("Cannot specify both certData and certFile properties")
}

if cfg.TLS.KeyData != "" && cfg.TLS.KeyFile != "" {
return nil, errors.New("Cannot specify both keyData and keyFile properties")
}

if cfg.TLS.CaData != "" && cfg.TLS.CaFile != "" {
return nil, errors.New("Cannot specify both caData and caFile properties")
}

cluster.SslOpts = &gocql.SslOptions{
CertPath: cfg.TLS.CertFile,
KeyPath: cfg.TLS.KeyFile,
CaPath: cfg.TLS.CaFile,
EnableHostVerification: cfg.TLS.EnableHostVerification,

Config: auth.NewTLSConfigForServer(cfg.TLS.ServerName, cfg.TLS.EnableHostVerification),
Config: auth.NewTLSConfigForServer(cfg.TLS.ServerName, cfg.TLS.EnableHostVerification),
}

if cfg.TLS.CertData != "" {
certBytes, err := base64.StdEncoding.DecodeString(cfg.TLS.CertData)
var certBytes []byte
var keyBytes []byte
var err error

if cfg.TLS.CertFile != "" {
certBytes, err = ioutil.ReadFile(cfg.TLS.CertFile)
if err != nil {
return nil, fmt.Errorf("error reading client certificate file: %w", err)
}
} else if cfg.TLS.CertData != "" {
certBytes, err = base64.StdEncoding.DecodeString(cfg.TLS.CertData)
if err != nil {
return nil, fmt.Errorf("client certificate could not be decoded: %w", err)
}
}

keyBytes, err := base64.StdEncoding.DecodeString(cfg.TLS.KeyData)
if cfg.TLS.KeyFile != "" {
keyBytes, err = ioutil.ReadFile(cfg.TLS.KeyFile)
if err != nil {
return nil, fmt.Errorf("error reading client certificate private key file: %w", err)
}
} else if cfg.TLS.KeyData != "" {
keyBytes, err = base64.StdEncoding.DecodeString(cfg.TLS.KeyData)
if err != nil {
return nil, fmt.Errorf("client certificate private key could not be decoded: %w", err)
}
}

if len(certBytes) > 0 {
clientCert, err := tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return nil, fmt.Errorf("unable to generate x509 key pair: %w", err)
Expand All @@ -99,15 +127,16 @@ func NewCassandraCluster(cfg config.Cassandra) (*gocql.ClusterConfig, error) {
}
}
}

if cfg.MaxConns > 0 {
cluster.NumConns = cfg.MaxConns
}

if cfg.ConnectTimeout > 0 {
cluster.ConnectTimeout = cfg.ConnectTimeout
}

cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.RoundRobinHostPolicy())

return cluster, nil
}

Expand Down
32 changes: 31 additions & 1 deletion common/cassandra/cassandraCluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestNewCassandraCluster(t *testing.T) {
},
err: base64.CorruptInputError(4),
},
"clientCert_missingpassword": {
"clientCert_missingprivatekey": {
cfg: config.Cassandra{
TLS: &auth.TLS{
Enabled: true,
Expand All @@ -96,6 +96,36 @@ func TestNewCassandraCluster(t *testing.T) {
},
err: fmt.Errorf("unable to generate x509 key pair: %w", errors.New("tls: failed to find any PEM data in key input")),
},
"clientCert_duplicate_cert": {
cfg: config.Cassandra{
TLS: &auth.TLS{
Enabled: true,
CertData: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVEVENDQXZXZ0F3SUJBZ0lDQm5vd0RRWUpLb1pJaHZjTkFRRUxCUUF3Z2FVeEN6QUpCZ05WQkFZVEFsVlQKTVFzd0NRWURWUVFJRXdKRFFURVVNQklHQTFVRUJ4TUxVMkZ1ZEdFZ1EyeGhjbUV4RVRBUEJnTlZCQW9UQ0VSaApkR0ZUZEdGNE1RNHdEQVlEVlFRTEV3VkRiRzkxWkRGUU1FNEdBMVVFQXhOSFkyRXVNMk5oTm1ZeE9XRXRabUZsCk9DMDBNRGhoTFdKak5EUXRNR0psTkRRNU5EZGpPVFE1TFhWekxYZGxjM1F0TWk1a1lpNWhjM1J5WVM1a1lYUmgKYzNSaGVDNWpiMjB3SGhjTk1qQXdPVE13TVRjeU56RXpXaGNOTXpBd09UTXdNVGN5TnpFeldqQ0JxVEVMTUFrRwpBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ1RBa05CTVJRd0VnWURWUVFIRXd0VFlXNTBZU0JEYkdGeVlURVJNQThHCkExVUVDaE1JUkdGMFlWTjBZWGd4RGpBTUJnTlZCQXNUQlVOc2IzVmtNVlF3VWdZRFZRUURFMHRqYkdsbGJuUXUKTTJOaE5tWXhPV0V0Wm1GbE9DMDBNRGhoTFdKak5EUXRNR0psTkRRNU5EZGpPVFE1TFhWekxYZGxjM1F0TWk1awpZaTVoYzNSeVlTNWtZWFJoYzNSaGVDNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUROaFd1RFM3U1dwcTlUWVd4bkM0eUQzMjgvbmlNT29RcE5yZXlLTFN2ZVVBQU1xMm93clB3UlJZYTUKZ3JKUzU1MXkyQjJaYXRTTUNPYXVqL1l6QmVlbk81NHhuRW43ZCtkZVFleVZOWk1RVmo5SzZyNVRIaEhpZnR0Mwo0Nk90R0FIbE1jNFVxNitwV1dzbHgwM0psMjl6TjdDaFZqWmpLVWZ4K2lsUzhUNmlMOTFlRzhsZHovM1l0N3B3CmNTWEhyK0JHejFidXo1d2YrcmtCU0N4NS9oT3NZUXhBMTRZZzA4QktuNERkbURmelZWOFl1ZlR4c2UxWStzemcKbjNxRVB4aGVDQmN2dGZjb0RHbkJUcEJaS0kybmRRRlV2TVJDdDFmalJoTU9QN2dEcGhMQUZpNERyTkpWQTBubwpQc05aa05QWTZNSElPUXBkcTN3MEdablRVai92QWdNQkFBR2pRVEEvTUE0R0ExVWREd0VCL3dRRUF3SUhnREFkCkJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFnWUlLd1lCQlFVSEF3RXdEZ1lEVlIwT0JBY0VCUUVDQXdRR01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNxOWoyRXZYZFFzck9jTVlheUtWL0M0dGw2WTNFOEJCSU1rMHpSZ0tmWgpZVFV2V0RSeFVLY0hDeXFnT1lWWjhQbzlFaVpmL2lFTEEzbEtmd3VwNVZTbVRCODdvUVNTTEx3ZmJ1YnlYcU56ClJzSDdBSlNKZ0drMzJ4cWdpYmZzVU81MzA2SUVtTkRLK3ZkUTZjSk51bUtaeGhvRkZqTHY3QVI2RXBsVDRpKzcKSEprQk5XQnJFejNyaVhNL0VFSnN5V0p4dWJBL3pkcUk4WkI5ZFNJcmZ3NWp5N3lGNWw4ZjNNWjBjNnJzVDluZQpRTXVIeFRBNC95UnIrenlGZ3oyNDlwTHoybHlJT01OTmlxVkNubzVER1ZNSHQ0T08zbnVyT2lIelJUSnVsbDFKCkxvaU1xK2FLVFFITUU4T1ZKRUhvbHgrT242Q3JvSHRLa1Y4SER3WCtsb2syCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
CertFile: "/a/b/c",
},
},
err: errors.New("Cannot specify both certData and certFile properties"),
},
"clientCert_duplicate_key": {
cfg: config.Cassandra{
TLS: &auth.TLS{
Enabled: true,
KeyData: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVEVENDQXZXZ0F3SUJBZ0lDQm5vd0RRWUpLb1pJaHZjTkFRRUxCUUF3Z2FVeEN6QUpCZ05WQkFZVEFsVlQKTVFzd0NRWURWUVFJRXdKRFFURVVNQklHQTFVRUJ4TUxVMkZ1ZEdFZ1EyeGhjbUV4RVRBUEJnTlZCQW9UQ0VSaApkR0ZUZEdGNE1RNHdEQVlEVlFRTEV3VkRiRzkxWkRGUU1FNEdBMVVFQXhOSFkyRXVNMk5oTm1ZeE9XRXRabUZsCk9DMDBNRGhoTFdKak5EUXRNR0psTkRRNU5EZGpPVFE1TFhWekxYZGxjM1F0TWk1a1lpNWhjM1J5WVM1a1lYUmgKYzNSaGVDNWpiMjB3SGhjTk1qQXdPVE13TVRjeU56RXpXaGNOTXpBd09UTXdNVGN5TnpFeldqQ0JxVEVMTUFrRwpBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ1RBa05CTVJRd0VnWURWUVFIRXd0VFlXNTBZU0JEYkdGeVlURVJNQThHCkExVUVDaE1JUkdGMFlWTjBZWGd4RGpBTUJnTlZCQXNUQlVOc2IzVmtNVlF3VWdZRFZRUURFMHRqYkdsbGJuUXUKTTJOaE5tWXhPV0V0Wm1GbE9DMDBNRGhoTFdKak5EUXRNR0psTkRRNU5EZGpPVFE1TFhWekxYZGxjM1F0TWk1awpZaTVoYzNSeVlTNWtZWFJoYzNSaGVDNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUROaFd1RFM3U1dwcTlUWVd4bkM0eUQzMjgvbmlNT29RcE5yZXlLTFN2ZVVBQU1xMm93clB3UlJZYTUKZ3JKUzU1MXkyQjJaYXRTTUNPYXVqL1l6QmVlbk81NHhuRW43ZCtkZVFleVZOWk1RVmo5SzZyNVRIaEhpZnR0Mwo0Nk90R0FIbE1jNFVxNitwV1dzbHgwM0psMjl6TjdDaFZqWmpLVWZ4K2lsUzhUNmlMOTFlRzhsZHovM1l0N3B3CmNTWEhyK0JHejFidXo1d2YrcmtCU0N4NS9oT3NZUXhBMTRZZzA4QktuNERkbURmelZWOFl1ZlR4c2UxWStzemcKbjNxRVB4aGVDQmN2dGZjb0RHbkJUcEJaS0kybmRRRlV2TVJDdDFmalJoTU9QN2dEcGhMQUZpNERyTkpWQTBubwpQc05aa05QWTZNSElPUXBkcTN3MEdablRVai92QWdNQkFBR2pRVEEvTUE0R0ExVWREd0VCL3dRRUF3SUhnREFkCkJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFnWUlLd1lCQlFVSEF3RXdEZ1lEVlIwT0JBY0VCUUVDQXdRR01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNxOWoyRXZYZFFzck9jTVlheUtWL0M0dGw2WTNFOEJCSU1rMHpSZ0tmWgpZVFV2V0RSeFVLY0hDeXFnT1lWWjhQbzlFaVpmL2lFTEEzbEtmd3VwNVZTbVRCODdvUVNTTEx3ZmJ1YnlYcU56ClJzSDdBSlNKZ0drMzJ4cWdpYmZzVU81MzA2SUVtTkRLK3ZkUTZjSk51bUtaeGhvRkZqTHY3QVI2RXBsVDRpKzcKSEprQk5XQnJFejNyaVhNL0VFSnN5V0p4dWJBL3pkcUk4WkI5ZFNJcmZ3NWp5N3lGNWw4ZjNNWjBjNnJzVDluZQpRTXVIeFRBNC95UnIrenlGZ3oyNDlwTHoybHlJT01OTmlxVkNubzVER1ZNSHQ0T08zbnVyT2lIelJUSnVsbDFKCkxvaU1xK2FLVFFITUU4T1ZKRUhvbHgrT242Q3JvSHRLa1Y4SER3WCtsb2syCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
KeyFile: "/a/b/c",
},
},
err: errors.New("Cannot specify both keyData and keyFile properties"),
},
"clientCert_duplicate_ca": {
cfg: config.Cassandra{
TLS: &auth.TLS{
Enabled: true,
CaData: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVEVENDQXZXZ0F3SUJBZ0lDQm5vd0RRWUpLb1pJaHZjTkFRRUxCUUF3Z2FVeEN6QUpCZ05WQkFZVEFsVlQKTVFzd0NRWURWUVFJRXdKRFFURVVNQklHQTFVRUJ4TUxVMkZ1ZEdFZ1EyeGhjbUV4RVRBUEJnTlZCQW9UQ0VSaApkR0ZUZEdGNE1RNHdEQVlEVlFRTEV3VkRiRzkxWkRGUU1FNEdBMVVFQXhOSFkyRXVNMk5oTm1ZeE9XRXRabUZsCk9DMDBNRGhoTFdKak5EUXRNR0psTkRRNU5EZGpPVFE1TFhWekxYZGxjM1F0TWk1a1lpNWhjM1J5WVM1a1lYUmgKYzNSaGVDNWpiMjB3SGhjTk1qQXdPVE13TVRjeU56RXpXaGNOTXpBd09UTXdNVGN5TnpFeldqQ0JxVEVMTUFrRwpBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ1RBa05CTVJRd0VnWURWUVFIRXd0VFlXNTBZU0JEYkdGeVlURVJNQThHCkExVUVDaE1JUkdGMFlWTjBZWGd4RGpBTUJnTlZCQXNUQlVOc2IzVmtNVlF3VWdZRFZRUURFMHRqYkdsbGJuUXUKTTJOaE5tWXhPV0V0Wm1GbE9DMDBNRGhoTFdKak5EUXRNR0psTkRRNU5EZGpPVFE1TFhWekxYZGxjM1F0TWk1awpZaTVoYzNSeVlTNWtZWFJoYzNSaGVDNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUROaFd1RFM3U1dwcTlUWVd4bkM0eUQzMjgvbmlNT29RcE5yZXlLTFN2ZVVBQU1xMm93clB3UlJZYTUKZ3JKUzU1MXkyQjJaYXRTTUNPYXVqL1l6QmVlbk81NHhuRW43ZCtkZVFleVZOWk1RVmo5SzZyNVRIaEhpZnR0Mwo0Nk90R0FIbE1jNFVxNitwV1dzbHgwM0psMjl6TjdDaFZqWmpLVWZ4K2lsUzhUNmlMOTFlRzhsZHovM1l0N3B3CmNTWEhyK0JHejFidXo1d2YrcmtCU0N4NS9oT3NZUXhBMTRZZzA4QktuNERkbURmelZWOFl1ZlR4c2UxWStzemcKbjNxRVB4aGVDQmN2dGZjb0RHbkJUcEJaS0kybmRRRlV2TVJDdDFmalJoTU9QN2dEcGhMQUZpNERyTkpWQTBubwpQc05aa05QWTZNSElPUXBkcTN3MEdablRVai92QWdNQkFBR2pRVEEvTUE0R0ExVWREd0VCL3dRRUF3SUhnREFkCkJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFnWUlLd1lCQlFVSEF3RXdEZ1lEVlIwT0JBY0VCUUVDQXdRR01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNxOWoyRXZYZFFzck9jTVlheUtWL0M0dGw2WTNFOEJCSU1rMHpSZ0tmWgpZVFV2V0RSeFVLY0hDeXFnT1lWWjhQbzlFaVpmL2lFTEEzbEtmd3VwNVZTbVRCODdvUVNTTEx3ZmJ1YnlYcU56ClJzSDdBSlNKZ0drMzJ4cWdpYmZzVU81MzA2SUVtTkRLK3ZkUTZjSk51bUtaeGhvRkZqTHY3QVI2RXBsVDRpKzcKSEprQk5XQnJFejNyaVhNL0VFSnN5V0p4dWJBL3pkcUk4WkI5ZFNJcmZ3NWp5N3lGNWw4ZjNNWjBjNnJzVDluZQpRTXVIeFRBNC95UnIrenlGZ3oyNDlwTHoybHlJT01OTmlxVkNubzVER1ZNSHQ0T08zbnVyT2lIelJUSnVsbDFKCkxvaU1xK2FLVFFITUU4T1ZKRUhvbHgrT242Q3JvSHRLa1Y4SER3WCtsb2syCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
CaFile: "/a/b/c",
},
},
err: errors.New("Cannot specify both caData and caFile properties"),
},
}

for name, tc := range tests {
Expand Down
161 changes: 132 additions & 29 deletions common/rpc/encryption/localStoreCertProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ package encryption
import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
Expand All @@ -52,23 +53,57 @@ func (s *localStoreCertProvider) GetSettings() *config.GroupTLS {
}

func (s *localStoreCertProvider) FetchServerCertificate() (*tls.Certificate, error) {
if s.tlsSettings.Server.CertFile == "" {
if s.tlsSettings.Server.CertFile == "" && s.tlsSettings.Server.CertData == "" {
return nil, nil
}

// Check under a read lock first
if s.tlsSettings.Server.CertFile != "" && s.tlsSettings.Server.CertData != "" {
return nil, errors.New("Cannot specify both certFile and certData properties")
}

s.RLock()
if s.serverCert != nil {
defer s.RUnlock()
return s.serverCert, nil
}
// Not found, manually unlock read lock and move to write lock

s.RUnlock()
s.Lock()
defer s.Unlock()

// Get serverCert from disk
serverCert, err := tls.LoadX509KeyPair(s.tlsSettings.Server.CertFile, s.tlsSettings.Server.KeyFile)
if s.serverCert != nil {
return s.serverCert, nil
}

var certBytes []byte
var keyBytes []byte
var err error

if s.tlsSettings.Server.CertFile != "" {
certBytes, err = ioutil.ReadFile(s.tlsSettings.Server.CertFile)
if err != nil {
return nil, err
}
} else if s.tlsSettings.Server.CertData != "" {
certBytes, err = base64.StdEncoding.DecodeString(s.tlsSettings.Server.CertData)
if err != nil {
return nil, fmt.Errorf("TLS public certificate could not be decoded: %w", err)
}
}

if s.tlsSettings.Server.KeyFile != "" {
keyBytes, err = ioutil.ReadFile(s.tlsSettings.Server.KeyFile)
if err != nil {
return nil, err
}
} else if s.tlsSettings.Server.KeyData != "" {
keyBytes, err = base64.StdEncoding.DecodeString(s.tlsSettings.Server.KeyData)
if err != nil {
return nil, fmt.Errorf("TLS private key could not be decoded: %w", err)
}
}

serverCert, err := tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return nil, fmt.Errorf("loading server tls certificate failed: %v", err)
}
Expand All @@ -78,73 +113,141 @@ func (s *localStoreCertProvider) FetchServerCertificate() (*tls.Certificate, err
}

func (s *localStoreCertProvider) FetchClientCAs() (*x509.CertPool, error) {
if s.tlsSettings.Server.ClientCAFiles == nil {
if len(s.tlsSettings.Server.ClientCAFiles) == 0 && len(s.tlsSettings.Server.ClientCAData) == 0 {
return nil, nil
}

// Check under a read lock first
s.RLock()
if s.clientCAs != nil {
defer s.RUnlock()
return s.clientCAs, nil
}
// Not found, manually unlock read lock and move to write lock

s.RUnlock()
s.Lock()
defer s.Unlock()

var clientCaPool *x509.CertPool
if len(s.tlsSettings.Server.ClientCAFiles) > 0 {
var err error
clientCaPool, err = buildCAPool(s.tlsSettings.Server.ClientCAFiles)
if err != nil {
return nil, err
}
if s.clientCAs != nil {
return s.clientCAs, nil
}

clientCaPoolFromFiles, err := buildCAPoolFromFiles(s.tlsSettings.Server.ClientCAFiles)
if err != nil {
return nil, err
}

clientCaPoolFromData, err := buildCAPoolFromData(s.tlsSettings.Server.ClientCAData)
if err != nil {
return nil, err
}

if clientCaPoolFromFiles != nil && clientCaPoolFromData != nil {
return nil, errors.New("cannot specify both clientCAFiles and clientCAData properties")
}

if clientCaPoolFromData != nil {
s.clientCAs = clientCaPoolFromData
} else {
s.clientCAs = clientCaPoolFromFiles
}

s.clientCAs = clientCaPool
return s.clientCAs, nil
}

func (s *localStoreCertProvider) FetchServerRootCAsForClient() (*x509.CertPool, error) {
if s.tlsSettings.Client.RootCAFiles == nil {
if len(s.tlsSettings.Client.RootCAFiles) == 0 && len(s.tlsSettings.Client.RootCAData) == 0 {
return nil, nil
}
// Check under a read lock first

s.RLock()
if s.serverCAs != nil {
defer s.RUnlock()
return s.clientCAs, nil
return s.serverCAs, nil
}
// Not found, manually unlock read lock and move to write lock

s.RUnlock()
s.Lock()
defer s.Unlock()

var serverCAPool *x509.CertPool
if len(s.tlsSettings.Client.RootCAFiles) > 0 {
var err error
serverCAPool, err = buildCAPool(s.tlsSettings.Client.RootCAFiles)
if s.serverCAs != nil {
return s.serverCAs, nil
}

serverCAPoolFromFiles, err := buildCAPoolFromFiles(s.tlsSettings.Client.RootCAFiles)
if err != nil {
return nil, err
}

serverCAPoolFromData, err := buildCAPoolFromData(s.tlsSettings.Client.RootCAData)
if err != nil {
return nil, err
}

if serverCAPoolFromData != nil && serverCAPoolFromFiles != nil {
return nil, errors.New("cannot specify both rootCAFiles and rootCAData properties")
}

if serverCAPoolFromData != nil {
s.serverCAs = serverCAPoolFromData
} else {
s.serverCAs = serverCAPoolFromFiles
}

return s.serverCAs, nil
}

func buildCAPoolFromData(caData []string) (*x509.CertPool, error) {
atLeastOneCert := false
caPool := x509.NewCertPool()

for _, ca := range caData {
if ca == "" {
continue
}

caBytes, err := base64.StdEncoding.DecodeString(ca)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to decode ca cert: %v", err)
}

if !caPool.AppendCertsFromPEM(caBytes) {
return nil, errors.New("unknown failure constructing cert pool for ca")
}

atLeastOneCert = true
}

s.serverCAs = serverCAPool
return s.serverCAs, nil
if !atLeastOneCert {
return nil, nil
}

return caPool, nil
}

func buildCAPool(caFiles []string) (*x509.CertPool, error) {
func buildCAPoolFromFiles(caFiles []string) (*x509.CertPool, error) {
atLeastOneCert := false
caPool := x509.NewCertPool()

for _, ca := range caFiles {
if ca == "" {
continue
}

caBytes, err := ioutil.ReadFile(ca)
if err != nil {
return nil, fmt.Errorf("failed reading client ca cert: %v", err)
return nil, fmt.Errorf("failed to read ca cert from file '%v': %v", ca, err)
}

if !caPool.AppendCertsFromPEM(caBytes) {
return nil, errors.New("unknown failure constructing cert pool for ca")
}

atLeastOneCert = true
}

if !atLeastOneCert {
return nil, nil
}

return caPool, nil
}

0 comments on commit 7d065a2

Please sign in to comment.