Expand SQL client support
- SSL (tested) - Basic auth - Proxying (HTTP and SOCKS) Original commit: elastic/x-pack-elasticsearch@3abc49897e
This commit is contained in:
parent
9cbe332fc3
commit
38ea150f17
|
@ -5,12 +5,12 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.cli;
|
||||
|
||||
import org.elasticsearch.xpack.sql.net.client.ConnectionConfiguration;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.elasticsearch.xpack.sql.net.client.ConnectionConfiguration;
|
||||
|
||||
//
|
||||
// Supports the following syntax
|
||||
//
|
||||
|
@ -74,7 +74,7 @@ public class CliConfiguration extends ConnectionConfiguration {
|
|||
public URL asUrl() {
|
||||
// TODO: need to assemble all the various params here
|
||||
try {
|
||||
return new URL(isSSL() ? "https" : "http", hostAndPort.ip, port(), urlFile);
|
||||
return new URL(isSSLEnabled() ? "https" : "http", hostAndPort.ip, port(), urlFile);
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new IllegalArgumentException("Cannot connect to server " + originalUrl, ex);
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ public class JdbcConfiguration extends ConnectionConfiguration {
|
|||
public URL asUrl() {
|
||||
// TODO: need to assemble all the various params here
|
||||
try {
|
||||
return new URL(isSSL() ? "https" : "http", hostAndPort.ip, port(), urlFile);
|
||||
return new URL(isSSLEnabled() ? "https" : "http", hostAndPort.ip, port(), urlFile);
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new JdbcException(ex, "Cannot connect to server %s", originalUrl);
|
||||
}
|
||||
|
|
|
@ -4,3 +4,21 @@ forbiddenApisMain {
|
|||
// does not depend on core, so only jdk and http signatures should be checked
|
||||
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/*"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.net.client;
|
||||
|
||||
import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -35,48 +33,30 @@ public class ConnectionConfiguration {
|
|||
// Timeouts
|
||||
|
||||
// 30s
|
||||
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 = "connect.timeout";
|
||||
private static final String CONNECT_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.SECONDS.toMillis(30));
|
||||
|
||||
// 1m
|
||||
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 = "network.timeout";
|
||||
private static final String NETWORK_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(1));
|
||||
|
||||
// 1m
|
||||
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 = "query.timeout";
|
||||
private static final String QUERY_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(1));
|
||||
|
||||
// 5m
|
||||
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 = "page.timeout";
|
||||
private static final String PAGE_TIMEOUT_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(5));
|
||||
|
||||
static final String PAGE_SIZE = "page.size";
|
||||
static final String PAGE_SIZE_DEFAULT = "1000";
|
||||
private static final String PAGE_SIZE = "page.size";
|
||||
private static final String PAGE_SIZE_DEFAULT = "1000";
|
||||
|
||||
static final String SSL = "ssl";
|
||||
static final String SSL_DEFAULT = "false";
|
||||
// Auth
|
||||
|
||||
static final String SSL_PROTOCOL = "ssl.protocol";
|
||||
static final String SSL_PROTOCOL_DEFAULT = "TLS"; // SSL alternative
|
||||
private static final String AUTH_USER = "user";
|
||||
private static final String AUTH_PASS = "pass";
|
||||
|
||||
static final String SSL_KEYSTORE_LOCATION = "ssl.keystore.location";
|
||||
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";
|
||||
|
||||
// Proxy
|
||||
|
||||
private final Properties settings;
|
||||
|
||||
|
@ -86,7 +66,12 @@ public class ConnectionConfiguration {
|
|||
|
||||
private long pageTimeout;
|
||||
private int pageSize;
|
||||
private final boolean ssl;
|
||||
|
||||
private final String user, pass;
|
||||
|
||||
|
||||
private final SslConfig sslConfig;
|
||||
private final ProxyConfig proxyConfig;
|
||||
|
||||
public ConnectionConfiguration(Properties props) {
|
||||
settings = props != null ? new Properties(props) : new Properties();
|
||||
|
@ -97,17 +82,31 @@ public class ConnectionConfiguration {
|
|||
// page
|
||||
pageTimeout = Long.parseLong(settings.getProperty(PAGE_TIMEOUT, PAGE_TIMEOUT_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() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected boolean isSSL() {
|
||||
return ssl;
|
||||
}
|
||||
|
||||
public void connectTimeout(long millis) {
|
||||
connectTimeout = millis;
|
||||
}
|
||||
|
@ -139,4 +138,14 @@ public class ConnectionConfiguration {
|
|||
public int pageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
// auth
|
||||
|
||||
public String authUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public String authPass() {
|
||||
return pass;
|
||||
}
|
||||
}
|
|
@ -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.CheckedConsumer;
|
||||
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.DataOutput;
|
||||
|
@ -16,10 +17,14 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.Base64;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class JreHttpUrlConnection implements Closeable {
|
||||
public static <R> R http(URL url, ConnectionConfiguration cfg, Function<JreHttpUrlConnection, R> handler) {
|
||||
try (JreHttpUrlConnection con = new JreHttpUrlConnection(url, cfg)) {
|
||||
|
@ -35,20 +40,51 @@ public class JreHttpUrlConnection implements Closeable {
|
|||
public JreHttpUrlConnection(URL url, ConnectionConfiguration cfg) throws ClientException {
|
||||
this.url = url;
|
||||
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) {
|
||||
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.setReadTimeout((int) cfg.networkTimeout());
|
||||
|
||||
// disable content caching
|
||||
con.setAllowUserInteraction(false);
|
||||
con.setUseCaches(false);
|
||||
|
||||
// HTTP params
|
||||
// HttpURL adds this header by default, HttpS does not
|
||||
// adding it here to be consistent
|
||||
con.setRequestProperty("Accept-Charset", "UTF-8");
|
||||
//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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
@ -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.
Loading…
Reference in New Issue