0021 - FIPS build support behind a cargo feature
- Status: Accepted
- Date: 2026-07-14
- Deciders: nexus core team
- Tags: crypto, fips, build, images
Context and Problem Statement
Section titled “Context and Problem Statement”Deployments in regulated environments require the cryptographic operations of every Nexus service to run in a FIPS 140-3 validated module, and require image bases that can be evaluated against DISA hardening baselines. The primary Nexus images are musl-static binaries on distroless bases, and the FIPS variant of our crypto stack does not support musl static targets. Nexus needs a way to produce FIPS-capable builds without changing the primary images or the default dependency graph.
Decision Drivers
Section titled “Decision Drivers”- Keep Rust microservices with axum + tonic for backend intact: rustls with the aws-lc-rs provider, no native-tls/openssl/ring.
- Standard builds, images, and tests must be completely unaffected.
- A FIPS build must fail loudly at startup if the validated module is not actually in FIPS mode, rather than silently serving with a non-FIPS provider.
- Hardened image variants are produced downstream from release-pinned Nexus source, so the source tree must carry everything a downstream builder needs.
Considered Options
Section titled “Considered Options”fipscargo feature + boot self-check (chosen) —nexus-runtimegains afipsfeature that switches aws-lc-rs to the FIPS module (aws-lc-fips-sys) for the whole dependency graph, installs the rustls FIPS default provider, and verifiesaws_lc_rs::try_fips_mode()at startup. Every service binary and the CLI expose a matchingfipsfeature that forwards to it.- Always-on FIPS module — makes every build require CMake/Go toolchains and glibc targets, breaking the musl-static primary images.
- Runtime provider selection via configuration — a config flag cannot swap the compiled-in crypto backend; the FIPS module is a build-time decision.
Decision Outcome
Section titled “Decision Outcome”Chosen option: the fips cargo feature with a mandatory boot self-check.
nexus-runtime::crypto::init() runs in every service main and in the CLI.
In standard builds it logs that FIPS mode is disabled and changes nothing. In
fips builds it installs the rustls FIPS default provider, fails startup when
the AWS-LC module does not report FIPS mode or when a non-FIPS provider is
already installed as the process default, and logs the passing self-check.
FIPS builds target *-unknown-linux-gnu (the FIPS module does not support
musl static linking) and require CMake, Go, and Perl at build time. The
pipeline build script accepts BUILD_CONTEXT and IMAGE_BUILD_ARGS so
downstream producer repositories can build hardened image variants from this
source tree with their own Dockerfiles.
aws-lc-fips-sys version 0.13.15 declares the conjunctive license expression
ISC AND (Apache-2.0 OR ISC) AND OpenSSL.
The license policy grants an exact-version OpenSSL exception for this crate
because it is the selected FIPS module.
The exception does not permit OpenSSL for other dependencies, and changing
the crate version requires the exception to be reviewed again.
Consequences
Section titled “Consequences”- Good: the default dependency graph, images, and toolchains are unchanged;
fipsis strictly additive. - Good: a misbuilt or misconfigured FIPS deployment fails at boot with an explicit error instead of running non-validated crypto.
- Bad:
fipsbuilds compile the AWS-LC FIPS module from source, which adds CMake/Go/Perl to builder images and lengthens build times. - Bad: two crypto backends exist across build profiles, so FIPS-specific behavior is only exercised by builds that enable the feature.
