Skip to content
Open
Show file tree
Hide file tree
Changes from 13 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Add cluster defaults for merge autoThrottle, maxMergeThreads, and maxMergeCount; Add segment size filter to the merged segment warmer ([#19629](https://github.com/opensearch-project/OpenSearch/pull/19629))
- Add build-tooling to run in FIPS environment ([#18921](https://github.com/opensearch-project/OpenSearch/pull/18921))
- Add SMILE/CBOR/YAML document format support to Bulk GRPC endpoint ([#19744](https://github.com/opensearch-project/OpenSearch/pull/19744))
- Make test-suite runnable under FIPS compliance support ([#18491](https://github.com/opensearch-project/OpenSearch/pull/18491))
- Implement GRPC Search params `Highlight`and `Sort` ([#19868](https://github.com/opensearch-project/OpenSearch/pull/19868))
- Implement GRPC ConstantScoreQuery, FuzzyQuery, MatchBoolPrefixQuery, MatchPhrasePrefix, PrefixQuery, MatchQuery ([#19854](https://github.com/opensearch-project/OpenSearch/pull/19854))
- Add async periodic flush task support for pull-based ingestion ([#19878](https://github.com/opensearch-project/OpenSearch/pull/19878))
Expand Down
Binary file not shown.
4 changes: 4 additions & 0 deletions client/rest-high-level/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ apply plugin: 'opensearch.build'
apply plugin: 'opensearch.rest-test'
apply plugin: 'opensearch.publish'
apply plugin: 'opensearch.rest-resources'
apply from: "$rootDir/gradle/fips.gradle"

base {
group = 'org.opensearch.client'
Expand Down Expand Up @@ -66,6 +67,9 @@ dependencies {
testImplementation "junit:junit:${versions.junit}"
//this is needed to make RestHighLevelClientTests#testApiNamingConventions work from IDEs
testImplementation project(":rest-api-spec")
testFipsRuntimeOnly "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
testFipsRuntimeOnly "org.bouncycastle:bctls-fips:${versions.bouncycastle_tls}"
testFipsRuntimeOnly "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
}

tasks.named('forbiddenApisMain').configure {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,22 @@
import java.security.PrivilegedAction;
import java.security.SecureRandom;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

/**
* Integration test to validate the builder builds a client with the correct configuration
*/
public class RestClientBuilderIntegTests extends RestClientTestCase {
public class RestClientBuilderIntegTests extends RestClientTestCase implements RestClientFipsAwareTestCase {

private static HttpsServer httpsServer;

@BeforeClass
public static void startHttpServer() throws Exception {
httpsServer = HttpsServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
httpsServer.setHttpsConfigurator(new HttpsConfigurator(getSslContext(true)));
httpsServer.setHttpsConfigurator(new HttpsConfigurator(new RestClientBuilderIntegTests().getSslContext(true)));
httpsServer.createContext("/", new ResponseHandler());
httpsServer.start();
}
Expand All @@ -91,7 +91,6 @@ public static void stopHttpServers() throws IOException {
}

public void testBuilderUsesDefaultSSLContext() throws Exception {
assumeFalse("https://github.com/elastic/elasticsearch/issues/49094", inFipsJvm());
final SSLContext defaultSSLContext = SSLContext.getDefault();
try {
try (RestClient client = buildRestClient()) {
Expand All @@ -118,24 +117,23 @@ private RestClient buildRestClient() {
return RestClient.builder(new HttpHost("https", address.getHostString(), address.getPort())).build();
}

private static SSLContext getSslContext(boolean server) throws Exception {
@Override
public SSLContext getSslContext(boolean server, String keyStoreType, SecureRandom secureRandom, String fileExtension) throws Exception {
SSLContext sslContext;
char[] password = "password".toCharArray();
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
String fileExtension = ".jks";

try (
InputStream trustStoreFile = RestClientBuilderIntegTests.class.getResourceAsStream("/test_truststore" + fileExtension);
InputStream keyStoreFile = RestClientBuilderIntegTests.class.getResourceAsStream("/testks" + fileExtension)
) {
KeyStore keyStore = KeyStore.getInstance("JKS");
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(keyStoreFile, password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, password);

KeyStore trustStore = KeyStore.getInstance("JKS");
KeyStore trustStore = KeyStore.getInstance(keyStoreType);
trustStore.load(trustStoreFile, password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);

SSLContextBuilder sslContextBuilder = SSLContextBuilder.create().setProtocol(getProtocol()).setSecureRandom(secureRandom);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client;

import javax.net.ssl.SSLContext;

import java.security.SecureRandom;

import static org.opensearch.client.RestClientTestCase.inFipsJvm;

interface RestClientFipsAwareTestCase {

default SSLContext getSslContext(boolean server) throws Exception {
if (inFipsJvm()) {
return getSslContext(server, "BCFKS", SecureRandom.getInstance("DEFAULT", "BCFIPS"), ".bcfks");
}
return getSslContext(server, "JKS", new SecureRandom(), ".jks");
}

SSLContext getSslContext(boolean server, String keyStoreType, SecureRandom secureRandom, String fileExtension) throws Exception;
}
Binary file not shown.
Binary file added client/rest/src/test/resources/testks.bcfks
Binary file not shown.
4 changes: 4 additions & 0 deletions client/sniffer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
*/
apply plugin: 'opensearch.build'
apply plugin: 'opensearch.publish'
apply from: "$rootDir/gradle/fips.gradle"

java {
targetCompatibility = JavaVersion.VERSION_11
Expand All @@ -55,6 +56,9 @@ dependencies {
testImplementation "org.objenesis:objenesis:${versions.objenesis}"
testImplementation "net.bytebuddy:byte-buddy:${versions.bytebuddy}"
testImplementation "net.bytebuddy:byte-buddy-agent:${versions.bytebuddy}"
testFipsRuntimeOnly "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
testFipsRuntimeOnly "org.bouncycastle:bctls-fips:${versions.bouncycastle_tls}"
testFipsRuntimeOnly "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
}

tasks.named('forbiddenApisMain').configure {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.tools.cli.fips.truststore;

import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;

public class CreateFipsTrustStoreFipsTests extends CreateFipsTrustStoreTests {

public void testConvertToBCFKS() throws Exception {
// given
KeyStore sourceKeyStore = CreateFipsTrustStore.loadJvmDefaultTrustStore(spec, JAVA_HOME);
assertTrue("Source keystore should have certificates", sourceKeyStore.size() > 0);

CommonOptions options = new CommonOptions();
options.force = false;
String password = "testPassword123";

// when
Path result = CreateFipsTrustStore.convertToBCFKS(spec, sourceKeyStore, options, password, confDir);

// then
assertNotNull(result);
assertTrue(Files.exists(result));
assertTrue(result.toString().endsWith("opensearch-fips-truststore.bcfks"));

// Verify the converted keystore has the same certificates
KeyStore bcfksStore = KeyStore.getInstance("BCFKS", "BCFIPS");
try (var is = Files.newInputStream(result)) {
bcfksStore.load(is, password.toCharArray());
}
assertEquals("Converted keystore should have same number of certificates", sourceKeyStore.size(), bcfksStore.size());
}

public void testConvertToBCFKSFileExistsWithoutForce() throws Exception {
// given
KeyStore sourceKeyStore = CreateFipsTrustStore.loadJvmDefaultTrustStore(spec, JAVA_HOME);
assertTrue("Source keystore should have certificates", sourceKeyStore.size() > 0);

CommonOptions options = new CommonOptions();
options.force = false;
String password = "testPassword123";

// Create file first to simulate existing truststore
Path trustStorePath = confDir.resolve("opensearch-fips-truststore.bcfks");
Files.createFile(trustStorePath);

assertTrue("Test setup: file should exist", Files.exists(trustStorePath));

// when/then
RuntimeException exception = expectThrows(
RuntimeException.class,
() -> CreateFipsTrustStore.convertToBCFKS(spec, sourceKeyStore, options, password, confDir)
);
assertEquals("Operation cancelled. Trust store file already exists.", exception.getMessage());
}

public void testConvertToBCFKSFileExistsWithForce() throws Exception {
// given
KeyStore sourceKeyStore = CreateFipsTrustStore.loadJvmDefaultTrustStore(spec, JAVA_HOME);
assertTrue("Source keystore should have certificates", sourceKeyStore.size() > 0);

CommonOptions options = new CommonOptions();
options.force = true;
String password = "testPassword123";

// Create file first
Path trustStorePath = confDir.resolve("opensearch-fips-truststore.bcfks");
Files.createFile(trustStorePath);

assertTrue(Files.exists(trustStorePath));

// when
Path result = CreateFipsTrustStore.convertToBCFKS(spec, sourceKeyStore, options, password, confDir);

// then
assertNotNull(result);
assertTrue(Files.exists(result));

// Verify the converted keystore has actual certificates
KeyStore bcfksStore = KeyStore.getInstance("BCFKS", "BCFIPS");
try (var is = Files.newInputStream(result)) {
bcfksStore.load(is, password.toCharArray());
}
assertTrue("Converted keystore should have certificates", bcfksStore.size() > 0);
assertEquals("Converted keystore should have same number of certificates", sourceKeyStore.size(), bcfksStore.size());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.function.Consumer;

import picocli.CommandLine;
Expand All @@ -29,10 +28,10 @@ public class CreateFipsTrustStoreTests extends OpenSearchTestCase {
@ClassRule
public static TemporaryFolder tempFolder = new TemporaryFolder();

private static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
private static Path confDir;
protected static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
protected static Path confDir;

private CommandLine.Model.CommandSpec spec;
protected CommandLine.Model.CommandSpec spec;

@BeforeClass
@SuppressForbidden(reason = "the java.io.File is exposed by TemporaryFolder")
Expand Down Expand Up @@ -126,90 +125,4 @@ public void testConfigureBCFKSTrustStore() {
assertEquals("BCFIPS", config.trustStoreProvider());
}

public void testConvertToBCFKS() throws Exception {
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

// given
KeyStore sourceKeyStore = CreateFipsTrustStore.loadJvmDefaultTrustStore(spec, JAVA_HOME);
assertTrue("Source keystore should have certificates", sourceKeyStore.size() > 0);

CommonOptions options = new CommonOptions();
options.force = false;
String password = "testPassword123";

// when
Path result = CreateFipsTrustStore.convertToBCFKS(spec, sourceKeyStore, options, password, confDir);

// then
assertNotNull(result);
assertTrue(Files.exists(result));
assertTrue(result.toString().endsWith("opensearch-fips-truststore.bcfks"));

// Verify the converted keystore has the same certificates
KeyStore bcfksStore = KeyStore.getInstance("BCFKS", "BCFIPS");
try (var is = Files.newInputStream(result)) {
bcfksStore.load(is, password.toCharArray());
}
assertEquals("Converted keystore should have same number of certificates", sourceKeyStore.size(), bcfksStore.size());
}

public void testConvertToBCFKSFileExistsWithoutForce() throws Exception {
// Skip if BCFIPS not available since the method needs it to check file handling
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

// given
KeyStore sourceKeyStore = CreateFipsTrustStore.loadJvmDefaultTrustStore(spec, JAVA_HOME);
assertTrue("Source keystore should have certificates", sourceKeyStore.size() > 0);

CommonOptions options = new CommonOptions();
options.force = false;
String password = "testPassword123";

// Create file first to simulate existing truststore
Path trustStorePath = confDir.resolve("opensearch-fips-truststore.bcfks");
Files.createFile(trustStorePath);

assertTrue("Test setup: file should exist", Files.exists(trustStorePath));

// when/then
RuntimeException exception = expectThrows(
RuntimeException.class,
() -> CreateFipsTrustStore.convertToBCFKS(spec, sourceKeyStore, options, password, confDir)
);
assertEquals("Operation cancelled. Trust store file already exists.", exception.getMessage());
}

public void testConvertToBCFKSFileExistsWithForce() throws Exception {
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

// given
KeyStore sourceKeyStore = CreateFipsTrustStore.loadJvmDefaultTrustStore(spec, JAVA_HOME);
assertTrue("Source keystore should have certificates", sourceKeyStore.size() > 0);

CommonOptions options = new CommonOptions();
options.force = true;
String password = "testPassword123";

// Create file first
Path trustStorePath = confDir.resolve("opensearch-fips-truststore.bcfks");
Files.createFile(trustStorePath);

assertTrue(Files.exists(trustStorePath));

// when
Path result = CreateFipsTrustStore.convertToBCFKS(spec, sourceKeyStore, options, password, confDir);

// then
assertNotNull(result);
assertTrue(Files.exists(result));

// Verify the converted keystore has actual certificates
KeyStore bcfksStore = KeyStore.getInstance("BCFKS", "BCFIPS");
try (var is = Files.newInputStream(result)) {
bcfksStore.load(is, password.toCharArray());
}
assertTrue("Converted keystore should have certificates", bcfksStore.size() > 0);
assertEquals("Converted keystore should have same number of certificates", sourceKeyStore.size(), bcfksStore.size());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@

import java.util.concurrent.Callable;

public class FipsTrustStoreCommandTests extends FipsTrustStoreCommandTestCase {
public class FipsTrustStoreCommandFipsTests extends FipsTrustStoreCommandTestCase {

public void testWithUnmatchedArgs() throws Exception {
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

var exitCode = commandLine.execute("--non-interactive", "--force", "-Ediscovery.type=single-node", "-Ehttp.port=9200");

assertEquals(0, exitCode);
Expand All @@ -27,8 +25,6 @@ public void testWithUnmatchedArgs() throws Exception {
}

public void testWithEmptyUnmatchedArgs() throws Exception {
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

var exitCode = commandLine.execute("--non-interactive", "--force");

assertEquals(0, exitCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@

import java.util.concurrent.Callable;

public class GeneratedTrustStoreCommandTests extends FipsTrustStoreCommandTestCase {
public class GeneratedTrustStoreCommandFipsTests extends FipsTrustStoreCommandTestCase {

public void testNonInteractiveModeAutoGeneratesPassword() throws Exception {
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

int exitCode = commandLine.execute("--non-interactive", "--force");

assertEquals(0, exitCode);
Expand All @@ -25,8 +23,6 @@ public void testNonInteractiveModeAutoGeneratesPassword() throws Exception {
}

public void testNonInteractiveModeWithPasswordOption() throws Exception {
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

int exitCode = commandLine.execute("--non-interactive", "--force", "--password", "MyPassword");

assertEquals(0, exitCode);
Expand All @@ -37,8 +33,6 @@ public void testNonInteractiveModeWithPasswordOption() throws Exception {
}

public void testCommandWithEmptyPassword() throws Exception {
assumeTrue("Should only run when BCFIPS provider is installed.", inFipsJvm());

int exitCode = commandLine.execute("--non-interactive", "--force", "--password", "");

assertEquals(0, exitCode);
Expand Down
Loading
Loading