Skip to content

Commit 0b31415

Browse files
authored
Support for specifying SSL certificates for Cassandra client (#3384)
Updated tests Regenerated documentation Added CHANGELOG entry Signed-off-by: Christopher Bradford <christopher.bradford@datastax.com> Regenerate documentation and update certificate key file to private key file Signed-off-by: Christopher Bradford <christopher.bradford@datastax.com>
1 parent 4b40f20 commit 0b31415

File tree

7 files changed

+155
-3
lines changed

7 files changed

+155
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
* [FEATURE] Query-frontend: added `compression` config to support results cache with compression. #3217
5555
* [FEATURE] Add OpenStack Swift support to blocks storage. #3303
5656
* [FEATURE] Added support for applying Prometheus relabel configs on series received by the distributor. A `metric_relabel_configs` field has been added to the per-tenant limits configuration. #3329
57+
* [FEATURE] Support for Cassandra client SSL certificates. #3384
5758
* [ENHANCEMENT] Ruler: Introduces two new limits `-ruler.max-rules-per-rule-group` and `-ruler.max-rule-groups-per-tenant` to control the number of rules per rule group and the total number of rule groups for a given user. They are disabled by default. #3366
5859
* [ENHANCEMENT] Allow to specify multiple comma-separated Cortex services to `-target` CLI option (or its respective YAML config option). For example, `-target=all,compactor` can be used to start Cortex single-binary with compactor as well. #3275
5960
* [ENHANCEMENT] Expose additional HTTP configs for the S3 backend client. New flag are listed below: #3244

docs/configuration/config-file-reference.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1984,6 +1984,14 @@ cassandra:
19841984
# CLI flag: -cassandra.ca-path
19851985
[CA_path: <string> | default = ""]
19861986

1987+
# Path to certificate file used by TLS.
1988+
# CLI flag: -cassandra.tls-cert-path
1989+
[tls_cert_path: <string> | default = ""]
1990+
1991+
# Path to private key file used by TLS.
1992+
# CLI flag: -cassandra.tls-key-path
1993+
[tls_key_path: <string> | default = ""]
1994+
19871995
# Enable password authentication when connecting to cassandra.
19881996
# CLI flag: -cassandra.auth
19891997
[auth: <boolean> | default = false]

pkg/chunk/cassandra/storage_client.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ type Config struct {
3434
SSL bool `yaml:"SSL"`
3535
HostVerification bool `yaml:"host_verification"`
3636
CAPath string `yaml:"CA_path"`
37+
CertPath string `yaml:"tls_cert_path"`
38+
KeyPath string `yaml:"tls_key_path"`
3739
Auth bool `yaml:"auth"`
3840
Username string `yaml:"username"`
3941
Password flagext.Secret `yaml:"password"`
@@ -62,6 +64,8 @@ func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
6264
f.BoolVar(&cfg.SSL, "cassandra.ssl", false, "Use SSL when connecting to cassandra instances.")
6365
f.BoolVar(&cfg.HostVerification, "cassandra.host-verification", true, "Require SSL certificate validation.")
6466
f.StringVar(&cfg.CAPath, "cassandra.ca-path", "", "Path to certificate file to verify the peer.")
67+
f.StringVar(&cfg.CertPath, "cassandra.tls-cert-path", "", "Path to certificate file used by TLS.")
68+
f.StringVar(&cfg.KeyPath, "cassandra.tls-key-path", "", "Path to private key file used by TLS.")
6569
f.BoolVar(&cfg.Auth, "cassandra.auth", false, "Enable password authentication when connecting to cassandra.")
6670
f.StringVar(&cfg.Username, "cassandra.username", "", "Username to use when connecting to cassandra.")
6771
f.Var(&cfg.Password, "cassandra.password", "Password to use when connecting to cassandra.")
@@ -86,6 +90,12 @@ func (cfg *Config) Validate() error {
8690
if cfg.SSL && cfg.HostVerification && len(strings.Split(cfg.Addresses, ",")) != 1 {
8791
return errors.Errorf("Host verification is only possible for a single host.")
8892
}
93+
if cfg.SSL && cfg.CertPath != "" && cfg.KeyPath == "" {
94+
return errors.Errorf("TLS certificate specified, but private key configuration is missing.")
95+
}
96+
if cfg.SSL && cfg.KeyPath != "" && cfg.CertPath == "" {
97+
return errors.Errorf("TLS private key specified, but certificate configuration is missing.")
98+
}
8999
return nil
90100
}
91101

@@ -144,17 +154,29 @@ func (cfg *Config) setClusterConfig(cluster *gocql.ClusterConfig) error {
144154
cluster.DisableInitialHostLookup = cfg.DisableInitialHostLookup
145155

146156
if cfg.SSL {
157+
tlsConfig := &tls.Config{}
158+
159+
if cfg.CertPath != "" {
160+
cert, err := tls.LoadX509KeyPair(cfg.CertPath, cfg.KeyPath)
161+
if err != nil {
162+
return errors.Wrap(err, "Unable to load TLS certificate and private key")
163+
}
164+
165+
tlsConfig.Certificates = []tls.Certificate{cert}
166+
}
167+
147168
if cfg.HostVerification {
169+
tlsConfig.ServerName = strings.Split(cfg.Addresses, ",")[0]
170+
148171
cluster.SslOpts = &gocql.SslOptions{
149172
CaPath: cfg.CAPath,
150173
EnableHostVerification: true,
151-
Config: &tls.Config{
152-
ServerName: strings.Split(cfg.Addresses, ",")[0],
153-
},
174+
Config: tlsConfig,
154175
}
155176
} else {
156177
cluster.SslOpts = &gocql.SslOptions{
157178
EnableHostVerification: false,
179+
Config: tlsConfig,
158180
}
159181
}
160182
}

pkg/chunk/cassandra/storage_client_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,48 @@ func TestConfig_setClusterConfig_authWithPasswordAndPasswordFile(t *testing.T) {
7575
assert.Error(t, cfg.Validate())
7676
}
7777

78+
func TestConfig_setClusterConfig_clientSSL(t *testing.T) {
79+
cfg := defaultConfig()
80+
cfg.SSL = true
81+
cfg.CAPath = "testdata/example.com.ca.pem"
82+
cfg.CertPath = "testdata/example.com.pem"
83+
cfg.KeyPath = "testdata/example.com-key.pem"
84+
require.NoError(t, cfg.Validate())
85+
86+
cqlCfg := gocql.NewCluster()
87+
err := cfg.setClusterConfig(cqlCfg)
88+
require.NoError(t, err)
89+
assert.NotNil(t, cqlCfg.SslOpts)
90+
assert.Len(t, cqlCfg.SslOpts.Certificates, 1)
91+
}
92+
93+
func TestConfig_setClusterConfig_clientSSLWithOnlyCertificatePath(t *testing.T) {
94+
cfg := defaultConfig()
95+
cfg.SSL = true
96+
cfg.CAPath = "testdata/example.com.ca.pem"
97+
cfg.CertPath = "testdata/example.com.pem"
98+
assert.Error(t, cfg.Validate(), "TLS certificate specified, but private key configuration is missing.")
99+
}
100+
101+
func TestConfig_setClusterConfig_clientSSLWithOnlyKeyPath(t *testing.T) {
102+
cfg := defaultConfig()
103+
cfg.SSL = true
104+
cfg.CAPath = "testdata/example.com.ca.pem"
105+
cfg.KeyPath = "testdata/example.com-key.pem"
106+
assert.Error(t, cfg.Validate(), "TLS private key specified, but certificate configuration is missing.")
107+
}
108+
109+
func TestConfig_setClusterConfig_clientSSLWithInvalidParameters(t *testing.T) {
110+
cfg := defaultConfig()
111+
cfg.SSL = true
112+
cfg.CAPath = "testdata/example.com.ca.pem"
113+
cfg.CertPath = "testdata/example.com-key.pem"
114+
cfg.KeyPath = "testdata/example.com.pem"
115+
116+
cluster := gocql.NewCluster()
117+
assert.Error(t, cfg.setClusterConfig(cluster), "Unable to load TLS certificate and private key.")
118+
}
119+
78120
func TestConfig_setClusterConfig_consistency(t *testing.T) {
79121
tests := map[string]struct {
80122
cfg Config
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEogIBAAKCAQEAp7T84Nzd5y54a0qEFNeCXIDoz4LdfQqB2MloSl6aicKFRX7Y
3+
rTPDgwul9trau0dtDi7goT7GoyesRRJBsAqmUEaX9YjgRbWCiiKr47SxQJRn0Hom
4+
sNgCfIgCJ+Ej8no87JaxM7gklqoXrWK2KeG+NoC7jO2hrlBomkTkjgjU466kYpXM
5+
8z2+p3Jt3JHNJYTnOl36s/Iaw0ugmEPOJpLrsdnt2Irs5J2dmbMbeDJ5+yaE2c8B
6+
YSzAnLh5GQZO+5G7Ez0SXs1VJ2DALXybLuLD9pEZpAt40TK9jNwU5dfTYZ6yEeyn
7+
jw6qqJMcxVEYRru7x4ArOP7Uvs0qlOLDxJdD+QIDAQABAoIBAFWbj9KBLE0Mba/n
8+
E9FHyWXK8Aytcr6XlHzDIxeDf0N/JmS5QYX4fH7yfT+rrCgZZ9PrngLxdphmcgu4
9+
LAvfA9LKlltiCYnMA9zbof7Uh/69Qtkq4YE4YtyK2P7ecGkgeOUUb1RFVXgLT5bU
10+
YsSyVVShFhv0WaoPpbXKIRlX7MRj188laiYQnhLJ8gZeutk9A3wkaPsRp2s1ME9R
11+
oyUDs9OAEJOXB7wEqGLmKXqxSqzTDSoC/7wbjQut8G63/ri+EYwp5ukRP886cbep
12+
hVUDt8bzoratw+IFsGt+fBe9MGNbAMvva7x0yb/7gdEf505+c1h3Q19CLbPHFIJv
13+
hEcbhAECgYEAwkZp26SQANi7zDVoGqS7PXIWs2TL2g9Y2vO5g0MTvbno/0OPo3+e
14+
GOtFW4rK2k6SoK3RtdOdmobJcxKf2G4W7ncdM6X1Rgxfgqyt58qfXSL/TlS3wfH2
15+
vDFQDyLEofZ64vSMEKinSkv5kY/FCCtw8q8hCgiqmpwY51PFlI/52bkCgYEA3P2h
16+
LmRYGImkw8eiKm4eAFYXdVlxzsytCF3s2cCI3GavoGyMwxayYUkEKRA7bcup1pK+
17+
RdIRaaVkNcFICrX4N9zaEqSkBEQkQ+ZQN0hW9HM3XKPR7VPQEusRCbV5hMNgI3tY
18+
irAZXgeOzS61Rl+o3Ta3SQ2jDcCoK31xzE1s3EECgYAYYb/tWfTctlazZUyAc4Yw
19+
Sv5AW3keD+kF6aqxp5x1pjxwtOj1CxIrbHOS7pNQ3KWYVthH6pwQBbSIpaC8B+0G
20+
1poqnjxvIyRlgQh+W7aueLL0ALvjMlvV+JZkn+dvsEBx9WESwifksi5LL3D5+oG9
21+
Y29RFA9dQhP6DFByubMQuQKBgAyIogykik6R9/NWrj7j0fXI7DmuogLNnv67fQR4
22+
pAqEFG/v2Cf0cJeN8Zt2nThD9dUCq6IAIRax17YoyTI6UeKxNvkZt2e6iagENwZ7
23+
ptrkcf5iGDTyrPl1tZisX0EFZ717cHElPbsUiKfgf02HfWdWhByzlkzgYWleCwdA
24+
WO1BAoGAMJA/dYgbEachKXl9nZ9HvqAtc30ioYG0eq5Zx9iYZWqhb1vqGJXS2M2t
25+
Onmw/+2pnvPIM1uP66G5VG0rcVL7jdJ0pibCgw+L9IHpDwCAuVGn1Cuzcty+NiAy
26+
qLkQAgPOMjwL9zJYrn6LAOVchnIbpuoaGNG14BdZWeh25kozk/k=
27+
-----END RSA PRIVATE KEY-----
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEMjCCAxqgAwIBAgIUDpfGq/7zYQRROCP65MxUy+LYewkwDQYJKoZIhvcNAQEL
3+
BQAwgbAxGDAWBgNVBAYTD0V4YW1wbGUgQ291bnRyeTEWMBQGA1UECBMNRXhhbXBs
4+
ZSBTdGF0ZTEZMBcGA1UEBxMQRXhhbXBsZSBMb2NhbGl0eTEdMBsGA1UEChMURXhh
5+
bXBsZSBPcmdhbml6YXRpb24xIjAgBgNVBAsTGUV4YW1wbGUgT3JnYW5pemF0aW9u
6+
IFVuaXQxHjAcBgNVBAMTFVNhbXBsZSBTZWxmLVNpZ25lZCBDQTAeFw0yMDEwMjMx
7+
NTUxMDBaFw0yNTEwMjIxNTUxMDBaMIGwMRgwFgYDVQQGEw9FeGFtcGxlIENvdW50
8+
cnkxFjAUBgNVBAgTDUV4YW1wbGUgU3RhdGUxGTAXBgNVBAcTEEV4YW1wbGUgTG9j
9+
YWxpdHkxHTAbBgNVBAoTFEV4YW1wbGUgT3JnYW5pemF0aW9uMSIwIAYDVQQLExlF
10+
eGFtcGxlIE9yZ2FuaXphdGlvbiBVbml0MR4wHAYDVQQDExVTYW1wbGUgU2VsZi1T
11+
aWduZWQgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXiANIcYCd
12+
AaniIG59ZEIhgT2tx4PpsjPc07sx4B8cPAhYVPlpom2lSfI9lj14i37vEBFEsuwD
13+
Dk7xz6MGIAsLbmw8eEAbUyMaKeCfUZDVwWWW/OQo/riCc1Vw85nKAUQ7YA4TyvpO
14+
ORf5UAhLx0/ZhJsoAOgEOGa+S7NZjNCwADvhOzE0j5oDCblv3+EeiB8zAYzAG1xm
15+
onkVsS09mfmiE2V5mA6zAn60E9Ssfx4hJbxFNAvTlv+im37uumipKEGr/gRCcnFp
16+
QcgamxjCbmD+XNjJ35u0/r/mXHeghvRNl+2ARl3XngKclFeNwhgm7DWJsx66bbGy
17+
Hv2YQb9xxTNbAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
18+
AQH/MB0GA1UdDgQWBBSuamXDGCosE/xvsAWBVbSshGQ65DANBgkqhkiG9w0BAQsF
19+
AAOCAQEAd21HOCRsQENKxOsMbsYyla1lyWPjTTnn+c4IbgfcKA7lkf8ESFa7ChVq
20+
Q2z8mTAxblvnGy1bHaeFw1vy0hIYnV1rizsb+3nBN5oBZQtG6Rmc9iL5MhIaHprB
21+
bHqx/9zuCwH2jzSMcIYGUbfJcC5+W67P/zpX5rKkCqiyu81Unw5GAmcawaU6600b
22+
Dtx1YEWWLjwnBXXQp+4udHHCChsq5SFwJuWZ13+KNIrrD0QqcXn4lGtEWLD7/Anl
23+
BFGcQJpHq5x17Of1pxOqEogXTk44+cMKNDRr05mH2xc6nZqvZlUkdusl34XLC9Lo
24+
kIim0bc2p5dHjfAeBS7HSkXuwYzeQA==
25+
-----END CERTIFICATE-----
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEfzCCA2egAwIBAgIUJr0Q3zi9e99mQtYS9nK2jniXnZkwDQYJKoZIhvcNAQEL
3+
BQAwgbAxGDAWBgNVBAYTD0V4YW1wbGUgQ291bnRyeTEWMBQGA1UECBMNRXhhbXBs
4+
ZSBTdGF0ZTEZMBcGA1UEBxMQRXhhbXBsZSBMb2NhbGl0eTEdMBsGA1UEChMURXhh
5+
bXBsZSBPcmdhbml6YXRpb24xIjAgBgNVBAsTGUV4YW1wbGUgT3JnYW5pemF0aW9u
6+
IFVuaXQxHjAcBgNVBAMTFVNhbXBsZSBTZWxmLVNpZ25lZCBDQTAeFw0yMDEwMjMx
7+
NTUyMDBaFw0yMTEwMjMxNTUyMDBaMIGmMRgwFgYDVQQGEw9FeGFtcGxlIENvdW50
8+
cnkxFjAUBgNVBAgTDUV4YW1wbGUgU3RhdGUxGTAXBgNVBAcTEEV4YW1wbGUgTG9j
9+
YWxpdHkxHTAbBgNVBAoTFEV4YW1wbGUgT3JnYW5pemF0aW9uMSIwIAYDVQQLExlF
10+
eGFtcGxlIE9yZ2FuaXphdGlvbiBVbml0MRQwEgYDVQQDEwtleGFtcGxlLmNvbTCC
11+
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKe0/ODc3ecueGtKhBTXglyA
12+
6M+C3X0KgdjJaEpemonChUV+2K0zw4MLpfba2rtHbQ4u4KE+xqMnrEUSQbAKplBG
13+
l/WI4EW1gooiq+O0sUCUZ9B6JrDYAnyIAifhI/J6POyWsTO4JJaqF61itinhvjaA
14+
u4ztoa5QaJpE5I4I1OOupGKVzPM9vqdybdyRzSWE5zpd+rPyGsNLoJhDziaS67HZ
15+
7diK7OSdnZmzG3gyefsmhNnPAWEswJy4eRkGTvuRuxM9El7NVSdgwC18my7iw/aR
16+
GaQLeNEyvYzcFOXX02GeshHsp48OqqiTHMVRGEa7u8eAKzj+1L7NKpTiw8SXQ/kC
17+
AwEAAaOBmDCBlTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG
18+
CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFNfuWV6Y129JoLnf12l+
19+
/OHgSyXEMB8GA1UdIwQYMBaAFK5qZcMYKiwT/G+wBYFVtKyEZDrkMBYGA1UdEQQP
20+
MA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBoIy1KhsjfHMwO1yAZ
21+
ECiyoPr7cfqPFSteAEk7CxqgdCW2ZsZJnKHdZYFhBfc7cEGTHss9gANEgOogzgEV
22+
ZOny0fINK3+GVuDlvm9DEB/r5sXm9zWoS5qvbtZ58lUlgOXDSlQ5gsMxOSFl7Gp5
23+
GAKxuGIvF8bFejQP8u1f5YBxSPgHhg+vzyHJ2vmOw4r+dxpMG5B/NDpbOxQNpNmw
24+
GTOETPbQh01Y/D6jI4HGZ90c6C9dP0+Tc1PScfaE3uOqa3GEToahXzLDLCW4sQFS
25+
PYma10nXk29DfjYtaYYFMmpZnemhZG8E61OXe7fLn5/DLt1a4lZRfoEoiGAaKqQd
26+
glLB
27+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)