Skip to content

Commit

Permalink
Merge pull request #96 from dysosmus/tls-support
Browse files Browse the repository at this point in the history
added the support of SSL/TLS connections.
  • Loading branch information
serebrserg authored May 10, 2017
2 parents 585aa87 + 40a3a06 commit 66c352c
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 10 deletions.
10 changes: 8 additions & 2 deletions src/main/java/ru/yandex/clickhouse/ClickHouseConnectionImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ru.yandex.clickhouse;


import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
Expand All @@ -23,6 +24,7 @@
import java.util.concurrent.TimeUnit;



public class ClickHouseConnectionImpl implements ClickHouseConnection {
private static final Logger log = LoggerFactory.getLogger(ClickHouseConnectionImpl.class);

Expand All @@ -49,8 +51,12 @@ public ClickHouseConnectionImpl(String url, ClickHouseProperties properties) {
}
ClickHouseHttpClientBuilder clientBuilder = new ClickHouseHttpClientBuilder(this.properties);
log.debug("new connection");
httpclient = clientBuilder.buildClient();
initTimeZone(properties);
try {
httpclient = clientBuilder.buildClient();
}catch (Exception e) {
throw new IllegalStateException("cannot initialize http client", e);
}
initTimeZone(properties);
}

private void initTimeZone(ClickHouseProperties properties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.*;



public class ClickHouseStatementImpl implements ClickHouseStatement {

private static final Logger log = LoggerFactory.getLogger(ClickHouseStatementImpl.class);
Expand Down Expand Up @@ -541,7 +542,7 @@ URI buildRequestUri(
);

return new URIBuilder()
.setScheme("http")
.setScheme(properties.getSsl() ? "https" : "http")
.setHost(properties.getHost())
.setPort(properties.getPort())
.setPath("/")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public enum ClickHouseConnectionSettings implements DriverPropertyCreator {
APACHE_BUFFER_SIZE("apache_buffer_size", 65536, ""),
SOCKET_TIMEOUT("socket_timeout", 30000, ""),
CONNECTION_TIMEOUT("connection_timeout", 50, ""),
SSL("ssl", false, "enable SSL/TLS for the connection"),
SSL_ROOT_CERTIFICATE("sslrootcert", "", "SSL/TLS root certificate"),
SSL_MODE("sslmode", "strict", "verify or not certificate: none (don't verify), strict (verify)"),

/*
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Properties;



public class ClickHouseProperties {

// connection settings
Expand All @@ -22,6 +23,9 @@ public class ClickHouseProperties {
private int maxTotal;
private String host;
private int port;
private boolean ssl;
private String sslRootCertificate;
private String sslMode;

//additional
private int maxCompressBufferSize;
Expand Down Expand Up @@ -68,6 +72,9 @@ public ClickHouseProperties(Properties info) {
this.defaultMaxPerRoute = (Integer)getSetting(info, ClickHouseConnectionSettings.DEFAULT_MAX_PER_ROUTE);
this.maxTotal = (Integer)getSetting(info, ClickHouseConnectionSettings.MAX_TOTAL);
this.maxCompressBufferSize = (Integer) getSetting(info, ClickHouseConnectionSettings.MAX_COMPRESS_BUFFER_SIZE);
this.ssl = (Boolean) getSetting(info, ClickHouseConnectionSettings.SSL);
this.sslRootCertificate = (String) getSetting(info, ClickHouseConnectionSettings.SSL_ROOT_CERTIFICATE);
this.sslMode = (String) getSetting(info, ClickHouseConnectionSettings.SSL_MODE);
this.useServerTimeZone = (Boolean)getSetting(info, ClickHouseConnectionSettings.USE_SERVER_TIME_ZONE);
this.useTimeZone = (String)getSetting(info, ClickHouseConnectionSettings.USE_TIME_ZONE);

Expand Down Expand Up @@ -105,6 +112,9 @@ public Properties asProperties() {
ret.put(ClickHouseConnectionSettings.DEFAULT_MAX_PER_ROUTE.getKey(), String.valueOf(defaultMaxPerRoute));
ret.put(ClickHouseConnectionSettings.MAX_TOTAL.getKey(), String.valueOf(maxTotal));
ret.put(ClickHouseConnectionSettings.MAX_COMPRESS_BUFFER_SIZE.getKey(), String.valueOf(maxCompressBufferSize));
ret.put(ClickHouseConnectionSettings.SSL.getKey(), String.valueOf(ssl));
ret.put(ClickHouseConnectionSettings.SSL_ROOT_CERTIFICATE.getKey(), String.valueOf(sslRootCertificate));
ret.put(ClickHouseConnectionSettings.SSL_MODE.getKey(), String.valueOf(sslMode));
ret.put(ClickHouseConnectionSettings.USE_SERVER_TIME_ZONE.getKey(), String.valueOf(useServerTimeZone));
ret.put(ClickHouseConnectionSettings.USE_TIME_ZONE.getKey(), String.valueOf(useTimeZone));

Expand Down Expand Up @@ -144,6 +154,9 @@ public ClickHouseProperties(ClickHouseProperties properties) {
setDefaultMaxPerRoute(properties.defaultMaxPerRoute);
setMaxTotal(properties.maxTotal);
setMaxCompressBufferSize(properties.maxCompressBufferSize);
setSsl(properties.ssl);
setSslRootCertificate(properties.sslRootCertificate);
setSslMode(properties.sslMode);
setUseServerTimeZone(properties.useServerTimeZone);
setUseTimeZone(properties.useTimeZone);

Expand Down Expand Up @@ -392,6 +405,30 @@ public void setMaxCompressBufferSize(int maxCompressBufferSize) {
this.maxCompressBufferSize = maxCompressBufferSize;
}

public boolean getSsl() {
return ssl;
}

public void setSsl(boolean ssl) {
this.ssl = ssl;
}

public String getSslRootCertificate() {
return sslRootCertificate;
}

public void setSslRootCertificate(String sslRootCertificate) {
this.sslRootCertificate = sslRootCertificate;
}

public String getSslMode() {
return sslMode;
}

public void setSslMode(String sslMode) {
this.sslMode = sslMode;
}

public boolean isUseServerTimeZone() {
return useServerTimeZone;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,28 @@
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import ru.yandex.clickhouse.settings.ClickHouseProperties;
import ru.yandex.clickhouse.util.ssl.NonValidatingTrustManager;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;



public class ClickHouseHttpClientBuilder {
Expand All @@ -30,7 +49,7 @@ public ClickHouseHttpClientBuilder(ClickHouseProperties properties) {
this.properties = properties;
}

public CloseableHttpClient buildClient() {
public CloseableHttpClient buildClient() throws Exception {
return HttpClientBuilder.create()
.setConnectionManager(getConnectionManager())
.setKeepAliveStrategy(createKeepAliveStrategy())
Expand All @@ -40,14 +59,25 @@ public CloseableHttpClient buildClient() {
.build();
}

private PoolingHttpClientConnectionManager getConnectionManager() {
private PoolingHttpClientConnectionManager getConnectionManager()
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {
RegistryBuilder<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory());

if (properties.getSsl()) {
registry.register("https", new SSLConnectionSocketFactory(getSSLContext()));
}

//noinspection resource
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build(),
null, null, new IpVersionPriorityResolver(), properties.getTimeToLiveMillis(), TimeUnit.MILLISECONDS);
registry.build(),
null,
null,
new IpVersionPriorityResolver(),
properties.getTimeToLiveMillis(),
TimeUnit.MILLISECONDS
);

connectionManager.setDefaultMaxPerRoute(properties.getDefaultMaxPerRoute());
connectionManager.setMaxTotal(properties.getMaxTotal());
return connectionManager;
Expand Down Expand Up @@ -89,4 +119,56 @@ public long getKeepAliveDuration(HttpResponse httpResponse, HttpContext httpCont
};
}

private SSLContext getSSLContext()
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
TrustManager[] tms;
if(properties.getSslMode().equals("none")) {
tms = new TrustManager[]{new NonValidatingTrustManager()};
} else if (properties.getSslMode().equals("strict")){
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmf.init(getKeyStore());
tms = tmf.getTrustManagers();
} else {
throw new IllegalArgumentException("unknown ssl mode '"+ properties.getSslMode() +"'");
}

SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[]{}, tms, new SecureRandom());

return ctx;
}

private KeyStore getKeyStore()
throws NoSuchAlgorithmException, IOException, CertificateException, KeyStoreException {
KeyStore ks;
try {
ks = KeyStore.getInstance("jks");
ks.load(null, null); // needed to initialize the key store
} catch (KeyStoreException e) {
throw new NoSuchAlgorithmException("jks KeyStore not available");
}

InputStream caInputStream;
try {
caInputStream = new FileInputStream(properties.getSslRootCertificate());
} catch (FileNotFoundException ex) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
caInputStream = cl.getResourceAsStream(properties.getSslRootCertificate());
if(caInputStream == null) {
throw new IOException(
"Could not open SSL/TLS root certificate file '" + properties
.getSslRootCertificate() + "'", ex);
}
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Iterator<? extends Certificate> caIt = cf.generateCertificates(caInputStream).iterator();
for (int i = 0; caIt.hasNext(); i++) {
ks.setCertificateEntry("cert" + i, caIt.next());
}

return ks;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ru.yandex.clickhouse.util.ssl;

import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;

/**
* An insecure {@link javax.net.ssl.TrustManager}, that don't validate the certificate.
*/
public class NonValidatingTrustManager implements X509TrustManager {

public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}

public void checkClientTrusted(X509Certificate[] certs, String authType) {
}

public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}

0 comments on commit 66c352c

Please sign in to comment.