Skip to content

Commit b2a4c99

Browse files
committed
Run ruby unit tests under FIPS mode
This commit shows a proposed pattern for running automated tests for logstash in FIPS mode. It uses a new identifier in gradle for conditionally setting properties to configure fips mode. The tests are run in a container representative of the base image the final artifacts will be built from.
1 parent 0cc5feb commit b2a4c99

File tree

6 files changed

+259
-0
lines changed

6 files changed

+259
-0
lines changed

.buildkite/pull_request_pipeline.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,25 @@ steps:
3737
artifact_paths:
3838
- "coverage/coverage.json"
3939

40+
- label: ":rspec: Ruby unit tests - FIPS mode"
41+
key: "ruby-unit-tests-fips"
42+
agents:
43+
provider: gcp
44+
imageProject: elastic-images-prod
45+
image: family/platform-ingest-logstash-ubuntu-2204
46+
machineType: "n2-standard-4"
47+
diskSizeGb: 64
48+
retry:
49+
automatic:
50+
- limit: 3
51+
command: |
52+
set -euo pipefail
53+
54+
docker build -t test-runner-image -f qa/fips/docker/Dockerfile .
55+
docker run test-runner-image ./gradlew --info --stacktrace -PrunTestsInFIPSMode=true rubyTests
56+
artifact_paths:
57+
- "coverage/coverage.json"
58+
4059
- label: ":java: Java unit tests"
4160
key: "java-unit-tests"
4261
agents:

build.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ import org.logstash.gradle.tooling.SignAliasDefinitions
5151
import org.logstash.gradle.tooling.ToolingUtils
5252
import org.logstash.gradle.tooling.SnapshotArtifactURLs
5353

54+
ext {
55+
runTestsInFIPSMode = project.hasProperty('runTestsInFIPSMode') ? project.property('runTestsInFIPSMode').toBoolean() : false
56+
}
57+
58+
subprojects {
59+
ext {
60+
runTestsInFIPSMode = rootProject.runTestsInFIPSMode
61+
}
62+
}
63+
5464
allprojects {
5565
group = 'org.logstash'
5666

logstash-core/build.gradle

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,21 @@ tasks.register("rubyTests", Test) {
156156
include '/org/logstash/plugins/CounterMetricImplTest.class'
157157
include '/org/logstash/plugins/factory/PluginFactoryExtTest.class'
158158
include '/org/logstash/execution/ObservedExecutionTest.class'
159+
160+
if (runTestsInFIPSMode) {
161+
systemProperty "java.security.properties", System.getenv("JAVA_SECURITY_PROPERTIES")
162+
systemProperty "javax.net.ssl.keyStore", "/etc/java/security/keystore.bcfks"
163+
systemProperty "javax.net.ssl.keyStoreType", "BCFKS"
164+
systemProperty "javax.net.ssl.keyStoreProvider", "BCFIPS"
165+
systemProperty "javax.net.ssl.keyStorePassword", "changeit"
166+
systemProperty "javax.net.ssl.trustStore", "/etc/java/security/cacerts.bcfks"
167+
systemProperty "javax.net.ssl.trustStoreType", "BCFKS"
168+
systemProperty "javax.net.ssl.trustStoreProvider", "BCFIPS"
169+
systemProperty "javax.net.ssl.trustStorePassword", "changeit"
170+
systemProperty "ssl.KeyManagerFactory.algorithm", "PKIX"
171+
systemProperty "ssl.TrustManagerFactory.algorithm", "PKIX"
172+
systemProperty "org.bouncycastle.fips.approved_only", "true"
173+
}
159174
}
160175

161176
test {
@@ -235,6 +250,11 @@ dependencies {
235250
runtimeOnly 'commons-logging:commons-logging:1.3.1'
236251
// also handle libraries relying on log4j 1.x to redirect their logs
237252
runtimeOnly "org.apache.logging.log4j:log4j-1.2-api:${log4jVersion}"
253+
// FIPS deps. TODO: figure out how to actually manage these
254+
runtimeOnly("org.bouncycastle:bc-fips:2.0.0")
255+
runtimeOnly("org.bouncycastle:bcpkix-fips:2.0.7")
256+
runtimeOnly("org.bouncycastle:bctls-fips:2.0.19")
257+
runtimeOnly("org.bouncycastle:bcutil-fips:2.0.3")
238258
implementation('org.reflections:reflections:0.10.2') {
239259
exclude group: 'com.google.guava', module: 'guava'
240260
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
grant {
2+
// Your existing permissions
3+
permission java.lang.PropertyPermission "java.runtime.name", "read";
4+
permission java.lang.RuntimePermission "accessClassInPackage.sun.security.internal.spec";
5+
permission java.lang.RuntimePermission "getProtectionDomain";
6+
permission java.lang.RuntimePermission "accessDeclaredMembers";
7+
permission org.bouncycastle.crypto.CryptoServicesPermission "tlsAlgorithmsEnabled";
8+
permission org.bouncycastle.crypto.CryptoServicesPermission "exportKeys";
9+
10+
// Add provider permissions
11+
permission java.security.SecurityPermission "putProviderProperty.BCFIPS";
12+
permission java.security.SecurityPermission "insertProvider.BCFIPS";
13+
permission java.security.SecurityPermission "putProviderProperty.BCJSSE";
14+
permission java.security.SecurityPermission "insertProvider.BCJSSE";
15+
};
16+
17+
deny {
18+
permission java.security.SecurityPermission "putProviderProperty.BC";
19+
permission java.security.SecurityPermission "insertProvider.BC";
20+
permission java.security.SecurityPermission "removeProvider.BC";
21+
};
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
2+
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
3+
security.provider.3=SUN
4+
security.provider.11=-BC
5+
6+
securerandom.source=file:/dev/random
7+
securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
8+
securerandom.drbg.config=
9+
10+
login.configuration.provider=sun.security.provider.ConfigFile
11+
12+
policy.provider=sun.security.provider.PolicyFile
13+
policy.url.1=file:/etc/java/security/java.policy
14+
policy.expandProperties=true
15+
policy.allowSystemProperty=true
16+
policy.ignoreIdentityScope=false
17+
18+
keystore.type=bcfks
19+
keystore.type.compat=true
20+
21+
package.access=sun.misc.,\
22+
sun.reflect.
23+
package.definition=sun.misc.,\
24+
sun.reflect.
25+
26+
security.overridePropertiesFile=true
27+
28+
ssl.KeyManagerFactory.algorithm=PKIX
29+
ssl.TrustManagerFactory.algorithm=PKIX
30+
31+
networkaddress.cache.negative.ttl=10
32+
33+
krb5.kdc.bad.policy = tryLast
34+
35+
sun.security.krb5.disableReferrals=false
36+
sun.security.krb5.maxReferrals=5
37+
38+
jdk.disabled.namedCurves = secp112r1, secp112r2, secp128r1, secp128r2, \
39+
secp160k1, secp160r1, secp160r2, secp192k1, secp192r1, secp224k1, \
40+
secp224r1, secp256k1, sect113r1, sect113r2, sect131r1, sect131r2, \
41+
sect163k1, sect163r1, sect163r2, sect193r1, sect193r2, sect233k1, \
42+
sect233r1, sect239k1, sect283k1, sect283r1, sect409k1, sect409r1, \
43+
sect571k1, sect571r1, X9.62 c2tnb191v1, X9.62 c2tnb191v2, \
44+
X9.62 c2tnb191v3, X9.62 c2tnb239v1, X9.62 c2tnb239v2, X9.62 c2tnb239v3, \
45+
X9.62 c2tnb359v1, X9.62 c2tnb431r1, X9.62 prime192v2, X9.62 prime192v3, \
46+
X9.62 prime239v1, X9.62 prime239v2, X9.62 prime239v3, brainpoolP256r1, \
47+
brainpoolP320r1, brainpoolP384r1, brainpoolP512r1
48+
49+
jdk.certpath.disabledAlgorithms=MD2, MD5, \
50+
RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224, \
51+
SHA1, \
52+
secp112r1, secp112r2, secp128r1, secp128r2, \
53+
secp160k1, secp160r1, secp160r2, secp192k1, secp192r1, secp224k1, \
54+
secp224r1, secp256k1, sect113r1, sect113r2, sect131r1, sect131r2, \
55+
sect163k1, sect163r1, sect163r2, sect193r1, sect193r2, sect233k1, \
56+
sect233r1, sect239k1, sect283k1, sect283r1, sect409k1, sect409r1, \
57+
sect571k1, sect571r1, \
58+
brainpoolP256r1, brainpoolP320r1, brainpoolP384r1, brainpoolP512r1
59+
60+
jdk.security.legacyAlgorithms=SHA1, \
61+
RSA keySize < 2048, DSA keySize < 2048
62+
63+
jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \
64+
DSA keySize < 1024, SHA1, \
65+
secp112r1, secp112r2, secp128r1, secp128r2, \
66+
secp160k1, secp160r1, secp160r2, secp192k1, secp192r1, secp224k1, \
67+
secp224r1, secp256k1, sect113r1, sect113r2, sect131r1, sect131r2, \
68+
sect163k1, sect163r1, sect163r2, sect193r1, sect193r2, sect233k1, \
69+
sect233r1, sect239k1, sect283k1, sect283r1, sect409k1, sect409r1, \
70+
sect571k1, sect571r1, X9.62 c2tnb191v1, X9.62 c2tnb191v2, \
71+
X9.62 c2tnb191v3, X9.62 c2tnb239v1, X9.62 c2tnb239v2, X9.62 c2tnb239v3, \
72+
X9.62 c2tnb359v1, X9.62 c2tnb431r1, X9.62 prime192v2, X9.62 prime192v3, \
73+
X9.62 prime239v1, X9.62 prime239v2, X9.62 prime239v3, brainpoolP256r1, \
74+
brainpoolP320r1, brainpoolP384r1, brainpoolP512r1
75+
76+
jdk.tls.disabledAlgorithms=MD5, SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
77+
DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
78+
secp112r1, secp112r2, secp128r1, secp128r2, \
79+
secp160k1, secp160r1, secp160r2, secp192k1, secp192r1, secp224k1, \
80+
secp224r1, secp256k1, sect113r1, sect113r2, sect131r1, sect131r2, \
81+
sect163k1, sect163r1, sect163r2, sect193r1, sect193r2, sect233k1, \
82+
sect233r1, sect239k1, sect283k1, sect283r1, sect409k1, sect409r1, \
83+
sect571k1, sect571r1, brainpoolP256r1, \
84+
brainpoolP320r1, brainpoolP384r1, brainpoolP512r1
85+
jdk.tls.legacyAlgorithms= \
86+
K_NULL, C_NULL, M_NULL, \
87+
DH_anon, ECDH_anon, \
88+
RC4_128, RC4_40, DES_CBC, DES40_CBC, \
89+
3DES_EDE_CBC
90+
jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37, \
91+
ChaCha20-Poly1305 KeyUpdate 2^37
92+
93+
crypto.policy=unlimited
94+
95+
jdk.xml.dsig.secureValidationPolicy=\
96+
disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\
97+
disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\
98+
disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\
99+
disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\
100+
maxTransforms 5,\
101+
maxReferences 30,\
102+
disallowReferenceUriSchemes file http https,\
103+
minKeySize RSA 1024,\
104+
minKeySize DSA 1024,\
105+
minKeySize EC 224,\
106+
noDuplicateIds,\
107+
noRetrievalMethodLoops
108+
109+
jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\
110+
java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!*
111+
112+
jdk.sasl.disabledMechanisms=CRAM-MD5, DIGEST-MD5
113+
jdk.security.caDistrustPolicies=SYMANTEC_TLS
114+
jdk.io.permissionsUseCanonicalPath=false
115+
116+
jdk.tls.alpnCharset=ISO_8859_1
117+
118+
org.bouncycastle.fips.approved_only=true

qa/fips/docker/Dockerfile

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Start from the FIPS-compliant base image
2+
FROM docker.elastic.co/wolfi/chainguard-base-fips:latest
3+
4+
# Install OpenJDK 21
5+
RUN apk add --no-cache \
6+
openjdk-21 \
7+
bash
8+
9+
# Create directory for security configuration
10+
RUN mkdir -p /etc/java/security
11+
RUN mkdir -p /root/.gradle
12+
13+
# Copy configuration files
14+
COPY qa/fips/config/security/java.security /etc/java/security/
15+
COPY qa/fips/config/security/java.policy /etc/java/security/
16+
17+
# Set environment variables
18+
ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk
19+
ENV PATH="${JAVA_HOME}/bin:${PATH}"
20+
21+
# Create working directory
22+
WORKDIR /logstash
23+
24+
# Copy the local Logstash source
25+
COPY . .
26+
27+
# Initial build using JKS truststore
28+
RUN ./gradlew clean bootstrap assemble installDefaultGems
29+
30+
# Convert JKS to BCFKS for truststore and keystore
31+
RUN keytool -importkeystore \
32+
-srckeystore $JAVA_HOME/lib/security/cacerts \
33+
-destkeystore /etc/java/security/cacerts.bcfks \
34+
-srcstoretype jks \
35+
-deststoretype bcfks \
36+
-providerpath /logstash/logstash-core/lib/jars/bc-fips-2.0.0.jar \
37+
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
38+
-deststorepass changeit \
39+
-srcstorepass changeit \
40+
-noprompt
41+
42+
RUN keytool -importkeystore \
43+
-srckeystore $JAVA_HOME/lib/security/cacerts \
44+
-destkeystore /etc/java/security/keystore.bcfks \
45+
-srcstoretype jks \
46+
-deststoretype bcfks \
47+
-providerpath /logstash/logstash-core/lib/jars/bc-fips-2.0.0.jar \
48+
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
49+
-deststorepass changeit \
50+
-srcstorepass changeit \
51+
-noprompt
52+
53+
ENV JAVA_SECURITY_PROPERTIES=/etc/java/security/java.security
54+
ENV LS_JAVA_OPTS="\
55+
-Dio.netty.ssl.provider=JDK \
56+
-Djava.security.debug=ssl,provider,certpath \
57+
-Djava.security.properties=${JAVA_SECURITY_PROPERTIES} \
58+
-Djavax.net.ssl.keyStore=/etc/java/security/keystore.bcfks \
59+
-Djavax.net.ssl.keyStoreType=BCFKS \
60+
-Djavax.net.ssl.keyStoreProvider=BCFIPS \
61+
-Djavax.net.ssl.keyStorePassword=changeit \
62+
-Djavax.net.ssl.trustStore=/etc/java/security/cacerts.bcfks \
63+
-Djavax.net.ssl.trustStoreType=BCFKS \
64+
-Djavax.net.ssl.trustStoreProvider=BCFIPS \
65+
-Djavax.net.ssl.trustStorePassword=changeit \
66+
-Dssl.KeyManagerFactory.algorithm=PKIX \
67+
-Dssl.TrustManagerFactory.algorithm=PKIX \
68+
-Dorg.bouncycastle.fips.approved_only=true"
69+
70+
# Example test run, most use cases will override this
71+
CMD ["./gradlew", "--info", "--stacktrace", "-PrunTestsInFIPSMode=true", "runIntegrationTests"]

0 commit comments

Comments
 (0)