Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
49 changes: 38 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@


import com.diffplug.gradle.spotless.JavaExtension
import org.opensearch.gradle.info.FipsBuildParams
import org.opensearch.gradle.test.RestIntegTestTask
import groovy.json.JsonBuilder

Expand Down Expand Up @@ -241,6 +240,21 @@ task listTasksAsJSON {
}
}

def configureFipsJvmArgs(Test task) {
if (project.findProperty('tests.fips.only') == 'true') {
def fipsSecurityFile = project.rootProject.file('config/fips_java.security')
task.jvmArgs += "-Djava.security.properties==${fipsSecurityFile}"
task.jvmArgs += "-Dorg.bouncycastle.fips.approved_only=true"
task.jvmArgs += "-Djavax.net.ssl.trustStore=/home/iigonin/install/opensearch-3.4.0-SNAPSHOT/config/opensearch-fips-truststore.bcfks"
task.jvmArgs += "-Djavax.net.ssl.trustStoreProvider=BCFIPS"
task.jvmArgs += "-Djavax.net.ssl.trustStoreType=BCFKS"
task.jvmArgs += "-Djavax.net.ssl.trustStorePassword=dtekVF0vEAA9FNvm#KMkTwMN"
} else {
def nonFipsSecurityFile = project.rootProject.file('config/java_test.security')
task.jvmArgs += "-Djava.security.properties==${nonFipsSecurityFile}"
}
}

tasks.register('copyExtraTestResources', Copy.class) {
dependsOn testClasses

Expand Down Expand Up @@ -270,6 +284,7 @@ def setCommonTestConfig(Test task) {
// this is needed to reflect access system env map.
task.jvmArgs += "--add-opens=java.base/java.io=ALL-UNNAMED"
task.jvmArgs += "--add-opens=java.base/java.util=ALL-UNNAMED"
configureFipsJvmArgs(task)
task.retry {
failOnPassedAfterRetry = false
maxRetries = 5
Expand Down Expand Up @@ -303,6 +318,7 @@ test {
// this is needed to reflect access system env map.
jvmArgs += "--add-opens=java.base/java.io=ALL-UNNAMED"
jvmArgs += "--add-opens=java.base/java.util=ALL-UNNAMED"
configureFipsJvmArgs(it)
retry {
failOnPassedAfterRetry = false
maxRetries = 5
Expand Down Expand Up @@ -580,6 +596,7 @@ allprojects {
integrationTestImplementation "org.apache.logging.log4j:log4j-jul:${versions.log4j}"
integrationTestImplementation 'org.hamcrest:hamcrest:2.2'
integrationTestImplementation "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
integrationTestImplementation "org.bouncycastle:bctls-fips:${versions.bouncycastle_tls}"
integrationTestImplementation "org.bouncycastle:bcpkix-fips:${versions.bouncycastle_pkix}"
integrationTestImplementation "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
integrationTestImplementation('org.awaitility:awaitility:4.3.0') {
Expand Down Expand Up @@ -642,6 +659,7 @@ task integrationTest(type: Test) {
systemProperty "java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
configureFipsJvmArgs(it)
//run the integrationTest task after the test task
shouldRunAfter test
jacoco {
Expand Down Expand Up @@ -683,16 +701,10 @@ dependencies {
implementation "com.google.guava:guava:${guava_version}"
implementation 'org.greenrobot:eventbus-java:3.3.1'
implementation 'commons-cli:commons-cli:1.11.0'
// When building with crypto.standard=FIPS-140-3 (set in gradle.properties), bcFips jars are provided by OpenSearch
if (FipsBuildParams.isInFipsMode()) {
compileOnly "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
compileOnly "org.bouncycastle:bcpkix-fips:${versions.bouncycastle_pkix}"
compileOnly "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
} else {
implementation "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
implementation "org.bouncycastle:bcpkix-fips:${versions.bouncycastle_pkix}"
implementation "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
}
compileOnly "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
compileOnly "org.bouncycastle:bctls-fips:${versions.bouncycastle_tls}"
compileOnly "org.bouncycastle:bcpkix-fips:${versions.bouncycastle_pkix}"
compileOnly "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
implementation 'org.ldaptive:ldaptive:1.2.3'
implementation 'com.nimbusds:nimbus-jose-jwt:10.9'
implementation 'com.rfksystems:blake2b:2.0.0'
Expand Down Expand Up @@ -803,6 +815,7 @@ dependencies {
exclude(group: 'org.hamcrest', module: 'hamcrest')
}
testImplementation "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
testImplementation "org.bouncycastle:bctls-fips:${versions.bouncycastle_tls}"
testImplementation "org.bouncycastle:bcpkix-fips:${versions.bouncycastle_pkix}"
testImplementation "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
// JUnit build requirement
Expand Down Expand Up @@ -842,6 +855,18 @@ tasks.register('testsJar', Jar) {
from(sourceSets.test.output)
}

def configureSecurityAdminBcFips(AbstractArchiveTask task) {
def bcFipsJars = configurations.detachedConfiguration(
dependencies.create("org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"),
dependencies.create("org.bouncycastle:bctls-fips:${versions.bouncycastle_tls}"),
dependencies.create("org.bouncycastle:bcpkix-fips:${versions.bouncycastle_pkix}"),
dependencies.create("org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}")
)
task.from(bcFipsJars) {
into 'deps/'
}
}

task bundleSecurityAdminStandalone(dependsOn: jar, type: Zip) {
archiveClassifier = 'securityadmin-standalone'
from(configurations.runtimeClasspath) {
Expand All @@ -857,6 +882,7 @@ task bundleSecurityAdminStandalone(dependsOn: jar, type: Zip) {
into 'deps/securityconfig'
}
}
configureSecurityAdminBcFips(bundleSecurityAdminStandalone)

task bundleSecurityAdminStandaloneTarGz(dependsOn: jar, type: Tar) {
archiveClassifier = 'securityadmin-standalone'
Expand All @@ -875,6 +901,7 @@ task bundleSecurityAdminStandaloneTarGz(dependsOn: jar, type: Tar) {
into 'deps/securityconfig'
}
}
configureSecurityAdminBcFips(bundleSecurityAdminStandaloneTarGz)

buildRpm {
arch = 'NOARCH'
Expand Down
58 changes: 58 additions & 0 deletions config/fips_java.security
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Security properties for FIPS test runs.
# Used with == (complete override): only the providers listed here are available.
# SunJCE is intentionally absent — this removes DES, MD5-based PBE, and other
# non-FIPS algorithms without requiring programmatic Security.removeProvider() calls.

security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider C:HYBRID;ENABLE{All};
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
security.provider.3=SunPKCS11
security.provider.4=SUN
security.provider.5=SunJGSS
security.provider.6=JdkLDAP

login.configuration.provider=sun.security.provider.ConfigFile

policy.expandProperties=true
policy.allowSystemProperty=true

keystore.type=BCFKS
keystore.type.compat=true

ssl.KeyManagerFactory.algorithm=PKIX
ssl.TrustManagerFactory.algorithm=PKIX

jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \
RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224, \
SHA1 usage SignedJAR & denyAfter 2019-01-01

jdk.security.legacyAlgorithms=SHA1, \
RSA keySize < 2048, DSA keySize < 2048, \
DES, DESede, MD5, RC2, ARCFOUR

jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \
DSA keySize < 1024, SHA1 denyAfter 2019-01-01

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \
MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
ECDH, TLS_RSA_*

jdk.tls.legacyAlgorithms=NULL, anon, RC4, DES, 3DES_EDE_CBC

jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37, \
ChaCha20-Poly1305 KeyUpdate 2^37

crypto.policy=unlimited

jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS

jdk.tls.alpnCharset=ISO_8859_1

# Revocation via BCTLS TrustManager (covers all TLS including LDAPS)
com.sun.net.ssl.checkRevocation=true

# BC FIPS CertPath revocation mechanisms
ocsp.enable=true
org.bouncycastle.x509.enableCRLDP=true

# OCSP stapling — request stapled response from server
jdk.tls.client.enableStatusRequestExtension=true
55 changes: 55 additions & 0 deletions config/java_test.security
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Security properties for non-FIPS test runs.
# Used with == (complete override) so all desired providers must be listed explicitly.
# Standard JDK providers are preserved in their default order; BCFIPS is appended last
# so it is available for tests that rely on BC-specific features without displacing JDK providers.

security.provider.1=SUN
security.provider.2=SunRsaSign
security.provider.3=SunEC
security.provider.4=SunJSSE
security.provider.5=SunJCE
security.provider.6=SunJGSS
security.provider.7=SunSASL
security.provider.8=XMLDSig
security.provider.9=SunPCSC
security.provider.10=JdkLDAP
security.provider.11=JdkSASL
security.provider.12=SunPKCS11
security.provider.13=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider C:HYBRID;ENABLE{All};

login.configuration.provider=sun.security.provider.ConfigFile

policy.expandProperties=true
policy.allowSystemProperty=true

keystore.type=pkcs12
keystore.type.compat=true

ssl.KeyManagerFactory.algorithm=SunX509
ssl.TrustManagerFactory.algorithm=PKIX

jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \
RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224, \
SHA1 usage SignedJAR & denyAfter 2019-01-01

jdk.security.legacyAlgorithms=SHA1, \
RSA keySize < 2048, DSA keySize < 2048, \
DES, DESede, MD5, RC2, ARCFOUR

jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \
DSA keySize < 1024, SHA1 denyAfter 2019-01-01

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \
MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
ECDH, TLS_RSA_*

jdk.tls.legacyAlgorithms=NULL, anon, RC4, DES, 3DES_EDE_CBC

jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37, \
ChaCha20-Poly1305 KeyUpdate 2^37

crypto.policy=unlimited

jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS

jdk.tls.alpnCharset=ISO_8859_1
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
crypto.standard=FIPS-140-3
tests.fips.only=true
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.junit.Rule;
import org.junit.Test;
import org.bouncycastle.crypto.CryptoServicesRegistrar;

import org.opensearch.test.framework.certificate.TestCertificates;
import org.opensearch.test.framework.cluster.ClusterManager;
Expand Down Expand Up @@ -71,7 +72,11 @@ public void clusterShouldNotStart_nodesSanIpsAreInvalid() {
);
clusterFuture.cancel(true);
} catch (Exception e) {
logsRule.assertThatContain("No subject alternative names matching IP address 127.0.0.1 found");
if (CryptoServicesRegistrar.isInApprovedOnlyMode()) {
logsRule.assertThatStackTraceContain("No subject alternative name found matching IP address 127.0.0.1");
} else {
logsRule.assertThatContain("No subject alternative names matching IP address 127.0.0.1 found");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.bouncycastle.crypto.CryptoServicesRegistrar;

import org.opensearch.security.auditlog.impl.AuditCategory;
import org.opensearch.test.framework.AuditCompliance;
Expand All @@ -47,7 +48,7 @@ public class TlsTests {

private static final User USER_ADMIN = new User("admin").roles(ALL_ACCESS);

public static final String SUPPORTED_CIPHER_SUIT = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
public static final String SUPPORTED_CIPHER_SUIT = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384";
public static final String NOT_SUPPORTED_CIPHER_SUITE = "TLS_RSA_WITH_AES_128_CBC_SHA";
public static final String AUTH_INFO_ENDPOINT = "/_opendistro/_security/authinfo?pretty";

Expand Down Expand Up @@ -95,7 +96,11 @@ public void shouldSupportClientCipherSuite_negative() throws IOException {
try (CloseableHttpClient client = cluster.getClosableHttpClient(new String[] { NOT_SUPPORTED_CIPHER_SUITE })) {
HttpGet httpGet = new HttpGet("https://localhost:" + cluster.getHttpPort() + AUTH_INFO_ENDPOINT);

assertThatThrownBy(() -> client.execute(httpGet), instanceOf(SSLHandshakeException.class));
if (CryptoServicesRegistrar.isInApprovedOnlyMode()) {
assertThatThrownBy(() -> client.execute(httpGet), instanceOf(IllegalStateException.class));
} else {
assertThatThrownBy(() -> client.execute(httpGet), instanceOf(SSLHandshakeException.class));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

public class HashingTests {

private static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS);
protected static TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS);

static final String PASSWORD = "top$ecret1234!";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.bouncycastle.crypto.CryptoServicesRegistrar;

import org.opensearch.security.support.ConfigConstants;
import org.opensearch.test.framework.TestSecurityConfig;
Expand Down Expand Up @@ -65,9 +66,9 @@ public static Collection<Object[]> data() {

@Before
public void startCluster() {

var password = CryptoServicesRegistrar.isInApprovedOnlyMode() ? "dtekVF0vEAA9FNvm#KMkTwMN" : "secret";
TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS)
.hash(generatePBKDF2Hash("secret", function, iterations, length));
.hash(generatePBKDF2Hash(password, function, iterations, length));
cluster = new LocalCluster.Builder().clusterManager(ClusterManager.SINGLENODE)
.authc(AUTHC_HTTPBASIC_INTERNAL)
.users(ADMIN_USER)
Expand All @@ -89,7 +90,7 @@ public void startCluster() {
.build();
cluster.before();

try (TestRestClient client = cluster.getRestClient(ADMIN_USER.getName(), "secret")) {
try (TestRestClient client = cluster.getRestClient(ADMIN_USER.getName(), password)) {
Awaitility.await()
.alias("Load default configuration")
.until(() -> client.securityHealth().getTextFromJsonBody("/status"), equalTo("UP"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.apache.http.HttpStatus;
import org.junit.ClassRule;
import org.junit.Test;
import org.bouncycastle.crypto.CryptoServicesRegistrar;

import org.opensearch.security.support.ConfigConstants;
import org.opensearch.test.framework.TestSecurityConfig;
Expand All @@ -28,15 +29,20 @@

public class PBKDF2DefaultConfigHashingTests extends HashingTests {

private static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS)
.hash(
generatePBKDF2Hash(
"secret",
ConfigConstants.SECURITY_PASSWORD_HASHING_PBKDF2_FUNCTION_DEFAULT,
ConfigConstants.SECURITY_PASSWORD_HASHING_PBKDF2_ITERATIONS_DEFAULT,
ConfigConstants.SECURITY_PASSWORD_HASHING_PBKDF2_LENGTH_DEFAULT
private static final String password = CryptoServicesRegistrar.isInApprovedOnlyMode() ? "dtekVF0vEAA9FNvm#KMkTwMN" : "secret";

static {
ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This modifies an attribute that was formely static final but was changed to just static in this PR.

I think this needs to be done in a different way, this is very prone to concurrency issues; and it is modifying static members of other classes is very surprising behavior which is usually not expected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - this is just a dirty hack to make it work. Thanks for pointing it out, it should be reworked before moving out of WIP

.hash(
generatePBKDF2Hash(
password,
ConfigConstants.SECURITY_PASSWORD_HASHING_PBKDF2_FUNCTION_DEFAULT,
ConfigConstants.SECURITY_PASSWORD_HASHING_PBKDF2_ITERATIONS_DEFAULT,
ConfigConstants.SECURITY_PASSWORD_HASHING_PBKDF2_LENGTH_DEFAULT
)
)
);
.password(password);
}

@ClassRule
public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.SINGLENODE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.bouncycastle.crypto.CryptoServicesRegistrar;

import org.opensearch.test.framework.LdapAuthenticationConfigBuilder;
import org.opensearch.test.framework.TestSecurityConfig;
Expand Down Expand Up @@ -94,7 +95,11 @@ public void shouldNotAuthenticateUserWithLdap() {

response.assertStatusCode(401);
}
logsRule.assertThatStackTraceContain("javax.net.ssl.SSLHandshakeException");
if (CryptoServicesRegistrar.isInApprovedOnlyMode()) {
logsRule.assertThatStackTraceContain("org.bouncycastle.tls.TlsFatalAlert");
} else {
logsRule.assertThatStackTraceContain("javax.net.ssl.SSLHandshakeException");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private static PemObject createPkcs8PrivateKeyPem(PrivateKey privateKey, String

private static OutputEncryptor getPasswordEncryptor(String password) throws OperatorCreationException {
if (!Strings.isNullOrEmpty(password)) {
JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.PBE_SHA1_3DES);
JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.AES_256_CBC);
encryptorBuilder.setRandom(secureRandom);
encryptorBuilder.setPassword(password.toCharArray());
return encryptorBuilder.build();
Expand Down
Loading
Loading