Skip to content

Commit 811f26d

Browse files
authored
Ensure that dual mode enabled flag from cluster settings can get propagated to core (opensearch-project#4820)
Signed-off-by: Craig Perkins <[email protected]>
1 parent 3edfac8 commit 811f26d

File tree

7 files changed

+156
-8
lines changed

7 files changed

+156
-8
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*
9+
*/
10+
package org.opensearch.security;
11+
12+
import java.util.Map;
13+
14+
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
15+
import org.junit.ClassRule;
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
19+
import org.opensearch.security.support.ConfigConstants;
20+
import org.opensearch.test.framework.cluster.ClusterManager;
21+
import org.opensearch.test.framework.cluster.LocalCluster;
22+
import org.opensearch.test.framework.cluster.TestRestClient;
23+
24+
import static java.util.concurrent.TimeUnit.SECONDS;
25+
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL;
26+
import static org.awaitility.Awaitility.await;
27+
import static org.junit.Assert.assertEquals;
28+
29+
/**
30+
* Test related to SSL-only mode of security plugin. In this mode, the security plugin is responsible only for TLS/SSL encryption.
31+
* Therefore, the plugin does not perform authentication and authorization. Moreover, the REST resources (e.g. /_plugins/_security/whoami,
32+
* /_plugins/_security/authinfo, etc.) provided by the plugin are not available.
33+
*/
34+
@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class)
35+
@ThreadLeakScope(ThreadLeakScope.Scope.NONE)
36+
public class EncryptionInTransitMigrationTests {
37+
38+
@ClassRule
39+
public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.DEFAULT)
40+
.anonymousAuth(false)
41+
.loadConfigurationIntoIndex(false)
42+
.nodeSettings(Map.of(ConfigConstants.SECURITY_SSL_ONLY, true))
43+
.sslOnly(true)
44+
.nodeSpecificSettings(0, Map.of(ConfigConstants.SECURITY_CONFIG_SSL_DUAL_MODE_ENABLED, true))
45+
.nodeSpecificSettings(1, Map.of(ConfigConstants.SECURITY_CONFIG_SSL_DUAL_MODE_ENABLED, true))
46+
.extectedNodeStartupCount(2)
47+
.authc(AUTHC_HTTPBASIC_INTERNAL)
48+
.build();
49+
50+
@Test
51+
public void shouldOnlyConnectWithThirdNodeAfterDynamicDualModeChange() {
52+
try (TestRestClient client = cluster.getRestClient()) {
53+
TestRestClient.HttpResponse response = client.get("_cat/nodes");
54+
response.assertStatusCode(200);
55+
56+
String[] lines = response.getBody().split("\n");
57+
assertEquals("Expected 2 nodes in the initial response", 2, lines.length);
58+
59+
String settingsJson = "{\"persistent\": {\"plugins.security_config.ssl_dual_mode_enabled\": false}}";
60+
TestRestClient.HttpResponse settingsResponse = client.putJson("_cluster/settings", settingsJson);
61+
settingsResponse.assertStatusCode(200);
62+
63+
await().atMost(10, SECONDS).pollInterval(1, SECONDS).until(() -> {
64+
TestRestClient.HttpResponse secondResponse = client.get("_cat/nodes");
65+
String[] secondLines = secondResponse.getBody().split("\n");
66+
return secondLines.length == 3;
67+
});
68+
}
69+
}
70+
}

src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ public class LocalCluster extends ExternalResource implements AutoCloseable, Ope
8585
private final List<Class<? extends Plugin>> plugins;
8686
private final ClusterManager clusterManager;
8787
private final TestSecurityConfig testSecurityConfig;
88+
private Map<Integer, Settings> nodeSpecificOverride;
8889
private Settings nodeOverride;
90+
private Integer expectedNodeStartupCount;
8991
private final String clusterName;
9092
private final MinimumSecuritySettingsSupplierFactory minimumOpenSearchSettingsSupplierFactory;
9193
private final TestCertificates testCertificates;
@@ -100,6 +102,7 @@ private LocalCluster(
100102
String clusterName,
101103
TestSecurityConfig testSgConfig,
102104
boolean sslOnly,
105+
Map<Integer, Settings> nodeSpecificOverride,
103106
Settings nodeOverride,
104107
ClusterManager clusterManager,
105108
List<Class<? extends Plugin>> plugins,
@@ -108,13 +111,15 @@ private LocalCluster(
108111
Map<String, LocalCluster> remotes,
109112
List<TestIndex> testIndices,
110113
boolean loadConfigurationIntoIndex,
111-
String defaultConfigurationInitDirectory
114+
String defaultConfigurationInitDirectory,
115+
Integer expectedNodeStartupCount
112116
) {
113117
this.plugins = plugins;
114118
this.testCertificates = testCertificates;
115119
this.clusterManager = clusterManager;
116120
this.testSecurityConfig = testSgConfig;
117121
this.sslOnly = sslOnly;
122+
this.nodeSpecificOverride = nodeSpecificOverride;
118123
this.nodeOverride = nodeOverride;
119124
this.clusterName = clusterName;
120125
this.minimumOpenSearchSettingsSupplierFactory = new MinimumSecuritySettingsSupplierFactory(testCertificates);
@@ -125,6 +130,7 @@ private LocalCluster(
125130
if (StringUtils.isNoneBlank(defaultConfigurationInitDirectory)) {
126131
System.setProperty(INIT_CONFIGURATION_DIR, defaultConfigurationInitDirectory);
127132
}
133+
this.expectedNodeStartupCount = expectedNodeStartupCount;
128134
}
129135

130136
public String getSnapshotDirPath() {
@@ -232,14 +238,16 @@ private void start() {
232238
try {
233239
NodeSettingsSupplier nodeSettingsSupplier = minimumOpenSearchSettingsSupplierFactory.minimumOpenSearchSettings(
234240
sslOnly,
241+
nodeSpecificOverride,
235242
nodeOverride
236243
);
237244
localOpenSearchCluster = new LocalOpenSearchCluster(
238245
clusterName,
239246
clusterManager,
240247
nodeSettingsSupplier,
241248
plugins,
242-
testCertificates
249+
testCertificates,
250+
expectedNodeStartupCount
243251
);
244252

245253
localOpenSearchCluster.start();
@@ -312,8 +320,10 @@ public CertificateData getAdminCertificate() {
312320
public static class Builder {
313321

314322
private final Settings.Builder nodeOverrideSettingsBuilder = Settings.builder();
323+
private final Map<Integer, Settings.Builder> nodeSpecificOverrideSettingsBuilder = new HashMap<>();
315324

316325
private boolean sslOnly = false;
326+
private Integer expectedNodeStartupCount;
317327
private final List<Class<? extends Plugin>> plugins = new ArrayList<>();
318328
private Map<String, LocalCluster> remoteClusters = new HashMap<>();
319329
private List<LocalCluster> clusterDependencies = new ArrayList<>();
@@ -365,6 +375,11 @@ public Builder sslOnly(boolean sslOnly) {
365375
return this;
366376
}
367377

378+
public Builder extectedNodeStartupCount(int expectedNodeStartupCount) {
379+
this.expectedNodeStartupCount = expectedNodeStartupCount;
380+
return this;
381+
}
382+
368383
public Builder nodeSettings(Map<String, Object> settings) {
369384
settings.forEach((key, value) -> {
370385
if (value instanceof List) {
@@ -378,6 +393,25 @@ public Builder nodeSettings(Map<String, Object> settings) {
378393
return this;
379394
}
380395

396+
public Builder nodeSpecificSettings(int nodeNumber, Map<String, Object> settings) {
397+
if (!nodeSpecificOverrideSettingsBuilder.containsKey(nodeNumber)) {
398+
Settings.Builder builderCopy = Settings.builder();
399+
builderCopy.put(nodeOverrideSettingsBuilder.build());
400+
nodeSpecificOverrideSettingsBuilder.put(nodeNumber, builderCopy);
401+
}
402+
Settings.Builder nodeSettingsBuilder = nodeSpecificOverrideSettingsBuilder.get(nodeNumber);
403+
settings.forEach((key, value) -> {
404+
if (value instanceof List) {
405+
List<String> values = ((List<?>) value).stream().map(String::valueOf).collect(Collectors.toList());
406+
nodeSettingsBuilder.putList(key, values);
407+
} else {
408+
nodeSettingsBuilder.put(key, String.valueOf(value));
409+
}
410+
});
411+
412+
return this;
413+
}
414+
381415
/**
382416
* Adds additional plugins to the cluster
383417
*/
@@ -512,10 +546,15 @@ public LocalCluster build() {
512546
}
513547
clusterName += "_" + num.incrementAndGet();
514548
Settings settings = nodeOverrideSettingsBuilder.build();
549+
Map<Integer, Settings> nodeSpecificSettings = new HashMap<>();
550+
for (Map.Entry<Integer, Settings.Builder> entry : nodeSpecificOverrideSettingsBuilder.entrySet()) {
551+
nodeSpecificSettings.put(entry.getKey(), entry.getValue().build());
552+
}
515553
return new LocalCluster(
516554
clusterName,
517555
testSecurityConfig,
518556
sslOnly,
557+
nodeSpecificSettings,
519558
settings,
520559
clusterManager,
521560
plugins,
@@ -524,7 +563,8 @@ public LocalCluster build() {
524563
remoteClusters,
525564
testIndices,
526565
loadConfigurationIntoIndex,
527-
defaultConfigurationInitDirectory
566+
defaultConfigurationInitDirectory,
567+
expectedNodeStartupCount
528568
);
529569
} catch (Exception e) {
530570
log.error("Failed to build LocalCluster", e);

src/integrationTest/java/org/opensearch/test/framework/cluster/LocalOpenSearchCluster.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public class LocalOpenSearchCluster {
9797
private final List<Class<? extends Plugin>> additionalPlugins;
9898
private final List<Node> nodes = new ArrayList<>();
9999
private final TestCertificates testCertificates;
100+
private final Integer expectedNodeStartupCount;
100101

101102
private File clusterHomeDir;
102103
private List<String> seedHosts;
@@ -112,13 +113,15 @@ public LocalOpenSearchCluster(
112113
ClusterManager clusterManager,
113114
NodeSettingsSupplier nodeSettingsSupplier,
114115
List<Class<? extends Plugin>> additionalPlugins,
115-
TestCertificates testCertificates
116+
TestCertificates testCertificates,
117+
Integer expectedNodeStartCount
116118
) {
117119
this.clusterName = clusterName;
118120
this.clusterManager = clusterManager;
119121
this.nodeSettingsSupplier = nodeSettingsSupplier;
120122
this.additionalPlugins = additionalPlugins;
121123
this.testCertificates = testCertificates;
124+
this.expectedNodeStartupCount = expectedNodeStartCount;
122125
try {
123126
createClusterDirectory(clusterName);
124127
} catch (IOException e) {
@@ -198,7 +201,12 @@ public void start() throws Exception {
198201

199202
log.info("Startup finished. Waiting for GREEN");
200203

201-
waitForCluster(ClusterHealthStatus.GREEN, TimeValue.timeValueSeconds(10), nodes.size());
204+
int expectedCount = nodes.size();
205+
if (expectedNodeStartupCount != null) {
206+
expectedCount = expectedNodeStartupCount;
207+
}
208+
209+
waitForCluster(ClusterHealthStatus.GREEN, TimeValue.timeValueSeconds(10), expectedCount);
202210
log.info("Started: {}", this);
203211

204212
}

src/integrationTest/java/org/opensearch/test/framework/cluster/MinimumSecuritySettingsSupplierFactory.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
package org.opensearch.test.framework.cluster;
3030

31+
import java.util.Map;
32+
3133
import org.opensearch.common.settings.Settings;
3234
import org.opensearch.security.support.ConfigConstants;
3335
import org.opensearch.test.framework.certificate.TestCertificates;
@@ -51,6 +53,16 @@ public NodeSettingsSupplier minimumOpenSearchSettings(boolean sslOnly, Settings
5153
return i -> minimumOpenSearchSettingsBuilder(i, sslOnly).put(other).build();
5254
}
5355

56+
public NodeSettingsSupplier minimumOpenSearchSettings(boolean sslOnly, Map<Integer, Settings> nodeOverride, Settings other) {
57+
return i -> {
58+
Settings override = nodeOverride.get(i);
59+
if (override != null) {
60+
return minimumOpenSearchSettingsBuilder(i, sslOnly).put(other).put(override).build();
61+
}
62+
return minimumOpenSearchSettingsBuilder(i, sslOnly).put(other).build();
63+
};
64+
}
65+
5466
private Settings.Builder minimumOpenSearchSettingsBuilder(int node, boolean sslOnly) {
5567

5668
Settings.Builder builder = Settings.builder();

src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2166,7 +2166,9 @@ public PluginSubject getPluginSubject(Plugin plugin) {
21662166

21672167
@Override
21682168
public Optional<SecureSettingsFactory> getSecureSettingFactory(Settings settings) {
2169-
return Optional.of(new OpenSearchSecureSettingsFactory(threadPool, sks, evaluateSslExceptionHandler(), securityRestHandler));
2169+
return Optional.of(
2170+
new OpenSearchSecureSettingsFactory(threadPool, sks, evaluateSslExceptionHandler(), securityRestHandler, SSLConfig)
2171+
);
21702172
}
21712173

21722174
@SuppressWarnings("removal")

src/main/java/org/opensearch/security/ssl/OpenSearchSecureSettingsFactory.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.opensearch.security.filter.SecurityRestFilter;
2828
import org.opensearch.security.ssl.http.netty.Netty4ConditionalDecompressor;
2929
import org.opensearch.security.ssl.http.netty.Netty4HttpRequestHeaderVerifier;
30+
import org.opensearch.security.ssl.transport.SSLConfig;
3031
import org.opensearch.threadpool.ThreadPool;
3132
import org.opensearch.transport.Transport;
3233
import org.opensearch.transport.TransportAdapterProvider;
@@ -38,17 +39,20 @@ public class OpenSearchSecureSettingsFactory implements SecureSettingsFactory {
3839
private final SecurityKeyStore sks;
3940
private final SslExceptionHandler sslExceptionHandler;
4041
private final SecurityRestFilter restFilter;
42+
private final SSLConfig sslConfig;
4143

4244
public OpenSearchSecureSettingsFactory(
4345
ThreadPool threadPool,
4446
SecurityKeyStore sks,
4547
SslExceptionHandler sslExceptionHandler,
46-
SecurityRestFilter restFilter
48+
SecurityRestFilter restFilter,
49+
SSLConfig sslConfig
4750
) {
4851
this.threadPool = threadPool;
4952
this.sks = sks;
5053
this.sslExceptionHandler = sslExceptionHandler;
5154
this.restFilter = restFilter;
55+
this.sslConfig = sslConfig;
5256
}
5357

5458
@Override
@@ -64,6 +68,16 @@ public void onError(Throwable t) {
6468
});
6569
}
6670

71+
@Override
72+
public Optional<SecureTransportParameters> parameters(Settings settings) {
73+
return Optional.of(new SecureTransportParameters() {
74+
@Override
75+
public boolean dualModeEnabled() {
76+
return sslConfig.isDualModeEnabled();
77+
}
78+
});
79+
}
80+
6781
@Override
6882
public Optional<SSLEngine> buildSecureServerTransportEngine(Settings settings, Transport transport) throws SSLException {
6983
return Optional.of(sks.createServerTransportSSLEngine());

src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,9 @@ public List<String> getSettingsFilter() {
674674

675675
@Override
676676
public Optional<SecureSettingsFactory> getSecureSettingFactory(Settings settings) {
677-
return Optional.of(new OpenSearchSecureSettingsFactory(threadPool, sks, NOOP_SSL_EXCEPTION_HANDLER, securityRestHandler));
677+
return Optional.of(
678+
new OpenSearchSecureSettingsFactory(threadPool, sks, NOOP_SSL_EXCEPTION_HANDLER, securityRestHandler, SSLConfig)
679+
);
678680
}
679681

680682
protected Settings migrateSettings(Settings settings) {

0 commit comments

Comments
 (0)