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 version accepts any GOFIPS140 value, even one the toolchain does not have, so it verifies nothing. go list std resolves the module and fails with unknown GOFIPS140 version. It needs no module and no temporary directory, and takes about 0.25s.
  • The fork. No version output separates the two toolchains. strictfipsruntime is 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 empty GOEXPERIMENT and GOFIPS140=off. No version output identifies the fork.
  • Both images accept GOEXPERIMENT=boringcrypto. That is why Build::Check.boringcrypto_supported? never identified the fork, and why !9773 (closed) removes it.
  • Both images ship lib/fips140, so GOFIPS140 also builds on the _fips image. That pair is wasteful, not broken, and does not raise.
  • The _fips image is the older fork: go list -f '{{.GoFiles}}' crypto/internal/backend returns [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 makes non-fips-toggle-on raise. The gate is load-bearing, because use_go_fips_module? already folds use_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 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-package jobs have a green pipeline against the latest commit.
  • This MR changes FIPS build behavior, so the Trigger:package:fips manual 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).

Merge request reports

Loading
Loading