Skip to content

Commit

Permalink
Add HLRC docs for AuthN and TLS (elastic#51355)
Browse files Browse the repository at this point in the history
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 <lcawley@elastic.co>
  • Loading branch information
jkakavas and lcawl committed Jan 28, 2020
1 parent a57a9a3 commit 308f95d
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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()
Expand All @@ -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
}

}
}
58 changes: 56 additions & 2 deletions docs/java-rest/low-level/configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down

0 comments on commit 308f95d

Please sign in to comment.