Skip to content

Commit 7c42c98

Browse files
wyfoavelanarius
authored andcommitted
Add ssl configuration to driver3
Inspired by https://github.com/scylladb/kafka-connect-scylladb Closes #72.
1 parent f34c78a commit 7c42c98

File tree

4 files changed

+202
-7
lines changed

4 files changed

+202
-7
lines changed

scylla-cdc-base/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,11 @@
2020
<artifactId>guava</artifactId>
2121
<version>${guava.version}</version>
2222
</dependency>
23+
<dependency>
24+
<groupId>io.netty</groupId>
25+
<artifactId>netty-handler</artifactId>
26+
<version>4.1.75.Final</version>
27+
<scope>compile</scope>
28+
</dependency>
2329
</dependencies>
2430
</project>

scylla-cdc-base/src/main/java/com/scylladb/cdc/cql/CQLConfiguration.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,11 @@ public enum ConsistencyLevel {
6363
public final String password;
6464
private final ConsistencyLevel consistencyLevel;
6565
private final String localDCName;
66+
public final SslConfig sslConfig;
6667

6768
private CQLConfiguration(List<InetSocketAddress> contactPoints,
6869
String user, String password, ConsistencyLevel consistencyLevel,
69-
String localDCName) {
70+
String localDCName, SslConfig sslConfig) {
7071
this.contactPoints = Preconditions.checkNotNull(contactPoints);
7172
Preconditions.checkArgument(!contactPoints.isEmpty());
7273

@@ -79,6 +80,7 @@ private CQLConfiguration(List<InetSocketAddress> contactPoints,
7980

8081
this.consistencyLevel = Preconditions.checkNotNull(consistencyLevel);
8182
this.localDCName = localDCName;
83+
this.sslConfig = sslConfig;
8284
}
8385

8486
/**
@@ -104,7 +106,7 @@ public ConsistencyLevel getConsistencyLevel() {
104106
* was not configured, this method returns <code>null</code>.
105107
*
106108
* @return the name of configured local datacenter or
107-
* <code>null</code> if it was not configured.
109+
* <code>null</code> if it was not configured.
108110
*/
109111
public String getLocalDCName() {
110112
return localDCName;
@@ -120,6 +122,7 @@ public static class Builder {
120122
private String password = null;
121123
private ConsistencyLevel consistencyLevel = DEFAULT_CONSISTENCY_LEVEL;
122124
private String localDCName = null;
125+
private SslConfig sslConfig = null;
123126

124127
public Builder addContactPoint(InetSocketAddress contactPoint) {
125128
Preconditions.checkNotNull(contactPoint);
@@ -181,8 +184,13 @@ public Builder withLocalDCName(String localDCName) {
181184
return this;
182185
}
183186

187+
public Builder withSslConfig(SslConfig sslConfig) {
188+
this.sslConfig = sslConfig;
189+
return this;
190+
}
191+
184192
public CQLConfiguration build() {
185-
return new CQLConfiguration(contactPoints, user, password, consistencyLevel, localDCName);
193+
return new CQLConfiguration(contactPoints, user, password, consistencyLevel, localDCName, sslConfig);
186194
}
187195
}
188196
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package com.scylladb.cdc.cql;
2+
3+
import io.netty.handler.ssl.SslProvider;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
public class SslConfig {
9+
public final SslProvider sslProvider;
10+
public final String trustStorePath;
11+
public final String trustStorePassword;
12+
public final String keyStorePath;
13+
public final String keyStorePassword;
14+
public final List<String> cipherSuites;
15+
public final String certPath;
16+
public final String privateKeyPath;
17+
18+
public SslConfig(SslProvider sslProvider, String trustStorePath, String trustStorePassword, String keyStorePath, String keyStorePassword, List<String> cipherSuites, String certPath, String privateKeyPath) {
19+
this.sslProvider = sslProvider;
20+
this.trustStorePath = trustStorePath;
21+
this.trustStorePassword = trustStorePassword;
22+
this.keyStorePath = keyStorePath;
23+
this.keyStorePassword = keyStorePassword;
24+
this.cipherSuites = cipherSuites;
25+
this.certPath = certPath;
26+
this.privateKeyPath = privateKeyPath;
27+
}
28+
29+
public static Builder builder() {
30+
return new Builder();
31+
}
32+
33+
public static class Builder {
34+
private SslProvider sslProvider = null;
35+
private String trustStorePath = null;
36+
private String trustStorePassword = null;
37+
private String keyStorePath = null;
38+
private String keyStorePassword = null;
39+
private final List<String> cipherSuites = new ArrayList<>();
40+
private String certPath = null;
41+
private String privateKeyPath = null;
42+
43+
public Builder withSslProvider(SslProvider sslProvider) {
44+
this.sslProvider = sslProvider;
45+
return this;
46+
}
47+
48+
public Builder withTrustStorePath(String trustStorePath) {
49+
this.trustStorePath = trustStorePath;
50+
return this;
51+
}
52+
53+
public Builder withTrustStorePassword(String trustStorePassword) {
54+
this.trustStorePassword = trustStorePassword;
55+
return this;
56+
}
57+
58+
public Builder withKeyStorePath(String keyStorePath) {
59+
this.keyStorePath = keyStorePath;
60+
return this;
61+
}
62+
63+
public Builder withKeyStorePassword(String keyStorePassword) {
64+
this.keyStorePassword = keyStorePassword;
65+
return this;
66+
}
67+
68+
public Builder withCipher(String cipher) {
69+
this.cipherSuites.add(cipher);
70+
return this;
71+
}
72+
73+
public Builder withCertPath(String certPath) {
74+
this.certPath = certPath;
75+
return this;
76+
}
77+
78+
public Builder withPrivateKeyPath(String privateKeyPath) {
79+
this.privateKeyPath = privateKeyPath;
80+
return this;
81+
}
82+
83+
public SslConfig build() {
84+
return new SslConfig(sslProvider, trustStorePath, trustStorePassword, keyStorePath, keyStorePassword, cipherSuites, certPath, privateKeyPath);
85+
}
86+
}
87+
}

scylla-cdc-driver3/src/main/java/com/scylladb/cdc/cql/driver3/Driver3Session.java

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
package com.scylladb.cdc.cql.driver3;
22

3-
import com.datastax.driver.core.Cluster;
4-
import com.datastax.driver.core.ConsistencyLevel;
5-
import com.datastax.driver.core.ProtocolVersion;
6-
import com.datastax.driver.core.Session;
3+
import com.datastax.driver.core.*;
74
import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy;
85
import com.scylladb.cdc.cql.CQLConfiguration;
6+
import com.scylladb.cdc.cql.SslConfig;
7+
import io.netty.handler.ssl.SslContext;
8+
import io.netty.handler.ssl.SslContextBuilder;
9+
10+
import javax.net.ssl.KeyManagerFactory;
11+
import javax.net.ssl.SSLException;
12+
import javax.net.ssl.TrustManagerFactory;
13+
import java.io.*;
14+
import java.nio.file.Files;
15+
import java.nio.file.Paths;
16+
import java.security.KeyStore;
17+
import java.security.KeyStoreException;
18+
import java.security.NoSuchAlgorithmException;
19+
import java.security.UnrecoverableKeyException;
20+
import java.security.cert.CertificateException;
921

1022
public class Driver3Session implements AutoCloseable {
1123
private final Cluster driverCluster;
@@ -18,6 +30,71 @@ public Driver3Session(CQLConfiguration cqlConfiguration) {
1830

1931
clusterBuilder = clusterBuilder.addContactPointsWithPorts(cqlConfiguration.contactPoints);
2032

33+
if (cqlConfiguration.sslConfig != null) {
34+
SslConfig sslConfig = cqlConfiguration.sslConfig;
35+
final SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
36+
System.out.println(sslConfig.getClass().getProtectionDomain().getCodeSource().getLocation());
37+
sslContextBuilder.sslProvider(sslConfig.sslProvider);
38+
if (sslConfig.trustStorePath != null) {
39+
final KeyStore trustKeyStore = createKeyStore(sslConfig.trustStorePath, sslConfig.trustStorePassword);
40+
41+
final TrustManagerFactory trustManagerFactory;
42+
try {
43+
trustManagerFactory =
44+
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
45+
trustManagerFactory.init(trustKeyStore);
46+
} catch (NoSuchAlgorithmException e) {
47+
throw new RuntimeException("Exception while creating TrustManagerFactory", e);
48+
} catch (KeyStoreException e) {
49+
throw new RuntimeException("Exception while calling TrustManagerFactory.init()", e);
50+
}
51+
sslContextBuilder.trustManager(trustManagerFactory);
52+
}
53+
54+
if (sslConfig.keyStorePath != null) {
55+
final KeyStore keyStore = createKeyStore(sslConfig.keyStorePath, sslConfig.keyStorePassword);
56+
57+
final KeyManagerFactory keyManagerFactory;
58+
try {
59+
keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
60+
keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray());
61+
} catch (NoSuchAlgorithmException e) {
62+
throw new RuntimeException("Exception while creating KeyManagerFactory", e);
63+
} catch (UnrecoverableKeyException | KeyStoreException e) {
64+
throw new RuntimeException("Exception while calling KeyManagerFactory.init()", e);
65+
}
66+
sslContextBuilder.keyManager(keyManagerFactory);
67+
}
68+
69+
if (sslConfig.cipherSuites.size() > 0) {
70+
sslContextBuilder.ciphers(sslConfig.cipherSuites);
71+
}
72+
73+
if (sslConfig.certPath != null && sslConfig.privateKeyPath != null) {
74+
try {
75+
sslContextBuilder.keyManager(new BufferedInputStream(new FileInputStream(sslConfig.certPath)),
76+
new BufferedInputStream(new FileInputStream(sslConfig.privateKeyPath)));
77+
} catch (IllegalArgumentException e) {
78+
throw new RuntimeException(String.format("Invalid certificate or private key: %s", e.getMessage()));
79+
} catch (FileNotFoundException e) {
80+
throw new RuntimeException("Invalid certificate or private key file path", e);
81+
}
82+
} else if ((sslConfig.certPath == null) != (sslConfig.privateKeyPath == null)) {
83+
throw new RuntimeException(String.format("%s cannot be set without %s and vice-versa: %s is not set",
84+
"scylla.ssl.openssl.keyCertChain", "scylla.ssl.openssl.privateKey",
85+
(sslConfig.certPath == null) ? "scylla.ssl.openssl.keyCertChain" : "scylla.ssl.openssl.privateKey"));
86+
}
87+
88+
final SslContext context;
89+
try {
90+
context = sslContextBuilder.build();
91+
} catch (SSLException e) {
92+
throw new RuntimeException(e);
93+
}
94+
final SSLOptions sslOptions = new RemoteEndpointAwareNettySSLOptions(context);
95+
clusterBuilder.withSSL(sslOptions);
96+
}
97+
2198
// Deliberately set the protocol version to V4,
2299
// as V5 implements returning a metadata id (schema id)
23100
// per each page. Our implementation of Driver3WorkerCQL
@@ -87,4 +164,21 @@ public void close() {
87164
driverCluster.close();
88165
}
89166
}
167+
168+
private KeyStore createKeyStore(String path, String password) {
169+
KeyStore keyStore;
170+
try {
171+
keyStore = KeyStore.getInstance("JKS");
172+
try (InputStream inputStream = Files.newInputStream(Paths.get(path))) {
173+
keyStore.load(inputStream, password.toCharArray());
174+
} catch (IOException e) {
175+
throw new RuntimeException("Exception while reading keystore", e);
176+
} catch (CertificateException | NoSuchAlgorithmException e) {
177+
throw new RuntimeException("Exception while loading keystore", e);
178+
}
179+
} catch (KeyStoreException e) {
180+
throw new RuntimeException("Exception while creating keystore", e);
181+
}
182+
return keyStore;
183+
}
90184
}

0 commit comments

Comments
 (0)