diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java index 0d7b2993d0c5e..a92908fa4edfd 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java @@ -61,6 +61,7 @@ import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.Security; +import java.util.Enumeration; import java.util.Map; import static java.util.Collections.emptyMap; @@ -231,11 +232,9 @@ private KeyStore loadTrustStore(TruststoreSettings truststoreSettings) throws Ge } logger.debug("Loaded custom truststore from path: {} with type: {}", truststorePath, truststoreType); } else if (Security.getProvider("BCFIPS") != null) { - throw new IllegalStateException( - "FIPS mode is active but no custom truststore is configured. " - + "Please configure gcs.client..truststore.path and " - + "gcs.client..truststore.secure_password settings." - ); + // FIPS mode is active: create FIPS-compliant (BCFKS) truststore with Google default certs + certTrustStore = createFipsCompliantGoogleTrustStore(); + logger.debug("Using FIPS-compliant Google default certificate trust store"); } else { // requires java.lang.RuntimePermission "setFactory" // Pin the TLS trust certificates. @@ -302,4 +301,25 @@ static Integer toTimeout(final TimeValue timeout) { } return Math.toIntExact(timeout.getMillis()); } + + private KeyStore createFipsCompliantGoogleTrustStore() throws GeneralSecurityException, IOException { + // Use BCFKS KeyStore type which is required for FIPS mode + KeyStore trustStore = KeyStore.getInstance("BCFIPS", "BCFIPS"); + // Initialize empty BCFKS trust store + trustStore.load(null, null); + + // Load Google's built-in default trust store + KeyStore googleDefaultStore = GoogleUtils.getCertificateTrustStore(); + Enumeration aliases = googleDefaultStore.aliases(); + + // Copy all certificate entries into the FIPS-compliant BCFKS trust store + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + if (googleDefaultStore.isCertificateEntry(alias)) { + trustStore.setCertificateEntry(alias, googleDefaultStore.getCertificate(alias)); + } + } + + return trustStore; + } } diff --git a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceFipsTests.java b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceFipsTests.java index 5d24abe352eaf..251d66f0ebc2f 100644 --- a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceFipsTests.java +++ b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceFipsTests.java @@ -12,7 +12,6 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; -import java.io.IOException; import java.nio.file.Path; import static org.hamcrest.Matchers.containsString; @@ -68,17 +67,18 @@ public void testCustomTruststoreConfiguration() throws Exception { assertNotNull(storage); } - public void testFipsModeEnforcesTruststoreConfiguration() throws Exception { + public void testFipsModeTruststoreConfiguration() throws Exception { final var service = new GoogleCloudStorageService(); final var statsCollector = new GoogleCloudStorageOperationsStats("bucket"); - { // Scenario 1: No truststore configuration -> should throw exception + { // Scenario 1: No truststore configuration -> auto-create FIPS truststore final var secureSettings = new MockSecureSettings(); final var settings = Settings.builder().setSecureSettings(secureSettings).build(); service.refreshAndClearCache(GoogleCloudStorageClientSettings.load(settings)); - final var exception = expectThrows(IOException.class, () -> service.client("default", "repo", statsCollector)); - assertThat(exception.getMessage(), containsString("FIPS mode is active but no custom truststore is configured")); + // Should create client successfully without exception + final var storage = service.client("default", "repo", statsCollector); + assertNotNull(storage); } { // Scenario 2: Path + password configured (missing type) -> should throw exception @@ -105,5 +105,4 @@ public void testFipsModeEnforcesTruststoreConfiguration() throws Exception { assertNotNull("Storage client should be created successfully with full truststore configuration", storage); } } - }