aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java4
-rw-r--r--server/sonar-main/src/main/java/org/sonar/application/ProcessLauncherImpl.java3
-rw-r--r--server/sonar-main/src/main/java/org/sonar/application/es/EsConnectorImpl.java27
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/es/EsConnectorImplTest.java14
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/es/EsClient.java41
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/es/EsClientProvider.java5
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/es/EsClientTest.java22
7 files changed, 105 insertions, 11 deletions
diff --git a/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java b/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java
index 28c2e0001a4..936dcb64996 100644
--- a/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java
+++ b/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java
@@ -41,6 +41,7 @@ import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HZ_PORT;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
+import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_PASSWORD;
public class AppStateFactory {
private final AppSettings settings;
@@ -74,6 +75,7 @@ public class AppStateFactory {
Set<HostAndPort> hostAndPorts = Arrays.stream(searchHosts.split(","))
.map(HostAndPort::fromString)
.collect(Collectors.toSet());
- return new EsConnectorImpl(hostAndPorts);
+ String searchPassword = props.value(CLUSTER_SEARCH_PASSWORD.getKey());
+ return new EsConnectorImpl(hostAndPorts, searchPassword);
}
}
diff --git a/server/sonar-main/src/main/java/org/sonar/application/ProcessLauncherImpl.java b/server/sonar-main/src/main/java/org/sonar/application/ProcessLauncherImpl.java
index 657d3a693e8..de47af4f884 100644
--- a/server/sonar-main/src/main/java/org/sonar/application/ProcessLauncherImpl.java
+++ b/server/sonar-main/src/main/java/org/sonar/application/ProcessLauncherImpl.java
@@ -32,7 +32,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Properties;
import java.util.function.Supplier;
import org.slf4j.Logger;
@@ -107,7 +106,7 @@ public class ProcessLauncherImpl implements ProcessLauncher {
if (processId == ProcessId.ELASTICSEARCH) {
checkArgument(esInstallation != null, "Incorrect configuration EsInstallation is null");
EsConnectorImpl esConnector = new EsConnectorImpl(singleton(HostAndPort.fromParts(esInstallation.getHost(),
- esInstallation.getHttpPort())));
+ esInstallation.getHttpPort())), esInstallation.getBootstrapPassword());
return new EsManagedProcess(process, processId, esConnector);
} else {
ProcessCommands commands = allProcessesCommands.createAfterClean(processId.getIpcIndex());
diff --git a/server/sonar-main/src/main/java/org/sonar/application/es/EsConnectorImpl.java b/server/sonar-main/src/main/java/org/sonar/application/es/EsConnectorImpl.java
index f5dbca10dcc..1c5a3263df1 100644
--- a/server/sonar-main/src/main/java/org/sonar/application/es/EsConnectorImpl.java
+++ b/server/sonar-main/src/main/java/org/sonar/application/es/EsConnectorImpl.java
@@ -26,11 +26,16 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
+import javax.annotation.Nullable;
import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.slf4j.Logger;
@@ -39,14 +44,17 @@ import org.slf4j.LoggerFactory;
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
public class EsConnectorImpl implements EsConnector {
+ private static final String ES_USERNAME = "elastic";
private static final Logger LOG = LoggerFactory.getLogger(EsConnectorImpl.class);
private final AtomicReference<RestHighLevelClient> restClient = new AtomicReference<>(null);
private final Set<HostAndPort> hostAndPorts;
+ private final String searchPassword;
- public EsConnectorImpl(Set<HostAndPort> hostAndPorts) {
+ public EsConnectorImpl(Set<HostAndPort> hostAndPorts, @Nullable String searchPassword) {
this.hostAndPorts = hostAndPorts;
+ this.searchPassword = searchPassword;
}
@Override
@@ -95,7 +103,22 @@ public class EsConnectorImpl implements EsConnector {
.collect(Collectors.joining(", "));
LOG.debug("Connected to Elasticsearch node: [{}]", addresses);
}
- return new RestHighLevelClient(RestClient.builder(httpHosts));
+
+ RestClientBuilder builder = RestClient.builder(httpHosts)
+ .setHttpClientConfigCallback(httpClientBuilder -> {
+ if (searchPassword != null) {
+ BasicCredentialsProvider provider = getBasicCredentialsProvider(searchPassword);
+ httpClientBuilder.setDefaultCredentialsProvider(provider);
+ }
+ return httpClientBuilder;
+ });
+ return new RestHighLevelClient(builder);
+ }
+
+ private static BasicCredentialsProvider getBasicCredentialsProvider(String searchPassword) {
+ BasicCredentialsProvider provider = new BasicCredentialsProvider();
+ provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(ES_USERNAME, searchPassword));
+ return provider;
}
}
diff --git a/server/sonar-main/src/test/java/org/sonar/application/es/EsConnectorImplTest.java b/server/sonar-main/src/test/java/org/sonar/application/es/EsConnectorImplTest.java
index d92dfb3f0b2..9c5e3d0d914 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/es/EsConnectorImplTest.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/es/EsConnectorImplTest.java
@@ -59,7 +59,7 @@ public class EsConnectorImplTest {
@Rule
public MockWebServer mockWebServer = new MockWebServer();
- EsConnectorImpl underTest = new EsConnectorImpl(Sets.newHashSet(HostAndPort.fromParts(mockWebServer.getHostName(), mockWebServer.getPort())));
+ EsConnectorImpl underTest = new EsConnectorImpl(Sets.newHashSet(HostAndPort.fromParts(mockWebServer.getHostName(), mockWebServer.getPort())), null);
@After
public void after() {
@@ -82,6 +82,18 @@ public class EsConnectorImplTest {
.hasValue(ClusterHealthStatus.YELLOW);
}
+ @Test
+ public void should_add_authentication_header() throws InterruptedException {
+ mockServerResponse(200, JSON_SUCCESS_RESPONSE);
+ String password = "test-password";
+
+ EsConnectorImpl underTest = new EsConnectorImpl(Sets.newHashSet(HostAndPort.fromParts(mockWebServer.getHostName(), mockWebServer.getPort())), password);
+
+ assertThat(underTest.getClusterHealthStatus())
+ .hasValue(ClusterHealthStatus.YELLOW);
+ assertThat(mockWebServer.takeRequest().getHeader("Authorization")).isEqualTo("Basic ZWxhc3RpYzp0ZXN0LXBhc3N3b3Jk");
+ }
+
private void mockServerResponse(int httpCode, String jsonResponse) {
mockWebServer.enqueue(new MockResponse()
.setResponseCode(httpCode)
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClient.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClient.java
index cb82f3a5161..eeb66afc1ac 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClient.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClient.java
@@ -27,7 +27,11 @@ import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.function.Supplier;
+import javax.annotation.Nullable;
import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
@@ -65,6 +69,7 @@ import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
@@ -72,6 +77,7 @@ import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.common.Priority;
+import org.jetbrains.annotations.NotNull;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
@@ -86,13 +92,18 @@ import static org.sonar.server.es.EsRequestDetails.computeDetailsAsString;
* with context) and profiling of requests.
*/
public class EsClient implements Closeable {
+ private static final String ES_USERNAME = "elastic";
private final RestHighLevelClient restHighLevelClient;
private final Gson gson;
public static final Logger LOGGER = Loggers.get("es");
public EsClient(HttpHost... hosts) {
- this(new MinimalRestHighLevelClient(hosts));
+ this(new MinimalRestHighLevelClient(null, hosts));
+ }
+
+ public EsClient(@Nullable String searchPassword, HttpHost... hosts) {
+ this(new MinimalRestHighLevelClient(searchPassword, hosts));
}
EsClient(RestHighLevelClient restHighLevelClient) {
@@ -256,9 +267,33 @@ public class EsClient implements Closeable {
}
static class MinimalRestHighLevelClient extends RestHighLevelClient {
+ private static final int CONNECT_TIMEOUT = 5000;
+ private static final int SOCKET_TIMEOUT = 60000;
+
+ public MinimalRestHighLevelClient(@Nullable String searchPassword, HttpHost... hosts) {
+ super(buildHttpClient(searchPassword, hosts));
+ }
+
+ @NotNull
+ private static RestClientBuilder buildHttpClient(@Nullable String searchPassword,
+ HttpHost[] hosts) {
+ return RestClient.builder(hosts)
+ .setRequestConfigCallback(r -> r
+ .setConnectTimeout(CONNECT_TIMEOUT)
+ .setSocketTimeout(SOCKET_TIMEOUT))
+ .setHttpClientConfigCallback(httpClientBuilder -> {
+ if (searchPassword != null) {
+ BasicCredentialsProvider provider = getBasicCredentialsProvider(searchPassword);
+ httpClientBuilder.setDefaultCredentialsProvider(provider);
+ }
+ return httpClientBuilder;
+ });
+ }
- public MinimalRestHighLevelClient(HttpHost... hosts) {
- super(RestClient.builder(hosts));
+ private static BasicCredentialsProvider getBasicCredentialsProvider(String searchPassword) {
+ BasicCredentialsProvider provider = new BasicCredentialsProvider();
+ provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(ES_USERNAME, searchPassword));
+ return provider;
}
MinimalRestHighLevelClient(RestClient restClient) {
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClientProvider.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClientProvider.java
index 051cd170936..7c1dd55de9e 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClientProvider.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/es/EsClientProvider.java
@@ -40,6 +40,7 @@ import static org.sonar.process.ProcessProperties.Property.CLUSTER_ENABLED;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_TYPE;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
+import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_PASSWORD;
import static org.sonar.process.ProcessProperties.Property.SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.SEARCH_PORT;
import static org.sonar.process.cluster.NodeType.SEARCH;
@@ -67,7 +68,7 @@ public class EsClientProvider extends ProviderAdapter {
LOGGER.info("Connected to remote Elasticsearch: [{}]", displayedAddresses(httpHosts));
} else {
- //defaults provided in:
+ // defaults provided in:
// * in org.sonar.process.ProcessProperties.Property.SEARCH_HOST
// * in org.sonar.process.ProcessProperties.Property.SEARCH_PORT
HostAndPort host = HostAndPort.fromParts(config.get(SEARCH_HOST.getKey()).get(), config.getInt(SEARCH_PORT.getKey()).get());
@@ -75,7 +76,7 @@ public class EsClientProvider extends ProviderAdapter {
LOGGER.info("Connected to local Elasticsearch: [{}]", displayedAddresses(httpHosts));
}
- cache = new EsClient(httpHosts.toArray(new HttpHost[0]));
+ cache = new EsClient(config.get(CLUSTER_SEARCH_PASSWORD.getKey()).orElse(null), httpHosts.toArray(new HttpHost[0]));
}
return cache;
}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/es/EsClientTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/es/EsClientTest.java
index 3f048ed39c8..7c9cc59da2c 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/es/EsClientTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/es/EsClientTest.java
@@ -22,11 +22,15 @@ package org.sonar.server.es;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Objects;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
@@ -144,6 +148,9 @@ public class EsClientTest {
" }" +
"}";
+ @Rule
+ public MockWebServer mockWebServer = new MockWebServer();
+
RestClient restClient = mock(RestClient.class);
RestHighLevelClient client = new EsClient.MinimalRestHighLevelClient(restClient);
@@ -231,6 +238,21 @@ public class EsClientTest {
underTest.clusterStats();
}
+ @Test
+ public void should_add_authentication_header() throws InterruptedException {
+ mockWebServer.enqueue(new MockResponse()
+ .setResponseCode(200)
+ .setBody(EXAMPLE_CLUSTER_STATS_JSON)
+ .setHeader("Content-Type", "application/json"));
+
+ String password = "test-password";
+ EsClient underTest = new EsClient(password, new HttpHost(mockWebServer.getHostName(), mockWebServer.getPort()));
+
+ underTest.clusterStats();
+
+ assertThat(mockWebServer.takeRequest().getHeader("Authorization")).isEqualTo("Basic ZWxhc3RpYzp0ZXN0LXBhc3N3b3Jk");
+ }
+
static class RawRequestMatcher implements ArgumentMatcher<Request> {
String endpoint;
String method;