Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate client transports to Apache HttpClient / Core 5.x #2172

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ buildscript {
version_tokens = opensearch_version.tokenize('-')
opensearch_build = version_tokens[0] + '.0'

common_utils_version = System.getProperty("common_utils.version", '2.1.0.0')
common_utils_version = System.getProperty("common_utils.version", '3.0.0.0-SNAPSHOT')
kafka_version = '3.0.2'

if (buildVersionQualifier) {
Expand Down Expand Up @@ -293,6 +293,13 @@ task integrationTest(type: Test) {
check.dependsOn integrationTest

dependencies {
api "org.apache.httpcomponents:httpclient:${versions.httpclient}"
api "org.apache.httpcomponents:httpcore:${versions.httpcore}"
api "org.apache.httpcomponents:httpasyncclient:${versions.httpasyncclient}"
api "org.apache.httpcomponents:httpcore-nio:${versions.httpcore}"
implementation "org.apache.httpcomponents:httpclient-cache:${versions.httpclient}"
testImplementation "org.apache.httpcomponents:fluent-hc:${versions.httpclient}"

implementation 'jakarta.annotation:jakarta.annotation-api:1.3.5'
implementation "org.opensearch.plugin:transport-netty4-client:${opensearch_version}"
implementation "org.opensearch.client:opensearch-rest-high-level-client:${opensearch_version}"
Expand All @@ -301,7 +308,6 @@ dependencies {
implementation 'commons-cli:commons-cli:1.3.1'
implementation "org.bouncycastle:bcprov-jdk15on:${versions.bouncycastle}"
implementation 'org.ldaptive:ldaptive:1.2.3'
implementation 'org.apache.httpcomponents:httpclient-cache:4.5.13'
implementation 'io.jsonwebtoken:jjwt-api:0.10.8'
implementation('org.apache.cxf:cxf-rt-rs-security-jose:3.4.5') {
exclude(group: 'jakarta.activation', module: 'jakarta.activation-api')
Expand Down Expand Up @@ -348,8 +354,6 @@ dependencies {
implementation 'commons-lang:commons-lang:2.4'
implementation 'commons-collections:commons-collections:3.2.2'
implementation 'com.jayway.jsonpath:json-path:2.4.0'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
implementation 'net.minidev:json-smart:2.4.7'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.10.8'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.10.8'
Expand Down Expand Up @@ -386,7 +390,6 @@ dependencies {
testImplementation 'com.github.stephenc.jcip:jcip-annotations:1.0-1'
testImplementation 'com.unboundid:unboundid-ldapsdk:4.0.9'
testImplementation 'javax.servlet:servlet-api:2.5'
testImplementation 'org.apache.httpcomponents:fluent-hc:4.5.13'
testImplementation "org.apache.kafka:kafka_2.13:${kafka_version}"
testImplementation "org.apache.kafka:kafka_2.13:${kafka_version}:test"
testImplementation "org.apache.kafka:kafka-clients:${kafka_version}:test"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,17 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;

import org.opensearch.client.RestClient;
import org.opensearch.client.RestClientBuilder;
Expand Down Expand Up @@ -94,17 +96,26 @@ default TestRestClient getRestClient(UserCredentialsHolder user, Header... heade

default RestHighLevelClient getRestHighLevelClient(UserCredentialsHolder user) {
InetSocketAddress httpAddress = getHttpAddress();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user.getName(), user.getPassword()));

RestClientBuilder.HttpClientConfigCallback configCallback = httpClientBuilder -> {
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider).setSSLStrategy(
new SSLIOSessionStrategy(getSSLContext(), null, null, NoopHostnameVerifier.INSTANCE));

return httpClientBuilder;
};

RestClientBuilder builder = RestClient.builder(new HttpHost(httpAddress.getHostString(), httpAddress.getPort(), "https"))
HttpHost httpHost = new HttpHost("https", httpAddress.getHostString(), httpAddress.getPort());
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(httpHost), new UsernamePasswordCredentials(user.getName(), user.getPassword().toCharArray()));

final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder
.create()
.setSslContext(getSSLContext())
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();

final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(tlsStrategy)
.build();

RestClientBuilder.HttpClientConfigCallback configCallback = httpClientBuilder ->
httpClientBuilder
.setDefaultCredentialsProvider(credentialsProvider)
.setConnectionManager(connectionManager);

RestClientBuilder builder = RestClient.builder(httpHost)
.setHttpClientConfigCallback(configCallback);


Expand Down
61 changes: 35 additions & 26 deletions src/main/java/org/opensearch/security/httpclient/HttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStore;
Expand All @@ -28,21 +27,25 @@
import java.util.stream.Collectors;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;

import com.google.common.collect.Lists;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.ssl.PrivateKeyDetails;
import org.apache.http.ssl.PrivateKeyStrategy;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.ssl.PrivateKeyDetails;
import org.apache.hc.core5.ssl.PrivateKeyStrategy;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.Timeout;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand Down Expand Up @@ -153,7 +156,7 @@ private HttpClient(final KeyStore trustStore, final String basicCredentials, fin

HttpHost[] hosts = Arrays.stream(servers)
.map(s->s.split(":"))
.map(s->new HttpHost(s[0], Integer.parseInt(s[1]),ssl?"https":"http"))
.map(s->new HttpHost(ssl?"https":"http", s[0], Integer.parseInt(s[1])))
.collect(Collectors.toList()).toArray(new HttpHost[0]);


Expand Down Expand Up @@ -223,7 +226,7 @@ private final HttpAsyncClientBuilder asyncClientBuilder(HttpAsyncClientBuilder h
sslContextBuilder.loadKeyMaterial(keystore, keyPassword, new PrivateKeyStrategy() {

@Override
public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
public String chooseAlias(Map<String, PrivateKeyDetails> aliases, SSLParameters sslParameters) {
if(aliases == null || aliases.isEmpty()) {
return keystoreAlias;
}
Expand All @@ -238,13 +241,19 @@ public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket)

final HostnameVerifier hnv = verifyHostnames?new DefaultHostnameVerifier():NoopHostnameVerifier.INSTANCE;

final SSLContext sslContext = sslContextBuilder.build();
httpClientBuilder.setSSLStrategy(new SSLIOSessionStrategy(
sslContext,
supportedProtocols,
supportedCipherSuites,
hnv
));
final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder
.create()
.setSslContext(sslContextBuilder.build())
.setCiphers(supportedCipherSuites)
.setTlsVersions(supportedProtocols)
.setHostnameVerifier(hnv)
.build();

final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(tlsStrategy)
.build();

httpClientBuilder.setConnectionManager(connectionManager);
}

if (basicCredentials != null) {
Expand All @@ -255,9 +264,9 @@ public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket)
int timeout = 5;

RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout * 1000)
.setConnectionRequestTimeout(timeout * 1000)
.setSocketTimeout(timeout * 1000).build();
.setConnectTimeout(Timeout.ofSeconds(timeout))
.setConnectionRequestTimeout(Timeout.ofSeconds(timeout))
.setResponseTimeout(Timeout.ofSeconds(timeout)).build();

httpClientBuilder.setDefaultRequestConfig(config);

Expand Down
43 changes: 25 additions & 18 deletions src/main/java/org/opensearch/security/tools/SecurityAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
Expand Down Expand Up @@ -70,12 +71,15 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.SSLContexts;

import org.opensearch.ExceptionsHelper;
import org.opensearch.OpenSearchException;
Expand Down Expand Up @@ -1387,26 +1391,29 @@ private static RestHighLevelClient getRestHighLevelClient(SSLContext sslContext,
String[] enabledProtocols,
String[] enabledCiphers,
String hostname,
int port) {
int port) throws NoSuchAlgorithmException {

final HostnameVerifier hnv = !nhnv ? new DefaultHostnameVerifier() : NoopHostnameVerifier.INSTANCE;

String[] supportedProtocols = enabledProtocols.length > 0 ? enabledProtocols : null;
String[] supportedCipherSuites = enabledCiphers.length > 0 ? enabledCiphers : null;

HttpHost httpHost = new HttpHost(hostname, port, "https");
final HttpHost httpHost = new HttpHost("https", hostname, port);
final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder
.create()
.setSslContext(sslContext)
.setCiphers(supportedCipherSuites)
.setTlsVersions(supportedProtocols)
.setHostnameVerifier(hnv)
.build();

final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(tlsStrategy)
.build();

RestClientBuilder restClientBuilder = RestClient.builder(httpHost)
.setHttpClientConfigCallback(
builder -> builder.setSSLStrategy(
new SSLIOSessionStrategy(
sslContext,
supportedProtocols,
supportedCipherSuites,
hnv
)
)
);
.setHttpClientConfigCallback(builder -> builder.setConnectionManager(connectionManager));

return new RestHighLevelClient(restClientBuilder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import java.nio.file.Path;
import java.util.Map;

import org.apache.http.HttpHost;
import org.apache.hc.core5.http.HttpHost;

import org.opensearch.client.Request;
import org.opensearch.client.Response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,14 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.netty.handler.ssl.OpenSsl;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -155,15 +158,22 @@ protected RestHighLevelClient getRestClient(ClusterInfo info, String keyStoreNam
sslContextBuilder.loadTrustMaterial(trustStore, null);
SSLContext sslContext = sslContextBuilder.build();

HttpHost httpHost = new HttpHost(info.httpHost, info.httpPort, "https");
HttpHost httpHost = new HttpHost("https", info.httpHost, info.httpPort);

final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder
.create()
.setSslContext(sslContext)
.setTlsVersions("TLSv1", "TLSv1.1", "TLSv1.2", "SSLv3")
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();

final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(tlsStrategy)
.build();

RestClientBuilder restClientBuilder = RestClient.builder(httpHost)
.setHttpClientConfigCallback(
builder -> builder.setSSLStrategy(
new SSLIOSessionStrategy(sslContext,
new String[] { "TLSv1", "TLSv1.1", "TLSv1.2", "SSLv3"},
null,
NoopHostnameVerifier.INSTANCE)));
builder -> builder.setConnectionManager(connectionManager));
return new RestHighLevelClient(restClientBuilder);
} catch (Exception e) {
log.error("Cannot create client", e);
Expand Down