Skip to content

Commit a53e385

Browse files
committed
[2.x] Enable security for bwc tests (opensearch-project#3257)
Opening up a PR to describe the issues faced with BWC tests with the security plugin installed and solicit feedback. I plan to forward port this change to main, but first wanted to show this working for 2.9 -> 2.10 tests (as of the time of this writing). Thanks to the work that @scrawfor99 did in [core](opensearch-project/OpenSearch#8900) to supply security settings to testClusters to be able to run the initial wait for cluster yellow checks with a URL that includes the right protocol (`https` when security is enabled) along with a username and password to authenticate the request. I ran into 4 hurdles to get this to run: 1. Initially the cluster didn't form. After a lot of frustration, I ended up finding that by supplying `network.bind_host` and `network.publish_host` to both 127.0.0.1 it resolved the issue. These could probably be combined into a single `network.host`, but I chose to keep them separated. 2. I had issue testing changes to the gradle build-tools after making changes locally. This was the most frustrating hurdle, but ultimately the solution was to change the [`opensearch.version` setting in `bwc-test/build.gradle`](https://github.com/opensearch-project/security/blob/2.x/bwc-test/build.gradle#L47) to `2.10.0-SNAPSHOT`. This value is specifically used as the version of the gradle build-tools that the [BWC tests use](https://github.com/opensearch-project/security/blob/main/bwc-test/build.gradle#L58). The changes I made locally didn't reflect because I was publishing to maven local from the 2.x branch (currently 2.10) and it was looking for 2.9.0-SNAPSHOT artifacts. After updating the value it found my maven local snapshots. For this artifact you can produce maven local snapshots using `./gradlew :build-tools:publishToMavenLocal` from the respective branch in the core repo. 3. After the waitForYellow checks were able to run successfully, the REST Client in the SecurityBackwardsCompatibilityIT was also having problems connecting to the cluster because it didn't recognize the certificates of the server. I ended up using the overly trustworthy route where there is no SSL verification for the REST Client used in this test. I borrowed this implementation from [k-NN's ODFERestTestCase](https://github.com/opensearch-project/k-NN/blob/2.x/src/testFixtures/java/org/opensearch/knn/ODFERestTestCase.java#L118-L141) which is widely used in the plugin ecosystem. There is an open issue to abstract this class into common-utils. More work can be done here to ensure the rest-high-level-client runs with a truststore with the root certificate. 4. The last hurdle I faced was a WarningFailureException where the REST Client could not deserialize the cluster health response because of a warning that was returned with the response about the request including system indices. According to this [comment](opensearch-project/OpenSearch#1108 (comment)), this may only be enabled in snapshots. To fix this, I set preserve cluster to true which [bypasses the method](https://github.com/opensearch-project/OpenSearch/blob/main/test/framework/src/main/java/org/opensearch/test/rest/OpenSearchRestTestCase.java#L364) where the error was thrown. * Category (Enhancement, New feature, Bug fix, Test fix, Refactoring, Maintenance, Documentation) Enhancement opensearch-project#3056 - [ ] New functionality includes testing - [ ] New functionality has been documented - [ ] Commits are signed per the DCO using --signoff By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). --------- Signed-off-by: Craig Perkins <[email protected]>
1 parent 0338cdd commit a53e385

File tree

4 files changed

+89
-6
lines changed

4 files changed

+89
-6
lines changed

.github/actions/run-bwc-suite/action.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ inputs:
1414
description: 'The name of the artifacts for this run, e.g. "BWC-2.1-to-2.4-results"'
1515
required: true
1616

17+
username:
18+
description: 'Username to use for cluster health check in testClusters'
19+
required: true
20+
21+
password:
22+
description: 'Password to use for cluster health check in testClusters'
23+
required: true
24+
1725
runs:
1826
using: "composite"
1927
steps:
@@ -35,6 +43,9 @@ runs:
3543
arguments: |
3644
bwcTestSuite
3745
-Dtests.security.manager=false
46+
-Dtests.opensearch.secure=true
47+
-Dtests.opensearch.username=${{ inputs.username }}
48+
-Dtests.opensearch.password=${{ inputs.password }}
3849
-Dbwc.version.previous=${{ steps.build-previous.outputs.built-version }}
3950
-Dbwc.version.next=${{ steps.build-next.outputs.built-version }} -i
4051
build-root-directory: bwc-test

.github/workflows/ci.yml

+2
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ jobs:
131131
plugin-previous-branch: "2.x"
132132
plugin-next-branch: "current_branch"
133133
report-artifact-name: bwc-${{ matrix.platform }}-jdk${{ matrix.jdk }}
134+
username: admin
135+
password: admin
134136

135137
code-ql:
136138
runs-on: ubuntu-latest

bwc-test/build.gradle

+6-6
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ ext {
4444

4545
buildscript {
4646
ext {
47-
opensearch_version = System.getProperty("opensearch.version", "3.0.0-SNAPSHOT")
47+
opensearch_version = System.getProperty("opensearch.version", "2.10.0-SNAPSHOT")
4848
opensearch_group = "org.opensearch"
49+
common_utils_version = System.getProperty("common_utils.version", '2.9.0.0-SNAPSHOT')
4950
}
5051
repositories {
5152
mavenLocal()
@@ -70,6 +71,7 @@ dependencies {
7071
testImplementation "com.google.guava:guava:${versions.guava}"
7172
testImplementation "org.opensearch.test:framework:${opensearch_version}"
7273
testImplementation "org.apache.logging.log4j:log4j-core:${versions.log4j}"
74+
testImplementation "org.opensearch:common-utils:${common_utils_version}"
7375
}
7476

7577
loggerUsageCheck.enabled = false
@@ -87,9 +89,6 @@ String projectVersion = nextVersion
8789
String previousOpenSearch = extractVersion(previousVersion) + "-SNAPSHOT";
8890
String nextOpenSearch = extractVersion(nextVersion) + "-SNAPSHOT";
8991

90-
println previousOpenSearch + nextOpenSearch;
91-
92-
9392
// Extracts the OpenSearch version from a plugin version string, 2.4.0.0 -> 2.4.0.
9493
def String extractVersion(versionStr) {
9594
def versionMatcher = versionStr =~ /(.+?)(\.\d+)$/
@@ -125,7 +124,8 @@ def String extractVersion(versionStr) {
125124
node.extraConfigFile("esnode.pem", file("src/test/resources/security/esnode.pem"))
126125
node.extraConfigFile("esnode-key.pem", file("src/test/resources/security/esnode-key.pem"))
127126
node.extraConfigFile("root-ca.pem", file("src/test/resources/security/root-ca.pem"))
128-
node.setting("plugins.security.disabled", "true")
127+
node.setting("network.bind_host", "127.0.0.1")
128+
node.setting("network.publish_host", "127.0.0.1")
129129
node.setting("plugins.security.ssl.transport.pemcert_filepath", "esnode.pem")
130130
node.setting("plugins.security.ssl.transport.pemkey_filepath", "esnode-key.pem")
131131
node.setting("plugins.security.ssl.transport.pemtrustedcas_filepath", "root-ca.pem")
@@ -137,7 +137,7 @@ def String extractVersion(versionStr) {
137137
node.setting("plugins.security.allow_unsafe_democertificates", "true")
138138
node.setting("plugins.security.allow_default_init_securityindex", "true")
139139
node.setting("plugins.security.authcz.admin_dn", "CN=kirk,OU=client,O=client,L=test,C=de")
140-
node.setting("plugins.security.audit.type", "internal_elasticsearch")
140+
node.setting("plugins.security.audit.type", "internal_opensearch")
141141
node.setting("plugins.security.enable_snapshot_restore_privilege", "true")
142142
node.setting("plugins.security.check_snapshot_restore_write_privileges", "true")
143143
node.setting("plugins.security.restapi.roles_enabled", "[\"all_access\", \"security_rest_api_access\"]")

bwc-test/src/test/java/SecurityBackwardsCompatibilityIT.java

+70
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,27 @@
77
*/
88
package org.opensearch.security.bwc;
99

10+
import java.io.IOException;
1011
import java.util.List;
1112
import java.util.Map;
13+
import java.util.Optional;
1214
import java.util.Set;
1315
import java.util.stream.Collectors;
1416

17+
import org.apache.http.Header;
18+
import org.apache.http.HttpHost;
19+
import org.apache.http.auth.AuthScope;
20+
import org.apache.http.auth.UsernamePasswordCredentials;
21+
import org.apache.http.client.CredentialsProvider;
22+
import org.apache.http.conn.ssl.NoopHostnameVerifier;
23+
import org.apache.http.impl.client.BasicCredentialsProvider;
24+
import org.apache.http.message.BasicHeader;
25+
import org.apache.http.ssl.SSLContextBuilder;
1526
import org.junit.Assume;
1627
import org.junit.Before;
28+
import org.opensearch.common.settings.Settings;
29+
import org.opensearch.common.util.concurrent.ThreadContext;
30+
import org.opensearch.test.rest.OpenSearchRestTestCase;
1731

1832
import org.opensearch.Version;
1933
import org.opensearch.common.settings.Settings;
@@ -22,6 +36,11 @@
2236
import static org.hamcrest.MatcherAssert.assertThat;
2337
import static org.hamcrest.Matchers.hasItem;
2438

39+
import org.opensearch.client.RestClient;
40+
import org.opensearch.client.RestClientBuilder;
41+
42+
import org.junit.Assert;
43+
2544
public class SecurityBackwardsCompatibilityIT extends OpenSearchRestTestCase {
2645

2746
private ClusterType CLUSTER_TYPE;
@@ -35,6 +54,11 @@ private void testSetup() {
3554
CLUSTER_NAME = System.getProperty("tests.clustername");
3655
}
3756

57+
@Override
58+
protected final boolean preserveClusterUponCompletion() {
59+
return true;
60+
}
61+
3862
@Override
3963
protected final boolean preserveIndicesUponCompletion() {
4064
return true;
@@ -50,6 +74,11 @@ protected boolean preserveTemplatesUponCompletion() {
5074
return true;
5175
}
5276

77+
@Override
78+
protected String getProtocol() {
79+
return "https";
80+
}
81+
5382
@Override
5483
protected final Settings restClientSettings() {
5584
return Settings.builder()
@@ -61,6 +90,41 @@ protected final Settings restClientSettings() {
6190
.build();
6291
}
6392

93+
@Override
94+
protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException {
95+
RestClientBuilder builder = RestClient.builder(hosts);
96+
configureHttpsClient(builder, settings);
97+
boolean strictDeprecationMode = settings.getAsBoolean("strictDeprecationMode", true);
98+
builder.setStrictDeprecationMode(strictDeprecationMode);
99+
return builder.build();
100+
}
101+
102+
protected static void configureHttpsClient(RestClientBuilder builder, Settings settings) throws IOException {
103+
Map<String, String> headers = ThreadContext.buildDefaultHeaders(settings);
104+
Header[] defaultHeaders = new Header[headers.size()];
105+
int i = 0;
106+
for (Map.Entry<String, String> entry : headers.entrySet()) {
107+
defaultHeaders[i++] = new BasicHeader(entry.getKey(), entry.getValue());
108+
}
109+
builder.setDefaultHeaders(defaultHeaders);
110+
builder.setHttpClientConfigCallback(httpClientBuilder -> {
111+
String userName = Optional.ofNullable(System.getProperty("tests.opensearch.username"))
112+
.orElseThrow(() -> new RuntimeException("user name is missing"));
113+
String password = Optional.ofNullable(System.getProperty("tests.opensearch.password"))
114+
.orElseThrow(() -> new RuntimeException("password is missing"));
115+
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
116+
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
117+
try {
118+
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
119+
// disable the certificate since our testing cluster just uses the default security configuration
120+
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
121+
.setSSLContext(SSLContextBuilder.create().loadTrustMaterial(null, (chains, authType) -> true).build());
122+
} catch (Exception e) {
123+
throw new RuntimeException(e);
124+
}
125+
});
126+
}
127+
64128
public void testBasicBackwardsCompatibility() throws Exception {
65129
String round = System.getProperty("tests.rest.bwcsuite_round");
66130

@@ -73,6 +137,12 @@ public void testBasicBackwardsCompatibility() throws Exception {
73137
}
74138
}
75139

140+
@SuppressWarnings("unchecked")
141+
public void testWhoAmI() throws Exception {
142+
Map<String, Object> responseMap = (Map<String, Object>) getAsMap("_plugins/_security/whoami");
143+
Assert.assertTrue(responseMap.containsKey("dn"));
144+
}
145+
76146
private enum ClusterType {
77147
OLD,
78148
MIXED,

0 commit comments

Comments
 (0)