Skip to content

Commit d1590c6

Browse files
authored
[2.x] Enable security for bwc tests (#3257)
### Description 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 ### Issues Resolved #3056 ### Check List - [ ] 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 8197431 commit d1590c6

File tree

5 files changed

+91
-7
lines changed

5 files changed

+91
-7
lines changed

.github/actions/create-bwc-build/action.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ runs:
3636
uses: gradle/gradle-build-action@v2
3737
with:
3838
cache-disabled: true
39-
arguments: assemble -Dbuild.snapshot=false
39+
arguments: assemble
4040
build-root-directory: ${{ inputs.plugin-branch }}
4141

4242
- id: get-opensearch-version
@@ -47,5 +47,5 @@ runs:
4747
- name: Copy current distro into the expected folder
4848
run: |
4949
mkdir -p ./bwc-test/src/test/resources/${{ steps.get-opensearch-version.outputs.version }}
50-
cp ${{ inputs.plugin-branch }}/build/distributions/opensearch-security-${{ steps.get-opensearch-version.outputs.version }}.zip ./bwc-test/src/test/resources/${{ steps.get-opensearch-version.outputs.version }}
50+
cp ${{ inputs.plugin-branch }}/build/distributions/opensearch-security-${{ steps.get-opensearch-version.outputs.version }}-SNAPSHOT.zip ./bwc-test/src/test/resources/${{ steps.get-opensearch-version.outputs.version }}
5151
shell: bash

.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
@@ -99,6 +99,8 @@ jobs:
9999
plugin-previous-branch: "2.9"
100100
plugin-next-branch: "current_branch"
101101
report-artifact-name: bwc-${{ matrix.platform }}-jdk${{ matrix.jdk }}
102+
username: admin
103+
password: admin
102104

103105
code-ql:
104106
runs-on: ubuntu-latest

bwc-test/build.gradle

+8-5
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", "2.9.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
@@ -84,8 +86,8 @@ String baseName = "securityBwcCluster"
8486
String bwcFilePath = "src/test/resources/"
8587
String projectVersion = nextVersion
8688

87-
String previousOpenSearch = extractVersion(previousVersion);
88-
String nextOpenSearch = extractVersion(nextVersion);
89+
String previousOpenSearch = extractVersion(previousVersion) + "-SNAPSHOT";
90+
String nextOpenSearch = extractVersion(nextVersion) + "-SNAPSHOT";
8991

9092
// Extracts the OpenSearch version from a plugin version string, 2.4.0.0 -> 2.4.0.
9193
def String extractVersion(versionStr) {
@@ -122,7 +124,8 @@ def String extractVersion(versionStr) {
122124
node.extraConfigFile("esnode.pem", file("src/test/resources/security/esnode.pem"))
123125
node.extraConfigFile("esnode-key.pem", file("src/test/resources/security/esnode-key.pem"))
124126
node.extraConfigFile("root-ca.pem", file("src/test/resources/security/root-ca.pem"))
125-
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")
126129
node.setting("plugins.security.ssl.transport.pemcert_filepath", "esnode.pem")
127130
node.setting("plugins.security.ssl.transport.pemkey_filepath", "esnode-key.pem")
128131
node.setting("plugins.security.ssl.transport.pemtrustedcas_filepath", "root-ca.pem")
@@ -134,7 +137,7 @@ def String extractVersion(versionStr) {
134137
node.setting("plugins.security.allow_unsafe_democertificates", "true")
135138
node.setting("plugins.security.allow_default_init_securityindex", "true")
136139
node.setting("plugins.security.authcz.admin_dn", "CN=kirk,OU=client,O=client,L=test,C=de")
137-
node.setting("plugins.security.audit.type", "internal_elasticsearch")
140+
node.setting("plugins.security.audit.type", "internal_opensearch")
138141
node.setting("plugins.security.enable_snapshot_restore_privilege", "true")
139142
node.setting("plugins.security.check_snapshot_restore_write_privileges", "true")
140143
node.setting("plugins.security.restapi.roles_enabled", "[\"all_access\", \"security_rest_api_access\"]")

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

+68
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,38 @@
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;
1728
import org.opensearch.common.settings.Settings;
29+
import org.opensearch.common.util.concurrent.ThreadContext;
1830
import org.opensearch.test.rest.OpenSearchRestTestCase;
1931

2032
import org.opensearch.Version;
2133

2234
import static org.hamcrest.MatcherAssert.assertThat;
2335
import static org.hamcrest.Matchers.hasItem;
2436

37+
import org.opensearch.client.RestClient;
38+
import org.opensearch.client.RestClientBuilder;
39+
40+
import org.junit.Assert;
41+
2542
public class SecurityBackwardsCompatibilityIT extends OpenSearchRestTestCase {
2643

2744
private ClusterType CLUSTER_TYPE;
@@ -35,6 +52,11 @@ private void testSetup() {
3552
CLUSTER_NAME = System.getProperty("tests.clustername");
3653
}
3754

55+
@Override
56+
protected final boolean preserveClusterUponCompletion() {
57+
return true;
58+
}
59+
3860
@Override
3961
protected final boolean preserveIndicesUponCompletion() {
4062
return true;
@@ -50,6 +72,11 @@ protected boolean preserveTemplatesUponCompletion() {
5072
return true;
5173
}
5274

75+
@Override
76+
protected String getProtocol() {
77+
return "https";
78+
}
79+
5380
@Override
5481
protected final Settings restClientSettings() {
5582
return Settings.builder()
@@ -61,6 +88,41 @@ protected final Settings restClientSettings() {
6188
.build();
6289
}
6390

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

@@ -73,6 +135,12 @@ public void testBasicBackwardsCompatibility() throws Exception {
73135
}
74136
}
75137

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

0 commit comments

Comments
 (0)