Expand SQL client support

- SSL (tested)
- Basic auth
- Proxying (HTTP and SOCKS)

Original commit: elastic/x-pack-elasticsearch@3abc49897e
This commit is contained in:
Costin Leau 2017-07-28 00:06:42 +03:00
parent 9cbe332fc3
commit 38ea150f17
12 changed files with 588 additions and 45 deletions

View File

@ -5,12 +5,12 @@
*/ */
package org.elasticsearch.xpack.sql.cli; package org.elasticsearch.xpack.sql.cli;
import org.elasticsearch.xpack.sql.net.client.ConnectionConfiguration;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Properties; import java.util.Properties;
import org.elasticsearch.xpack.sql.net.client.ConnectionConfiguration;
// //
// Supports the following syntax // Supports the following syntax
// //
@ -74,7 +74,7 @@ public class CliConfiguration extends ConnectionConfiguration {
public URL asUrl() { public URL asUrl() {
// TODO: need to assemble all the various params here // TODO: need to assemble all the various params here
try { try {
return new URL(isSSL() ? "https" : "http", hostAndPort.ip, port(), urlFile); return new URL(isSSLEnabled() ? "https" : "http", hostAndPort.ip, port(), urlFile);
} catch (MalformedURLException ex) { } catch (MalformedURLException ex) {
throw new IllegalArgumentException("Cannot connect to server " + originalUrl, ex); throw new IllegalArgumentException("Cannot connect to server " + originalUrl, ex);
} }

View File

@ -176,7 +176,7 @@ public class JdbcConfiguration extends ConnectionConfiguration {
public URL asUrl() { public URL asUrl() {
// TODO: need to assemble all the various params here // TODO: need to assemble all the various params here
try { try {
return new URL(isSSL() ? "https" : "http", hostAndPort.ip, port(), urlFile); return new URL(isSSLEnabled() ? "https" : "http", hostAndPort.ip, port(), urlFile);
} catch (MalformedURLException ex) { } catch (MalformedURLException ex) {
throw new JdbcException(ex, "Cannot connect to server %s", originalUrl); throw new JdbcException(ex, "Cannot connect to server %s", originalUrl);
} }

View File

@ -4,3 +4,21 @@ forbiddenApisMain {
// does not depend on core, so only jdk and http signatures should be checked // does not depend on core, so only jdk and http signatures should be checked
signaturesURLs = [this.class.getResource('/forbidden/jdk-signatures.txt')] signaturesURLs = [this.class.getResource('/forbidden/jdk-signatures.txt')]
} }
forbiddenApisTest {
bundledSignatures -= 'jdk-non-portable'
bundledSignatures += 'jdk-internal'
}
// Allow for com.sun.net.httpserver.* usage for testing
eclipse {
classpath.file {
whenMerged { cp ->
def con = entries.find { e ->
e.kind == "con" && e.toString().contains("org.eclipse.jdt.launching.JRE_CONTAINER")
}
con.accessRules.add(new org.gradle.plugins.ide.eclipse.model.AccessRule(
"accessible", "com/sun/net/httpserver/*"))
}
}
}

View File

@ -5,8 +5,6 @@
*/ */
package org.elasticsearch.xpack.sql.net.client; package org.elasticsearch.xpack.sql.net.client;
import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -35,48 +33,30 @@ public class ConnectionConfiguration {
// Timeouts // Timeouts
// 30s // 30s
static final String CONNECT_TIMEOUT = "connect.timeout"; private static final String CONNECT_TIMEOUT = "connect.timeout";
static final String CONNECT_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.SECONDS.toMillis(30)); private static final String CONNECT_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.SECONDS.toMillis(30));
// 1m // 1m
static final String NETWORK_TIMEOUT = "network.timeout"; private static final String NETWORK_TIMEOUT = "network.timeout";
static final String NETWORK_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(1)); private static final String NETWORK_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(1));
// 1m // 1m
static final String QUERY_TIMEOUT = "query.timeout"; private static final String QUERY_TIMEOUT = "query.timeout";
static final String QUERY_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(1)); private static final String QUERY_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(1));
// 5m // 5m
static final String PAGE_TIMEOUT = "page.timeout"; private static final String PAGE_TIMEOUT = "page.timeout";
static final String PAGE_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(5)); private static final String PAGE_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(5));
static final String PAGE_SIZE = "page.size"; private static final String PAGE_SIZE = "page.size";
static final String PAGE_SIZE_DEFAULT = "1000"; private static final String PAGE_SIZE_DEFAULT = "1000";
static final String SSL = "ssl"; // Auth
static final String SSL_DEFAULT = "false";
static final String SSL_PROTOCOL = "ssl.protocol"; private static final String AUTH_USER = "user";
static final String SSL_PROTOCOL_DEFAULT = "TLS"; // SSL alternative private static final String AUTH_PASS = "pass";
static final String SSL_KEYSTORE_LOCATION = "ssl.keystore.location"; // Proxy
static final String SSL_KEYSTORE_LOCATION_DEFAULT = "";
static final String SSL_KEYSTORE_PASS = "ssl.keystore.location";
static final String SSL_KEYSTORE_PASS_DEFAULT = "";
static final String SSL_KEYSTORE_TYPE = "ssl.keystore.type";
static final String SSL_KEYSTORE_TYPE_DEFAULT = "JKS"; // PCKS12
static final String SSL_TRUSTSTORE_LOCATION = "ssl.keystore.location";
static final String SSL_TRUSTSTORE_LOCATION_DEFAULT = "";
static final String SSL_TRUSTSTORE_PASS = "ssl.keystore.location";
static final String SSL_TRUSTSTORE_PASS_DEFAULT = "";
static final String SSL_TRUSTSTORE_TYPE = "ssl.keystore.location";
static final String SSL_TRUSTSTORE_TYPE_DEFAULT = "ssl.keystore.location";
private final Properties settings; private final Properties settings;
@ -86,7 +66,12 @@ public class ConnectionConfiguration {
private long pageTimeout; private long pageTimeout;
private int pageSize; private int pageSize;
private final boolean ssl;
private final String user, pass;
private final SslConfig sslConfig;
private final ProxyConfig proxyConfig;
public ConnectionConfiguration(Properties props) { public ConnectionConfiguration(Properties props) {
settings = props != null ? new Properties(props) : new Properties(); settings = props != null ? new Properties(props) : new Properties();
@ -97,17 +82,31 @@ public class ConnectionConfiguration {
// page // page
pageTimeout = Long.parseLong(settings.getProperty(PAGE_TIMEOUT, PAGE_TIMEOUT_DEFAULT)); pageTimeout = Long.parseLong(settings.getProperty(PAGE_TIMEOUT, PAGE_TIMEOUT_DEFAULT));
pageSize = Integer.parseInt(settings.getProperty(PAGE_SIZE, PAGE_SIZE_DEFAULT)); pageSize = Integer.parseInt(settings.getProperty(PAGE_SIZE, PAGE_SIZE_DEFAULT));
ssl = StringUtils.parseBoolean(settings.getProperty(SSL, SSL_DEFAULT));
// auth
user = settings.getProperty(AUTH_USER);
pass = settings.getProperty(AUTH_PASS);
sslConfig = new SslConfig(props);
proxyConfig = new ProxyConfig(props);
}
protected boolean isSSLEnabled() {
return sslConfig.isEnabled();
}
SslConfig sslConfig() {
return sslConfig;
}
ProxyConfig proxyConfig() {
return proxyConfig;
} }
protected Properties settings() { protected Properties settings() {
return settings; return settings;
} }
protected boolean isSSL() {
return ssl;
}
public void connectTimeout(long millis) { public void connectTimeout(long millis) {
connectTimeout = millis; connectTimeout = millis;
} }
@ -139,4 +138,14 @@ public class ConnectionConfiguration {
public int pageSize() { public int pageSize() {
return pageSize; return pageSize;
} }
// auth
public String authUser() {
return user;
}
public String authPass() {
return pass;
}
} }

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.net.client;
import org.elasticsearch.xpack.sql.net.client.util.Bytes; import org.elasticsearch.xpack.sql.net.client.util.Bytes;
import org.elasticsearch.xpack.sql.net.client.util.CheckedConsumer; import org.elasticsearch.xpack.sql.net.client.util.CheckedConsumer;
import org.elasticsearch.xpack.sql.net.client.util.IOUtils; import org.elasticsearch.xpack.sql.net.client.util.IOUtils;
import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
import java.io.Closeable; import java.io.Closeable;
import java.io.DataOutput; import java.io.DataOutput;
@ -16,10 +17,14 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.util.Base64;
import java.util.function.Function; import java.util.function.Function;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import javax.net.ssl.HttpsURLConnection;
public class JreHttpUrlConnection implements Closeable { public class JreHttpUrlConnection implements Closeable {
public static <R> R http(URL url, ConnectionConfiguration cfg, Function<JreHttpUrlConnection, R> handler) { public static <R> R http(URL url, ConnectionConfiguration cfg, Function<JreHttpUrlConnection, R> handler) {
try (JreHttpUrlConnection con = new JreHttpUrlConnection(url, cfg)) { try (JreHttpUrlConnection con = new JreHttpUrlConnection(url, cfg)) {
@ -35,20 +40,51 @@ public class JreHttpUrlConnection implements Closeable {
public JreHttpUrlConnection(URL url, ConnectionConfiguration cfg) throws ClientException { public JreHttpUrlConnection(URL url, ConnectionConfiguration cfg) throws ClientException {
this.url = url; this.url = url;
try { try {
con = (HttpURLConnection) url.openConnection(); // due to the way the URL API is designed, the proxy needs to be passed in first
Proxy p = cfg.proxyConfig().proxy();
con = (HttpURLConnection) (p != null ? url.openConnection(p) : url.openConnection());
} catch (IOException ex) { } catch (IOException ex) {
throw new ClientException(ex, "Cannot setup connection to %s (%s)", url, ex.getMessage()); throw new ClientException(ex, "Cannot setup connection to %s (%s)", url, ex.getMessage());
} }
// the rest of the connection setup
setupConnection(cfg);
}
private void setupConnection(ConnectionConfiguration cfg) {
// setup basic stuff first
// timeouts
con.setConnectTimeout((int) cfg.connectTimeout()); con.setConnectTimeout((int) cfg.connectTimeout());
con.setReadTimeout((int) cfg.networkTimeout()); con.setReadTimeout((int) cfg.networkTimeout());
// disable content caching
con.setAllowUserInteraction(false); con.setAllowUserInteraction(false);
con.setUseCaches(false); con.setUseCaches(false);
// HTTP params
// HttpURL adds this header by default, HttpS does not // HttpURL adds this header by default, HttpS does not
// adding it here to be consistent // adding it here to be consistent
con.setRequestProperty("Accept-Charset", "UTF-8"); con.setRequestProperty("Accept-Charset", "UTF-8");
//con.setRequestProperty("Accept-Encoding", GZIP); //con.setRequestProperty("Accept-Encoding", GZIP);
setupSSL(cfg);
setupBasicAuth(cfg);
}
private void setupSSL(ConnectionConfiguration cfg) {
if (cfg.sslConfig().isEnabled()) {
HttpsURLConnection https = (HttpsURLConnection) con;
https.setSSLSocketFactory(cfg.sslConfig().sslSocketFactory());
}
}
private void setupBasicAuth(ConnectionConfiguration cfg) {
if (StringUtils.hasText(cfg.authUser())) {
String basicValue = cfg.authUser() + ":" + cfg.authPass();
String encoded = StringUtils.asUTFString(Base64.getEncoder().encode(StringUtils.toUTF(basicValue)));
con.setRequestProperty("Authorization", "Basic " + encoded);
}
} }
public boolean head() throws ClientException { public boolean head() throws ClientException {

View File

@ -0,0 +1,69 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.net.client;
import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Properties;
class ProxyConfig {
private static final String HTTP_PROXY = "proxy.http";
private static final String HTTP_PROXY_DEFAULT = StringUtils.EMPTY;
private static final String SOCKS_PROXY = "proxy.socks";
private static final String SOCKS_PROXY_DEFAULT = StringUtils.EMPTY;
private final Proxy proxy;
ProxyConfig(Properties settings) {
Proxy.Type type = null;
// try http first
Object[] address = host(settings.getProperty(HTTP_PROXY, HTTP_PROXY_DEFAULT), 80);
type = Proxy.Type.HTTP;
// nope, check socks
if (address == null) {
address = host(settings.getProperty(SOCKS_PROXY, SOCKS_PROXY_DEFAULT), 1080);
type = Proxy.Type.SOCKS;
}
if (address != null) {
proxy = createProxy(type, address);
}
else {
proxy = null;
}
}
@SuppressForbidden(reason = "create the actual proxy")
private Proxy createProxy(Proxy.Type type, Object[] address) {
return new Proxy(type, new InetSocketAddress((String) address[0], (int) address[1]));
}
boolean enabled() {
return proxy != null;
}
Proxy proxy() {
return proxy;
}
// returns hostname (string), port (int)
private static Object[] host(String address, int defaultPort) {
if (!StringUtils.hasText(address)) {
return null;
}
try {
URI uri = new URI(address);
Object[] results = { uri.getHost(), uri.getPort() > 0 ? uri.getPort() : defaultPort };
return results;
} catch (URISyntaxException ex) {
throw new ClientException("Unrecognized address format %s", address);
}
}
}

View File

@ -0,0 +1,164 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.net.client;
import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.util.Objects;
import java.util.Properties;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
class SslConfig {
private static final String SSL = "ssl";
private static final String SSL_DEFAULT = "false";
private static final String SSL_PROTOCOL = "ssl.protocol";
private static final String SSL_PROTOCOL_DEFAULT = "TLS"; // SSL alternative
private static final String SSL_KEYSTORE_LOCATION = "ssl.keystore.location";
private static final String SSL_KEYSTORE_LOCATION_DEFAULT = "";
private static final String SSL_KEYSTORE_PASS = "ssl.keystore.pass";
private static final String SSL_KEYSTORE_PASS_DEFAULT = "";
private static final String SSL_KEYSTORE_TYPE = "ssl.keystore.type";
private static final String SSL_KEYSTORE_TYPE_DEFAULT = "JKS"; // PCKS12
private static final String SSL_TRUSTSTORE_LOCATION = "ssl.truststore.location";
private static final String SSL_TRUSTSTORE_LOCATION_DEFAULT = "";
private static final String SSL_TRUSTSTORE_PASS = "ssl.truststore.pass";
private static final String SSL_TRUSTSTORE_PASS_DEFAULT = "";
private static final String SSL_TRUSTSTORE_TYPE = "ssl.truststore.type";
private static final String SSL_TRUSTSTORE_TYPE_DEFAULT = "JKS";
private final boolean enabled;
private final String protocol, keystoreLocation, keystorePass, keystoreType;
private final String truststoreLocation, truststorePass, truststoreType;
private final SSLContext sslContext;
SslConfig(Properties settings) {
// ssl
enabled = StringUtils.parseBoolean(settings.getProperty(SSL, SSL_DEFAULT));
protocol = settings.getProperty(SSL_PROTOCOL, SSL_PROTOCOL_DEFAULT);
keystoreLocation = settings.getProperty(SSL_KEYSTORE_LOCATION, SSL_KEYSTORE_LOCATION_DEFAULT);
keystorePass = settings.getProperty(SSL_KEYSTORE_PASS, SSL_KEYSTORE_PASS_DEFAULT);
keystoreType = settings.getProperty(SSL_KEYSTORE_TYPE, SSL_KEYSTORE_TYPE_DEFAULT);
truststoreLocation = settings.getProperty(SSL_TRUSTSTORE_LOCATION, SSL_TRUSTSTORE_LOCATION_DEFAULT);
truststorePass = settings.getProperty(SSL_TRUSTSTORE_PASS, SSL_TRUSTSTORE_PASS_DEFAULT);
truststoreType = settings.getProperty(SSL_TRUSTSTORE_TYPE, SSL_TRUSTSTORE_TYPE_DEFAULT);
sslContext = enabled ? createSSLContext() : null;
}
// ssl
boolean isEnabled() {
return enabled;
}
SSLSocketFactory sslSocketFactory() {
return sslContext.getSocketFactory();
}
private SSLContext createSSLContext() {
SSLContext ctx;
try {
ctx = SSLContext.getInstance(protocol);
ctx.init(loadKeyManagers(), loadTrustManagers(), null);
} catch (Exception ex) {
throw new ClientException(ex, "Failed to initialize SSL - %s", ex.getMessage());
}
return ctx;
}
private KeyManager[] loadKeyManagers() throws GeneralSecurityException, IOException {
if (!StringUtils.hasText(keystoreLocation)) {
return null;
}
char[] pass = (StringUtils.hasText(keystorePass) ? keystorePass.trim().toCharArray() : null);
KeyStore keyStore = loadKeyStore(keystoreLocation, pass, keystoreType);
KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmFactory.init(keyStore, pass);
return kmFactory.getKeyManagers();
}
private KeyStore loadKeyStore(String location, char[] pass, String keyStoreType) throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
Path path = Paths.get(location);
if (!Files.exists(path)) {
throw new ClientException(
"Expected to find keystore file at [%s] but was unable to. Make sure you have specified a valid URI.", location);
}
try (InputStream in = Files.newInputStream(Paths.get(location), StandardOpenOption.READ)) {
keyStore.load(in, pass);
} catch (Exception ex) {
throw new ClientException(ex, "Cannot open keystore [%s] - %s", location, ex.getMessage());
} finally {
}
return keyStore;
}
private TrustManager[] loadTrustManagers() throws GeneralSecurityException, IOException {
KeyStore keyStore = null;
if (StringUtils.hasText(truststoreLocation)) {
char[] pass = (StringUtils.hasText(truststorePass) ? truststorePass.trim().toCharArray() : null);
keyStore = loadKeyStore(truststoreLocation, pass, truststoreType);
}
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmFactory.init(keyStore);
return tmFactory.getTrustManagers();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
SslConfig other = (SslConfig) obj;
return Objects.equals(enabled, other.enabled)
&& Objects.equals(protocol, other.protocol)
&& Objects.equals(keystoreLocation, other.keystoreLocation)
&& Objects.equals(keystorePass, other.keystorePass)
&& Objects.equals(keystoreType, other.keystoreType)
&& Objects.equals(truststoreLocation, other.truststoreLocation)
&& Objects.equals(truststorePass, other.truststorePass)
&& Objects.equals(truststoreType, other.truststoreType);
}
public int hashCode() {
return getClass().hashCode();
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.net.client;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;
import org.elasticsearch.common.io.Streams;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
@SuppressWarnings("restriction")
public class BasicSSLServer {
@SuppressForbidden(reason = "it's a test, not production code")
private static class EchoHandler implements HttpHandler {
public void handle(HttpExchange e) throws IOException {
e.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0);
Streams.copy(e.getRequestBody(), e.getResponseBody());
}
}
private HttpsServer server;
private ExecutorService executor;
public void start(int port) throws Exception {
// similar to Executors.newCached but with a smaller bound and much smaller keep-alive
executor = Executors.newCachedThreadPool();
server = HttpsServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), port);
server.setHttpsConfigurator(httpConfigurator());
server.createContext("/ssl", new EchoHandler());
server.setExecutor(executor);
server.start();
}
private static HttpsConfigurator httpConfigurator() throws Exception {
char[] pass = "password".toCharArray();
// so this works on JDK 7 as well
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
KeyStore ks = KeyStore.getInstance("JKS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
ks.load(BasicSSLServer.class.getResourceAsStream("/ssl/server.keystore"), pass);
kmf.init(ks, pass);
TrustManager[] trustAll = new TrustManager[] {
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
}
};
// chain
sslContext.init(kmf.getKeyManagers(), trustAll, null);
HttpsConfigurator configurator = new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
SSLContext c = getSSLContext();
SSLParameters defaults = c.getDefaultSSLParameters();
params.setSSLParameters(defaults);
// client can send a cert if they want to (use Need to force the client to present one)
//params.setWantClientAuth(true);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
};
return configurator;
}
public void stop() {
server.stop(1);
server = null;
executor.shutdownNow();
executor = null;
}
public InetSocketAddress address() {
return server != null ? server.getAddress() : null;
}
public String url() {
return server != null ? "https://localhost:" + address().getPort() + "/ssl" : null;
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.net.client;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.net.client.util.Bytes;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.rules.ExternalResource;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Properties;
import java.util.UUID;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class SSLTests extends ESTestCase {
private static URL sslServer;
@ClassRule
public static ExternalResource SSL_SERVER = new ExternalResource() {
private BasicSSLServer server;
@Override
protected void before() throws Throwable {
server = new BasicSSLServer();
server.start(0);
sslServer = new URL(server.url());
}
@Override
protected void after() {
sslServer = null;
try {
server.stop();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
};
private ConnectionConfiguration cfg;
@Before
public void setup() throws Exception {
Properties prop = new Properties();
// ssl config
prop.setProperty("ssl", "true");
// specify the TLS just in case (who knows what else will be deprecated across JDKs)
prop.setProperty("ssl.protocol", "TLSv1.2");
prop.setProperty("ssl.keystore.location",
PathUtils.get(getClass().getResource("/ssl/client.keystore").toURI()).toRealPath().toString());
prop.setProperty("ssl.keystore.pass", "password");
// set the truststore as well since otherwise there will be cert errors ...
prop.setProperty("ssl.truststore.location",
PathUtils.get(getClass().getResource("/ssl/client.keystore").toURI()).toRealPath().toString());
prop.setProperty("ssl.truststore.pass", "password");
//prop.setProperty("ssl.accept.self.signed.certs", "true");
cfg = new ConnectionConfiguration(prop);
}
@After
public void destroy() {
cfg = null;
}
public void testSslSetup() throws Exception {
SSLContext context = SSLContext.getDefault();
SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket();
String[] protocols = socket.getSupportedProtocols();
logger.info("Supported Protocols: {}", protocols.length);
logger.info("{}", Arrays.toString(protocols));
protocols = socket.getEnabledProtocols();
logger.info("Enabled Protocols: {}", protocols.length);
logger.info("{}", Arrays.toString(protocols));
String[] ciphers = socket.getSupportedCipherSuites();
logger.info("{}", Arrays.toString(ciphers));
}
public void testSslHead() throws Exception {
assertTrue(JreHttpUrlConnection.http(sslServer, cfg, JreHttpUrlConnection::head));
}
public void testSslPost() throws Exception {
String message = UUID.randomUUID().toString();
Bytes b = JreHttpUrlConnection.http(sslServer, cfg, c -> {
return c.post(o -> {
o.writeUTF(message);
});
});
String received = new DataInputStream(new ByteArrayInputStream(b.bytes())).readUTF();
assertEquals(message, received);
}
}

Binary file not shown.

View File

@ -0,0 +1,13 @@
# setup of the SSL files
# generate keys for server and client
$ keytool -v -genkey -keyalg rsa -alias server -keypass password -keystore server.keystore -storepass password -validity 99999 -ext SAN=dns:localhost,ip:127.0.0.1
$ keytool -v -genkey -keyalg rsa -alias client -keypass password -keystore client.keystore -storepass password -validity 99999 -ext SAN=dns:localhost,ip:127.0.0.1
# generate certificates
$ keytool -v -export -alias server -file server.crt -keystore server.keystore -storepass password
$ keytool -v -export -alias client -file client.crt -keystore client.keystore -storepass password
# import the client cert into the server keystore and vice-versa
$ keytool -v -importcert -alias client -file client.crt -keystore server.keystore -storepass password
$ keytool -v -importcert -alias server -file server.crt -keystore client.keystore -storepass password

Binary file not shown.