Adding FIPS Variants
Overview
FIPS variants provide container images that use only
FIPS 140-3 validated cryptographic modules. FIPS is implemented as a
cross-cutting modifier variant (like builder), not a separate image — the
FIPS variant uses the same base image structure but layers FIPS configuration
on top via additional RPM packages.
FIPS variants are restricted to the Hummingbird distro only, because FIPS-validated packages are not available in Fedora Rawhide.
FIPS images must not contain any non-validated cryptographic libraries. The
presence of an unvalidated crypto library (such as libgcrypt or gnutls)
would undermine the FIPS compliance guarantee, even if the application itself
does not use that library. Global tests enforce this constraint.
FIPS Validation Scope
The cryptographic modules shipped in FIPS images are FIPS 140-3 validated through the NIST Cryptographic Module Validation Program (CMVP). This validation applies only when running on RHEL systems installed in FIPS mode. Running FIPS images outside this environment (e.g., on Fedora, on RHEL not in FIPS mode, or on other Linux distributions) is outside the validated configuration.
As a best-effort goal, FIPS images aim to behave similarly on both FIPS and non-FIPS hosts — restricting cryptographic operations to FIPS-approved algorithms regardless of host configuration. This is not a guarantee; some images (particularly NSS-based ones like OpenJDK) may behave differently depending on host FIPS mode.
Validated Cryptographic Modules
All validated modules in Hummingbird are pre-built binaries from RHEL 9.2, submitted to NIST for FIPS 140-3 validation:
| Module | Package | Verified by global test |
|---|---|---|
| OpenSSL FIPS provider | openssl-config-fips |
fips-provider-matches-ubi9 |
| NSS softokn | nss-softokn-fips |
fips-nss-modules-match-rhel92 |
| NSS freebl | nss-softokn-freebl-fips |
fips-nss-modules-match-rhel92 |
Global tests validate each module’s checksum against the known-good RHEL 9.2 validated binaries. Checksums are architecture-specific because the binaries differ per architecture.
All FIPS variants also install crypto-policies-config-fips, which sets the
system-wide crypto policy to FIPS.
Two FIPS Stacks
Two FIPS stacks exist depending on which crypto library the image’s software uses:
OpenSSL stack — crypto-policies-config-fips + openssl-config-fips.
The OpenSSL FIPS provider is enabled via a drop-in configuration file at
/etc/pki/tls/openssl.d/fips-provider-enable.cnf. Enforcement is
container-side: non-approved algorithms are rejected even on non-FIPS hosts.
Used by: Nginx, Node.js, Python, Ruby. Go images also include these packages
for system tools (e.g., git-core), but Go itself uses its own
native FIPS 140-3 module.
NSS stack — crypto-policies-config-fips + nss-softokn-fips +
nss-softokn-freebl-fips. NSS checks the host kernel’s FIPS mode
(/proc/sys/crypto/fips_enabled), so enforcement behavior differs between
FIPS and non-FIPS hosts. The primary Java security provider switches to
SunPKCS11-NSS-FIPS. Used by: OpenJDK.
Both stacks share crypto-policies-config-fips for the system crypto policy,
but differ in which cryptographic library provides the validated
implementation.
Adding a FIPS Variant
1. Determine the FIPS Stack
Identify which crypto library the image’s software uses:
- OpenSSL — most languages and servers (Python, Node.js, Ruby, Nginx, Go)
- NSS — Java/OpenJDK (uses NSS via PKCS#11)
This determines which FIPS packages to install.
2. Update properties.yml
Add fips to additional_variants with a Hummingbird distro restriction,
and add the FIPS packages to rpm_packages.fips.
OpenSSL-based image:
additional_variants:
- name: fips
distros: [hummingbird]
rpm_packages:
fips:
- crypto-policies-config-fips
- openssl-config-fips
NSS-based image (OpenJDK):
OpenJDK has existing runtime and runtime-builder base variants. FIPS
is added as both fips (full JDK) and runtime-fips (headless JRE):
additional_variants:
- name: fips
distros: [hummingbird]
- name: runtime-fips
distros: [hummingbird]
rpm_packages:
fips:
- java-21-openjdk-devel # variant-specific packages
- crypto-policies-config-fips
- nss-softokn-fips
- nss-softokn-freebl-fips
runtime-fips:
- crypto-policies-config-fips
- nss-softokn-fips
- nss-softokn-freebl-fips
See the Image Configuration Reference for
complete properties.yml options, including
additional_variants
and rpm_packages.
3. Generate Files
Run make to generate the hummingbird/fips/ directory structure
(Containerfile, RPM lockfiles, VERSION, TAGS):
make
4. Write FIPS Tests
Add FIPS-specific tests to tests-container.yml using filters.variants to
target FIPS variants. A standard set of FIPS tests validates:
- Non-approved algorithm rejected — e.g., MD5 (for security use), Blowfish, RC2
- Approved algorithm works — e.g., SHA-256
- Non-approved cipher rejected — e.g., 3DES, CHACHA20-POLY1305
- Approved cipher works — e.g., AES-256-GCM (encrypt/decrypt roundtrip)
Example (Python):
fips-rejects-md5:
filters:
variants: ["*fips*"]
command: |
test_engine_run --rm "${TEST_IMAGE:?}" python3 -c "
import hashlib
try:
hashlib.new('md5')
print('MD5_ALLOWED')
except ValueError:
print('MD5_REJECTED')
" | grep -q MD5_REJECTED \
|| test_fail "FIPS mode should reject MD5"
fips-allows-sha256:
filters:
variants: ["*fips*"]
command: |
test_engine_run --rm "${TEST_IMAGE:?}" python3 -c "
import hashlib
print(hashlib.sha256(b'test').hexdigest())
" | grep -q 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 \
|| test_fail "FIPS mode should allow SHA-256"
Host FIPS mode considerations:
Where possible, write tests without filters.fips so they validate
behavior on both FIPS and non-FIPS hosts. This supports the best-effort goal
of consistent behavior across host types. Use filters.fips: true only for
tests that inherently require a FIPS-enabled host kernel — primarily
NSS-based images where enforcement depends on the host:
# NSS-based: rejection only works on FIPS hosts
fips-rejects-blowfish:
filters:
variants: ["*fips*"]
fips: true # requires FIPS-enabled host kernel
command: |
...
See the Test Configuration Reference for variant filters and FIPS mode selection.
5. Build and Test
# Build the FIPS variant
ci/build_images.sh <image>/hummingbird/fips
# Run tests
ci/run_tests_container.sh <image>/hummingbird/fips
# Run linters
make check
Go-Specific FIPS Requirements
Go 1.24+ includes a native FIPS 140-3 cryptographic module that has been
CMVP validated (CMVP Certificate #5247). Unlike the previous
golang-fips approach (which replaced the compiler with a fork using OpenSSL
as the crypto backend), native FIPS uses the same golang package with
build-time and runtime environment variables:
GOFIPS140=v1.0.0(build-time): Tellsgo buildto include the CMVP-validated FIPS module (v1.0.0) in the resulting binary. Set in the image environment so all builds in the container use it.GODEBUG=fips140=on(runtime): Enables FIPS 140-3 mode — uses NIST DRBG for randomness, negotiates only FIPS-approved TLS, and runs mandatory self-tests. Set in the image environment as the default for binaries run in the container.- Same package: The FIPS variant installs the standard
golang1.25package (noversion_packageoverride needed). - No cgo dependency: Native FIPS works with both
CGO_ENABLED=0andCGO_ENABLED=1binaries.
main_package: golang1.25
rpm_packages:
fips:
- crypto-policies-config-fips
- golang1.25
- openssl-config-fips
The openssl-config-fips package is retained for system tools in the image
(e.g., git-core) that use OpenSSL for TLS connections.
Go-specific FIPS tests verify:
crypto/fips140.Enabled()returnstrue(native FIPS module is active)go version -moutput includesbuild GOFIPS140=v1.0.0-c2097c7c(CMVP-validated module, per the security policy section 11.1)- FIPS works with
CGO_ENABLED=0static binaries (a key advantage over the oldgolang-fipsapproach)
OpenJDK-Specific FIPS Requirements
OpenJDK uses the NSS stack instead of OpenSSL, which has several implications:
- No
openssl-config-fips— usesnss-softokn-fips+nss-softokn-freebl-fipsinstead - Host FIPS mode dependency — NSS checks
/proc/sys/crypto/fips_enabled, so FIPS enforcement behavior differs between FIPS and non-FIPS hosts. Crypto rejection tests must usefilters.fips: true. - Compound variants — OpenJDK introduces
runtime-fipsalongsidefips, combining theruntimebase variant with the FIPS modifier. Both share the same NSS FIPS packages;fipsadditionally includesjava-*-openjdk-devel. - Security provider — in FIPS mode, the primary Java security provider
becomes
SunPKCS11-NSS-FIPSinstead of the defaultSUN/SunJCEproviders.
Global FIPS Tests
The following tests run automatically for all FIPS images via
ci/global-tests/tests-container.yml. Each test skips gracefully for images
using the other FIPS stack.
| Test | Validates | Applies to |
|---|---|---|
fips-provider-matches-ubi9 |
OpenSSL FIPS module checksum matches RHEL 9.2 binaries | OpenSSL stack |
fips-nss-modules-match-rhel92 |
NSS module checksums match RHEL 9.2 validated binaries | NSS stack |
fips-no-libgcrypt |
libgcrypt is not present (not a validated module) |
All FIPS |
openssl-fips-config-installed |
OpenSSL FIPS drop-in config exists | OpenSSL stack |
crypto-policy-is-fips |
System crypto policy is set to FIPS |
All FIPS |
Image-specific FIPS tests (algorithm rejection/acceptance) are defined in each
image’s tests-container.yml.
Testing on FIPS Hosts
The test runner detects host FIPS mode from /proc/sys/crypto/fips_enabled
and exports the TEST_FIPS environment variable (true or false). Tests
can filter on this using filters.fips:
# Runs only on FIPS-enabled hosts
fips-host-required:
filters:
fips: true
command: |
...
# Runs only on non-FIPS hosts
non-fips-only:
filters:
fips: false
command: |
...
# Runs regardless of host FIPS mode (default when filters.fips is omitted)
always-runs:
command: |
...
OpenSSL-based images: Most FIPS tests run regardless of host FIPS mode, because the OpenSSL FIPS provider enforces algorithm restrictions at the container level.
NSS-based images: Crypto rejection tests require filters.fips: true,
because NSS enforcement depends on the host kernel’s FIPS mode.
Next Steps
- Image Configuration Reference —
additional_variants,rpm_packages,version_package - Test Configuration Reference — variant filters, FIPS mode selection
- Testing Guide — how to run and write tests