From 308f95d9f35f42273cd61d6b0b808ca70d944f3d Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Tue, 28 Jan 2020 19:09:38 +0200 Subject: [PATCH] Add HLRC docs for AuthN and TLS (#51355) This commit adds examples in our documentation for - An HLRC instance authenticating to an elasticsearch cluster using an elasticsearch token service access token or an API key - An HLRC instance connecting to an elasticsearch cluster that is setup for TLS on the HTTP layer when the CA certificate of the cluster is available either as a PEM file or a keystore - An HLRC instance connecting to an elasticsearch cluster that requires client authentication where the client key and certificate are available in a keystore Co-Authored-By: Lisa Cawley --- .../RestClientDocumentation.java | 92 ++++++++++++++++++- .../low-level/configuration.asciidoc | 58 +++++++++++- 2 files changed, 145 insertions(+), 5 deletions(-) diff --git a/client/rest/src/test/java/org/elasticsearch/client/documentation/RestClientDocumentation.java b/client/rest/src/test/java/org/elasticsearch/client/documentation/RestClientDocumentation.java index 7ade990e1f811..ba19b21f1b0d5 100644 --- a/client/rest/src/test/java/org/elasticsearch/client/documentation/RestClientDocumentation.java +++ b/client/rest/src/test/java/org/elasticsearch/client/documentation/RestClientDocumentation.java @@ -51,10 +51,14 @@ import javax.net.ssl.SSLContext; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.util.Base64; import java.util.Iterator; import java.util.concurrent.CountDownLatch; @@ -379,11 +383,11 @@ public HttpAsyncClientBuilder customizeHttpClient( //end::rest-client-config-disable-preemptive-auth } { - Path keyStorePath = Paths.get(""); String keyStorePass = ""; //tag::rest-client-config-encrypted-communication - KeyStore truststore = KeyStore.getInstance("jks"); - try (InputStream is = Files.newInputStream(keyStorePath)) { + Path trustStorePath = Paths.get("/path/to/truststore.p12"); + KeyStore truststore = KeyStore.getInstance("pkcs12"); + try (InputStream is = Files.newInputStream(trustStorePath)) { truststore.load(is, keyStorePass.toCharArray()); } SSLContextBuilder sslBuilder = SSLContexts.custom() @@ -400,5 +404,87 @@ public HttpAsyncClientBuilder customizeHttpClient( }); //end::rest-client-config-encrypted-communication } + { + //tag::rest-client-config-trust-ca-pem + Path caCertificatePath = Paths.get("/path/to/ca.crt"); + CertificateFactory factory = + CertificateFactory.getInstance("X.509"); + Certificate trustedCa; + try (InputStream is = Files.newInputStream(caCertificatePath)) { + trustedCa = factory.generateCertificate(is); + } + KeyStore trustStore = KeyStore.getInstance("pkcs12"); + trustStore.load(null, null); + trustStore.setCertificateEntry("ca", trustedCa); + SSLContextBuilder sslContextBuilder = SSLContexts.custom() + .loadTrustMaterial(trustStore, null); + final SSLContext sslContext = sslContextBuilder.build(); + RestClient.builder( + new HttpHost("localhost", 9200, "https")) + .setHttpClientConfigCallback(new HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient( + HttpAsyncClientBuilder httpClientBuilder) { + return httpClientBuilder.setSSLContext(sslContext); + } + }); + //end::rest-client-config-trust-ca-pem + } + { + String trustStorePass = ""; + String keyStorePass = ""; + //tag::rest-client-config-mutual-tls-authentication + Path trustStorePath = Paths.get("/path/to/your/truststore.p12"); + Path keyStorePath = Paths.get("/path/to/your/keystore.p12"); + KeyStore trustStore = KeyStore.getInstance("pkcs12"); + KeyStore keyStore = KeyStore.getInstance("pkcs12"); + try (InputStream is = Files.newInputStream(trustStorePath)) { + trustStore.load(is, trustStorePass.toCharArray()); + } + try (InputStream is = Files.newInputStream(keyStorePath)) { + keyStore.load(is, keyStorePass.toCharArray()); + } + SSLContextBuilder sslBuilder = SSLContexts.custom() + .loadTrustMaterial(trustStore, null) + .loadKeyMaterial(keyStore, keyStorePass.toCharArray()); + final SSLContext sslContext = sslBuilder.build(); + RestClientBuilder builder = RestClient.builder( + new HttpHost("localhost", 9200, "https")) + .setHttpClientConfigCallback(new HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient( + HttpAsyncClientBuilder httpClientBuilder) { + return httpClientBuilder.setSSLContext(sslContext); + } + }); + //end::rest-client-config-mutual-tls-authentication + } + { + //tag::rest-client-auth-bearer-token + RestClientBuilder builder = RestClient.builder( + new HttpHost("localhost", 9200, "http")); + Header[] defaultHeaders = + new Header[]{new BasicHeader("Authorization", + "Bearer u6iuAxZ0RG1Kcm5jVFI4eU4tZU9aVFEwT2F3")}; + builder.setDefaultHeaders(defaultHeaders); + //end::rest-client-auth-bearer-token + } + { + //tag::rest-client-auth-api-key + String apiKeyId = "uqlEyn8B_gQ_jlvwDIvM"; + String apiKeySecret = "HxHWk2m4RN-V_qg9cDpuX"; + String apiKeyAuth = + Base64.getEncoder().encodeToString( + (apiKeyId + ":" + apiKeySecret) + .getBytes(StandardCharsets.UTF_8)); + RestClientBuilder builder = RestClient.builder( + new HttpHost("localhost", 9200, "http")); + Header[] defaultHeaders = + new Header[]{new BasicHeader("Authorization", + "ApiKey " + apiKeyAuth)}; + builder.setDefaultHeaders(defaultHeaders); + //end::rest-client-auth-api-key + } + } } diff --git a/docs/java-rest/low-level/configuration.asciidoc b/docs/java-rest/low-level/configuration.asciidoc index e284b52c67a67..b67f00112a8f9 100644 --- a/docs/java-rest/low-level/configuration.asciidoc +++ b/docs/java-rest/low-level/configuration.asciidoc @@ -64,21 +64,75 @@ include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-config-disa -------------------------------------------------- <1> Disable preemptive authentication +=== Other authentication methods + +==== Elasticsearch Token Service tokens + +If you want the client to authenticate with an Elasticsearch access token, set the relevant HTTP request header. +If the client makes requests on behalf of a single user only, you can set the necessary `Authorization` header as a default header as shown +in the following example: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-auth-bearer-token] +-------------------------------------------------- + +==== Elasticsearch API keys + +If you want the client to authenticate with an Elasticsearch API key, set the relevant HTTP request header. +If the client makes requests on behalf of a single user only, you can set the necessary `Authorization` header as a default header as shown +in the following example: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-auth-api-key] +-------------------------------------------------- + === Encrypted communication -Encrypted communication can also be configured through the +Encrypted communication using TLS can also be configured through the `HttpClientConfigCallback`. The https://hc.apache.org/httpcomponents-asyncclient-dev/httpasyncclient/apidocs/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.html[`org.apache.http.impl.nio.client.HttpAsyncClientBuilder`] received as an argument exposes multiple methods to configure encrypted communication: `setSSLContext`, `setSSLSessionStrategy` and `setConnectionManager`, in order of precedence from the least important. - The following is an example: + +When accessing an Elasticsearch cluster that is setup for TLS on the HTTP layer, the client needs to trust the certificate that +Elasticsearch is using. + The following is an example of setting up the client to trust the CA that has signed the certificate that Elasticsearch is using, when + that CA certificate is available in a PKCS#12 keystore: ["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-config-encrypted-communication] -------------------------------------------------- +The following is an example of setting up the client to trust the CA that has signed the certificate that Elasticsearch is using, when +that CA certificate is available as a PEM encoded file. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-config-trust-ca-pem] +-------------------------------------------------- + +When Elasticsearch is configured to require client TLS authentication, for example when a PKI realm is configured, the client needs to provide +a client certificate during the TLS handshake in order to authenticate. The following is an example of setting up the client for TLS +authentication with a certificate and a private key that are stored in a PKCS#12 keystore. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-config-mutual-tls-authentication] +-------------------------------------------------- + +If the client certificate and key are not available in a keystore but rather as PEM encoded files, you cannot use them +directly to build an SSLContext. You must rely on external libraries to parse the PEM key into a PrivateKey instance. Alternatively, you +can use external tools to build a keystore from your PEM files, as shown in the following example: + +``` +openssl pkcs12 -export -in client.crt -inkey private_key.pem \ + -name "client" -out client.p12 +``` + If no explicit configuration is provided, the http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#CustomizingStores[system default configuration] will be used.