Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding security enabled integration tests #400

Merged
merged 21 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 14 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
43 changes: 43 additions & 0 deletions .github/workflows/test_security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Security test workflow for Flow Framework
on:
push:
branches:
- "*"
pull_request:
branches:
- "*"

jobs:
Get-CI-Image-Tag:
uses: opensearch-project/opensearch-build/.github/workflows/get-ci-image-tag.yml@main
with:
product: opensearch

integ-test-with-security-linux:
strategy:
matrix:
java: [11, 17, 21]

name: Run Secuirty Integration Tests on Linux
joshpalis marked this conversation as resolved.
Show resolved Hide resolved
runs-on: ubuntu-latest
needs: Get-CI-Image-Tag
container:
# using the same image which is used by opensearch-build team to build the OpenSearch Distribution
# this image tag is subject to change as more dependencies and updates will arrive over time
image: ${{ needs.Get-CI-Image-Tag.outputs.ci-image-version-linux }}
# need to switch to root so that github actions can install runner binary on container without permission issues.
options: --user root

steps:
- name: Checkout Flow Framework
uses: actions/checkout@v1
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v1
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
- name: Run build
joshpalis marked this conversation as resolved.
Show resolved Hide resolved
# switching the user, as OpenSearch cluster can only be started as root/Administrator on linux-deb/linux-rpm/windows-zip.
run: |
chown -R 1000:1000 `pwd`
su `id -un 1000` -c "whoami && java -version && ./gradlew integTest -Dhttps=true -Duser=admin -Dpassword=admin"
owaiskazi19 marked this conversation as resolved.
Show resolved Hide resolved
226 changes: 180 additions & 46 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,48 @@
import java.nio.file.Files
import org.opensearch.gradle.testclusters.OpenSearchCluster
import org.opensearch.gradle.test.RestIntegTestTask
import java.util.concurrent.Callable
import java.nio.file.Paths

buildscript {
ext {
opensearch_version = System.getProperty("opensearch.version", "3.0.0-SNAPSHOT")
buildVersionQualifier = System.getProperty("build.version_qualifier", "")
isSnapshot = "true" == System.getProperty("build.snapshot", "true")
version_tokens = opensearch_version.tokenize('-')
opensearch_build = version_tokens[0] + '.0'
plugin_no_snapshot = opensearch_build
if (buildVersionQualifier) {
opensearch_build += "-${buildVersionQualifier}"
plugin_no_snapshot += "-${buildVersionQualifier}"
}
if (isSnapshot) {
opensearch_build += "-SNAPSHOT"
}
opensearch_group = "org.opensearch"
opensearch_no_snapshot = opensearch_build.replace("-SNAPSHOT","")
System.setProperty('tests.security.manager', 'false')
common_utils_version = System.getProperty("common_utils.version", opensearch_build)
}

repositories {
mavenLocal()
maven { url "https://aws.oss.sonatype.org/content/repositories/snapshots" }
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url 'https://jitpack.io' }
}

dependencies {
classpath "org.opensearch.gradle:build-tools:${opensearch_version}"
classpath "com.diffplug.spotless:spotless-plugin-gradle:6.23.3"
classpath "com.github.form-com.diff-coverage-gradle:diff-coverage:0.9.5"
}
}

plugins {
id "de.undercouch.download" version "5.3.0"
owaiskazi19 marked this conversation as resolved.
Show resolved Hide resolved
}

apply plugin: 'java'
apply plugin: 'idea'
Expand Down Expand Up @@ -39,42 +81,6 @@ thirdPartyAudit.enabled = false
// No need to validate pom, as we do not upload to maven/sonatype
validateNebulaPom.enabled = false

buildscript {
ext {
opensearch_version = System.getProperty("opensearch.version", "3.0.0-SNAPSHOT")
buildVersionQualifier = System.getProperty("build.version_qualifier", "")
isSnapshot = "true" == System.getProperty("build.snapshot", "true")
version_tokens = opensearch_version.tokenize('-')
opensearch_build = version_tokens[0] + '.0'
plugin_no_snapshot = opensearch_build
if (buildVersionQualifier) {
opensearch_build += "-${buildVersionQualifier}"
plugin_no_snapshot += "-${buildVersionQualifier}"
}
if (isSnapshot) {
opensearch_build += "-SNAPSHOT"
}
opensearch_group = "org.opensearch"
opensearch_no_snapshot = opensearch_build.replace("-SNAPSHOT","")
System.setProperty('tests.security.manager', 'false')
common_utils_version = System.getProperty("common_utils.version", opensearch_build)
}

repositories {
mavenLocal()
maven { url "https://aws.oss.sonatype.org/content/repositories/snapshots" }
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url 'https://jitpack.io' }
}

dependencies {
classpath "org.opensearch.gradle:build-tools:${opensearch_version}"
classpath "com.diffplug.spotless:spotless-plugin-gradle:6.23.3"
classpath "com.github.form-com.diff-coverage-gradle:diff-coverage:0.9.5"
}
}

allprojects {
// Default to the apache license
project.ext.licenseName = 'The Apache Software License, Version 2.0'
Expand Down Expand Up @@ -156,6 +162,7 @@ dependencies {

// ZipArchive dependencies used for integration tests
zipArchive group: 'org.opensearch.plugin', name:'opensearch-ml-plugin', version: "${opensearch_build}"
zipArchive group: 'org.opensearch.plugin', name:'opensearch-security', version: "${opensearch_build}"

configurations.all {
resolutionStrategy {
Expand All @@ -171,6 +178,96 @@ def opensearch_tmp_dir = rootProject.file('build/private/opensearch_tmp').absolu
opensearch_tmp_dir.mkdirs()
def _numNodes = findProperty('numNodes') as Integer ?: 1

ext{

configureSecurityPlugin = { OpenSearchCluster cluster ->

// Retrieve Security Plugin Zip from zipArchive
configurations.zipArchive.asFileTree.each {
if(it.name.contains("opensearch-security")) {
cluster.plugin(provider(new Callable<RegularFile>(){
@Override
RegularFile call() throws Exception {
return new RegularFile() {
@Override
File getAsFile() {
return it
}
}
}
})
)
}
}

cluster.getNodes().forEach { node ->
var creds = node.getCredentials()
if (creds.isEmpty()) {
creds.add(Map.of('username', 'admin', 'password', 'admin'))
} else {
creds.get(0).putAll(Map.of('username', 'admin', 'password', 'admin'))
}
}

// Config below including files are copied from security demo configuration
['esnode.pem', 'esnode-key.pem', 'root-ca.pem'].forEach { file ->
File local = Paths.get(opensearch_tmp_dir.absolutePath, file).toFile()
download.run {
src "https://raw.githubusercontent.com/opensearch-project/security/main/bwc-test/src/test/resources/security/" + file
dest local
overwrite false
}
cluster.extraConfigFile(file, local)
}

// This configuration is copied from the security plugins demo install:
// https://github.com/opensearch-project/security/blob/2.11.1.0/tools/install_demo_configuration.sh#L365-L388
cluster.setting("plugins.security.ssl.transport.pemcert_filepath", "esnode.pem")
amitgalitz marked this conversation as resolved.
Show resolved Hide resolved
cluster.setting("plugins.security.ssl.transport.pemkey_filepath", "esnode-key.pem")
cluster.setting("plugins.security.ssl.transport.pemtrustedcas_filepath", "root-ca.pem")
cluster.setting("plugins.security.ssl.transport.enforce_hostname_verification", "false")
cluster.setting("plugins.security.ssl.http.enabled", "true")
cluster.setting("plugins.security.ssl.http.pemcert_filepath", "esnode.pem")
cluster.setting("plugins.security.ssl.http.pemkey_filepath", "esnode-key.pem")
cluster.setting("plugins.security.ssl.http.pemtrustedcas_filepath", "root-ca.pem")
cluster.setting("plugins.security.allow_unsafe_democertificates", "true")
cluster.setting("plugins.security.allow_default_init_securityindex", "true")
cluster.setting("plugins.security.unsupported.inject_user.enabled", "true")

cluster.setting("plugins.security.authcz.admin_dn", "\n- CN=kirk,OU=client,O=client,L=test, C=de")
cluster.setting('plugins.security.restapi.roles_enabled', '["all_access", "security_rest_api_access"]')
cluster.setting('plugins.security.system_indices.enabled', "true")
cluster.setting('plugins.security.system_indices.indices', '[' +
'".plugins-ml-config", ' +
'".plugins-ml-connector", ' +
'".plugins-ml-model-group", ' +
'".plugins-ml-model", ".plugins-ml-task", ' +
'".plugins-ml-conversation-meta", ' +
'".plugins-ml-conversation-interactions", ' +
'".opendistro-alerting-config", ' +
'".opendistro-alerting-alert*", ' +
'".opendistro-anomaly-results*", ' +
'".opendistro-anomaly-detector*", ' +
'".opendistro-anomaly-checkpoints", ' +
'".opendistro-anomaly-detection-state", ' +
'".opendistro-reports-*", ' +
'".opensearch-notifications-*", ' +
'".opensearch-notebooks", ' +
'".opensearch-observability", ' +
'".ql-datasources", ' +
'".opendistro-asynchronous-search-response*", ' +
'".replication-metadata-store", ' +
'".opensearch-knn-models", ' +
'".geospatial-ip2geo-data*", ' +
'".plugins-flow-framework-config", ' +
'".plugins-flow-framework-templates", ' +
'".plugins-flow-framework-state"' +
owaiskazi19 marked this conversation as resolved.
Show resolved Hide resolved
joshpalis marked this conversation as resolved.
Show resolved Hide resolved
']'
)
cluster.setSecure(true)
}
}

test {
include '**/*Tests.class'
}
Expand Down Expand Up @@ -209,6 +306,20 @@ integTest {
}
}

// Exclude integration tests that require security plugin
if (System.getProperty("https") == null || System.getProperty("https") == "false") {
filter {
excludeTestsMatching "org.opensearch.flowframework.rest.FlowFrameworkSecureRestApiIT"
}
}

// Include only secure integration tests in security enabled clusters
if (System.getProperty("https") != null && System.getProperty("https") == "true") {
filter {
includeTestsMatching "org.opensearch.flowframework.rest.FlowFrameworkSecureRestApiIT"
excludeTestsMatching "org.opensearch.flowframework.rest.FlowFrameworkRestApiIT"
}
}

// doFirst delays this block until execution time
doFirst {
Expand All @@ -235,19 +346,27 @@ integTest {
testClusters.integTest {
testDistribution = "ARCHIVE"

// Installs all registered zipArchive dependencies on integTest cluster nodes
// Optionally install security
if (System.getProperty("https") != null && System.getProperty("https") == "true") {
configureSecurityPlugin(testClusters.integTest)
}

// Installs all registered zipArchive dependencies on integTest cluster nodes except security
configurations.zipArchive.asFileTree.each {
plugin(provider(new Callable<RegularFile>(){
@Override
RegularFile call() throws Exception {
return new RegularFile() {
if(!it.name.contains("opensearch-security")) {
plugin(provider(new Callable<RegularFile>(){
@Override
File getAsFile() {
return it
RegularFile call() throws Exception {
return new RegularFile() {
@Override
File getAsFile() {
return it
}
}
}
}
}
}))
})
)
}
}

// Install Flow Framwork Plugin on integTest cluster nodes
Expand Down Expand Up @@ -285,6 +404,21 @@ task integTestRemote(type: RestIntegTestTask) {
includeTestsMatching "org.opensearch.flowframework.rest.*IT"
}
}

// Exclude integration tests that require security plugin
if (System.getProperty("https") == null || System.getProperty("https") == "false") {
filter {
excludeTestsMatching "org.opensearch.flowframework.rest.FlowFrameworkSecureRestApiIT"
}
}

// Include only secure integration tests in security enabled clusters
if (System.getProperty("https") != null && System.getProperty("https") == "true") {
filter {
includeTestsMatching "org.opensearch.flowframework.rest.FlowFrameworkSecureRestApiIT"
excludeTestsMatching "org.opensearch.flowframework.rest.FlowFrameworkRestApiIT"
}
}
owaiskazi19 marked this conversation as resolved.
Show resolved Hide resolved
}

// Automatically sets up the integration test cluster locally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.ExceptionsHelper;
import org.opensearch.client.node.NodeClient;
import org.opensearch.core.action.ActionListener;
import org.opensearch.core.rest.RestStatus;
Expand Down Expand Up @@ -95,11 +96,14 @@
channel.sendResponse(new BytesRestResponse(RestStatus.CREATED, builder));
}, exception -> {
try {
FlowFrameworkException ex = (FlowFrameworkException) exception;
FlowFrameworkException ex = exception instanceof FlowFrameworkException
? (FlowFrameworkException) exception
: new FlowFrameworkException(exception.getMessage(), ExceptionsHelper.status(exception));

Check warning on line 101 in src/main/java/org/opensearch/flowframework/rest/RestCreateWorkflowAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/flowframework/rest/RestCreateWorkflowAction.java#L100-L101

Added lines #L100 - L101 were not covered by tests
XContentBuilder exceptionBuilder = ex.toXContent(channel.newErrorBuilder(), ToXContent.EMPTY_PARAMS);
channel.sendResponse(new BytesRestResponse(ex.getRestStatus(), exceptionBuilder));
} catch (IOException e) {
logger.error("Failed to send back create workflow exception", e);
logger.error("Failed to send back provision workflow exception", e);
channel.sendResponse(new BytesRestResponse(ExceptionsHelper.status(e), e.getMessage()));

Check warning on line 106 in src/main/java/org/opensearch/flowframework/rest/RestCreateWorkflowAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/flowframework/rest/RestCreateWorkflowAction.java#L105-L106

Added lines #L105 - L106 were not covered by tests
}
}));
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.ExceptionsHelper;
import org.opensearch.client.node.NodeClient;
import org.opensearch.core.action.ActionListener;
import org.opensearch.core.rest.RestStatus;
Expand Down Expand Up @@ -90,11 +91,14 @@
channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder));
}, exception -> {
try {
FlowFrameworkException ex = (FlowFrameworkException) exception;
FlowFrameworkException ex = exception instanceof FlowFrameworkException
? (FlowFrameworkException) exception
: new FlowFrameworkException(exception.getMessage(), ExceptionsHelper.status(exception));

Check warning on line 96 in src/main/java/org/opensearch/flowframework/rest/RestProvisionWorkflowAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/flowframework/rest/RestProvisionWorkflowAction.java#L95-L96

Added lines #L95 - L96 were not covered by tests
XContentBuilder exceptionBuilder = ex.toXContent(channel.newErrorBuilder(), ToXContent.EMPTY_PARAMS);
channel.sendResponse(new BytesRestResponse(ex.getRestStatus(), exceptionBuilder));
} catch (IOException e) {
logger.error("Failed to send back provision workflow exception", e);
channel.sendResponse(new BytesRestResponse(ExceptionsHelper.status(e), e.getMessage()));

Check warning on line 101 in src/main/java/org/opensearch/flowframework/rest/RestProvisionWorkflowAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/flowframework/rest/RestProvisionWorkflowAction.java#L101

Added line #L101 was not covered by tests
}
}));
} catch (FlowFrameworkException ex) {
Expand Down
Loading
Loading