Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package org.elasticsearch.xpack.remotecluster;

import io.netty.handler.codec.http.HttpMethod;

import org.apache.http.HttpHost;
import org.apache.http.client.methods.HttpPost;
import org.elasticsearch.client.Request;
Expand All @@ -25,6 +27,7 @@
import org.elasticsearch.test.cluster.util.resource.Resource;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ObjectPath;
import org.elasticsearch.xpack.security.SecurityFeatures;
import org.junit.AfterClass;
import org.junit.BeforeClass;

Expand All @@ -33,8 +36,11 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.equalTo;
Expand Down Expand Up @@ -78,6 +84,7 @@ public abstract class AbstractRemoteClusterSecurityTestCase extends ESRestTestCa
.configFile("remote-cluster-client.key", Resource.fromClasspath("ssl/remote-cluster-client.key"))
.configFile("remote-cluster-client.crt", Resource.fromClasspath("ssl/remote-cluster-client.crt"))
.configFile("remote-cluster-client-ca.crt", Resource.fromClasspath("ssl/remote-cluster-client-ca.crt"))
.configFile("signing.crt", Resource.fromClasspath("signing/signing.crt"))
.module("reindex") // Needed for the role metadata migration
.user(USER, PASS.toString());

Expand Down Expand Up @@ -197,8 +204,10 @@ protected void configureRemoteCluster(
boolean isProxyMode,
boolean skipUnavailable
) throws Exception {
putFulfillingClusterSettings();

// For configurable remote cluster security, this method assumes the cross cluster access API key is already configured in keystore
putRemoteClusterSettings(clusterAlias, targetFulfillingCluster, basicSecurity, isProxyMode, skipUnavailable);
putQueryClusterSettings(clusterAlias, targetFulfillingCluster, basicSecurity, isProxyMode, skipUnavailable);

// Ensure remote cluster is connected
checkRemoteConnection(clusterAlias, targetFulfillingCluster, basicSecurity, isProxyMode);
Expand Down Expand Up @@ -234,7 +243,18 @@ protected void reloadSecureSettings() throws IOException {
}
}

protected void putRemoteClusterSettings(
protected void putFulfillingClusterSettings() throws IOException {
if (getFulfillingClusterNodeFeatures().contains(SecurityFeatures.CERTIFICATE_IDENTITY_FIELD_FEATURE.id())) {
final var request = newXContentRequest(HttpMethod.PUT, "/_cluster/settings", (builder, params) -> {
builder.startObject("persistent");
Settings.builder().put("cluster.remote.signing.certificate_authorities", "signing.crt").build().toXContent(builder, params);
return builder.endObject();
});
assertOK(performRequestAgainstFulfillingCluster(request));
}
}

protected void putQueryClusterSettings(
String clusterAlias,
ElasticsearchCluster targetFulfillingCluster,
boolean basicSecurity,
Expand Down Expand Up @@ -303,6 +323,22 @@ protected static String randomEncodedApiKey() {
.encodeToString((UUIDs.base64UUID() + ":" + UUIDs.randomBase64UUIDSecureString()).getBytes(StandardCharsets.UTF_8));
}

protected Set<String> getFulfillingClusterNodeFeatures() throws IOException {
final Request request = new Request("GET", "_cluster/state");
request.addParameter("filter_path", "nodes_features");
final Response response = performRequestAgainstFulfillingCluster(request);

var responseData = responseAsMap(response);
if (responseData.get("nodes_features") instanceof List<?> nodesFeatures) {
return nodesFeatures.stream().map(Map.class::cast).flatMap(nodeFeatureMap -> {
@SuppressWarnings("unchecked")
List<String> features = (List<String>) nodeFeatureMap.get("features");
return features.stream();
}).collect(Collectors.toSet());
}
return Set.of();
}

protected record TestClusterConfigProviders(LocalClusterConfigProvider server, LocalClusterConfigProvider client) {}

protected static TestClusterConfigProviders EMPTY_CONFIG_PROVIDERS = new TestClusterConfigProviders(cluster -> {}, cluster -> {});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ public class RemoteClusterSecurityBWCToRCS2ClusterRestIT extends AbstractRemoteC
.setting("xpack.ml.enabled", "false")
.setting("remote_cluster_server.enabled", "true")
.setting("remote_cluster.port", "0")
.setting("cluster.remote.signing.certificate_authorities", "signing.crt")
.configFile("signing.crt", Resource.fromClasspath("signing/signing.crt"))
.setting("xpack.security.remote_cluster_server.ssl.enabled", "true")
.setting("xpack.security.remote_cluster_server.ssl.key", "remote-cluster.key")
.setting("xpack.security.remote_cluster_server.ssl.certificate", "remote-cluster.crt")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ private void configureRcs2() throws Exception {
final boolean configureSettingsFirst = randomBoolean();
// it's valid to first configure remote cluster, then credentials
if (configureSettingsFirst) {
putRemoteClusterSettings("my_remote_cluster", fulfillingCluster, false, isProxyMode, randomBoolean());
putQueryClusterSettings("my_remote_cluster", fulfillingCluster, false, isProxyMode, randomBoolean());
}

configureRemoteClusterCredentials("my_remote_cluster", remoteClusterCredentials, keystoreSettings);
Expand Down