Draft: Stop a FIPS build when the Go toolchain cannot deliver FIPS
What does this MR do?
USE_GO_FIPS_MODULE and the builder image come from two separate CI variables. Nothing checks that they agree. The worst pair is a FIPS build with the toggle off on a toolchain that is not golang-fips: every Go binary gets standard Go crypto, the build succeeds, and nothing reports it. This is the same silent failure as #10091 (closed), one level up.
omnibus.rb now calls Build::Check.verify_go_fips_toolchain! next to the central export. A mismatch stops the build at config load, not deep inside the first Go component.
The two probes
Measurement chose both. See the evidence below.
- The module.
go versionaccepts anyGOFIPS140value, even one the toolchain does not have, so it verifies nothing.go list stdresolves the module and fails withunknown GOFIPS140 version. It needs no module and no temporary directory, and takes about 0.25s. - The fork. No version output separates the two toolchains.
strictfipsruntimeis the fork's own documented experiment and build tag, and upstream Go rejects the name.
The fork's crypto/internal/backend tree also separates them, but it belongs to the OpenSSL backend, which the fork deprecated and removed in favour of patches over the certified native module. It goes away as builder images move forward. strictfipsruntime does not.
What the guard catches
| Build | Toolchain | Before | After |
|---|---|---|---|
| Not FIPS, any toggle value | any | fine | not checked |
| FIPS, toggle on | base image | works | works |
| FIPS, toggle on | _fips image |
works | works |
| FIPS, toggle off | _fips image |
works | works |
| FIPS, toggle off | base image | ships standard Go crypto, silently | fails at config load |
| FIPS, toggle on | module version the toolchain lacks | fails in the first Go build | fails at config load |
| FIPS | no go on PATH |
fails later | fails at config load |
Empirical testing
Both builder images at revision 5.67.0, docker compose, one service per cell. Each runs the real Build::Check.verify_go_fips_toolchain! against that container's Go, and fails if the guard disagrees with the expected outcome.
| Service | Image | USE_GO_FIPS_MODULE |
fork? | Result |
|---|---|---|---|---|
native-on-base |
base | true |
no | no raise, OK |
legacy-on-base |
base | false |
no | raised, OK |
native-on-fips |
_fips |
true |
yes | no raise, OK |
legacy-on-fips |
_fips |
false |
yes | no raise, OK |
non-fips-toggle-on |
base | true, no USE_SYSTEM_SSL |
no | no raise, OK |
Facts the run established, each of which corrected an assumption:
- Both images report
go version go1.26.7 linux/amd64,GOVERSION=go1.26.7, an emptyGOEXPERIMENTandGOFIPS140=off. No version output identifies the fork. - Both images accept
GOEXPERIMENT=boringcrypto. That is whyBuild::Check.boringcrypto_supported?never identified the fork, and why !9773 (closed) removes it. - Both images ship
lib/fips140, soGOFIPS140also builds on the_fipsimage. That pair is wasteful, not broken, and does not raise. - The
_fipsimage is the older fork:go list -f '{{.GoFiles}}' crypto/internal/backendreturns[hostfips.go not_strict_fips.go openssl.go]. The OpenSSL backend is compiled in and turns on at run time from host FIPS mode, which is why it leaves no build marker. - Removing the
use_system_ssl?gate makesnon-fips-toggle-onraise. The gate is load-bearing, becauseuse_go_fips_module?already foldsuse_system_ssl?in.
bundle exec rspec spec/lib gives 607 examples and 0 failures. Rubocop is clean.
🛑 Merge order
This MR is on top of !9773 (closed) and blocks on it. The chain is !9771 (closed), then !9774 (closed), then !9773 (closed), then this MR. The diff against master therefore also shows the earlier commits until they merge.
The second branch of the guard serves USE_GO_FIPS_MODULE=false alone. Retire the branch with the toggle.
Related issues
Related to #10091 (closed)
Related to #10002 (closed)
Checklist
See Definition of done.
Required
- MR title and description are up to date, accurate, and descriptive.
- MR targeting the appropriate branch.
- Latest Merge Result pipeline is green.
- When ready for review, MR is labeled workflowready for review.
- UBT: not applicable.
For GitLab team members
- The manual
Trigger:ee-packagejobs have a green pipeline against the latest commit. - This MR changes FIPS build behavior, so the
Trigger:package:fipsmanual job must succeed. - A FIPS build with the toggle off must still pass, because the guard raises on that path when the image does not match.
Expected
- Documentation updated.
- Tests added.
- Test plan: see Empirical testing above.
- Chart equivalent: not applicable. The Go FIPS work for CNG is in gitlab-org#22761 (closed).