Build Go components against the FIPS module unconditionally

What does this MR do?

FIPS builds could produce Go binaries two ways: against the golang-fips fork, or against upstream Go's native FIPS 140-3 module. USE_GO_FIPS_MODULE and FIPS_BUILDER_IMAGE_SUFFIX selected between them, and nothing tied the two variables to each other or to the build cache.

This MR collapses that to one configuration. The golang-fips path is gone, GOFIPS140 is exported once from omnibus.rb instead of per recipe, and a config-load check stops the build when the toolchain cannot supply the module.

It supersedes !9771 (closed), !9773 (closed), !9774 (closed) and !9781 (closed), which are the same work spread across a toggle that no longer exists. Those can close when this merges.

Builder image dependency

gitlab-omnibus-builder!512 (merged) removes the go_fips snippet, so the FIPS builder images install the same upstream Go release as every other image. That merged as 5588a699, and tag 5.69.0 points at exactly that commit. BUILDER_IMAGE_REVISION is now 5.69.0.

This pin matters. 5.68.0 predates !512 (closed) and still ships golang-fips. That toolchain accepts GOFIPS140 and builds without an error, and the binaries then stop at init on a host in FIPS mode.

One gate, one behavior

Review raised that FIPS_MODE=1 was being gated on which Go FIPS mechanism we use, which joins unrelated concerns. The gates are now independent:

  • Build::Check.use_system_ssl? controls what C code links against.
  • Build::Check.use_go_fips_module? controls only which crypto module Go compiles against. It is no longer implied by use_system_ssl?.

Every FIPS build job sets USE_SYSTEM_SSL, USE_SYSTEM_LIBGCRYPT and USE_GO_FIPS_MODULE together in .fips_branch_template and .fips_tag_template, so no pipeline changes behavior. Because the gates no longer cover for each other, a build that sets USE_SYSTEM_SSL without USE_GO_FIPS_MODULE now skips the GOFIPS140 export, skips the toolchain check, and gives its Go components no FIPS_MODE=1.

Why gitaly.rb gates on either check

Gitaly's Makefile makes FIPS_MODE a single switch over two concerns. Under ifdef FIPS_MODE it appends the fips Go build tag to SERVER_BUILD_TAGS, and it sets GIT_FIPS_MESON_BUILD_OPTIONS to -Dsha256_backend=openssl for the bundled Git C build. Upstream offers no way to request one without the other.

gitaly.rb runs make install, which builds the Go binaries and bundled Git, so neither gate alone is correct for it. use_system_ssl? alone drops the Go build tag from a module build; use_go_fips_module? alone drops the OpenSSL SHA256 backend from the C build. Both fail silently. It therefore sets FIPS_MODE when either is true.

git.rb runs make git install-bundled-git, which builds no Go binaries, so FIPS_MODE there does nothing but select the SHA256 backend. It stays on use_system_ssl? alone, unchanged.

Splitting FIPS_MODE upstream in Gitaly would let each gate drive exactly one behavior. That is a Gitaly change, tracked separately.

Why the toggle had to go rather than flip

The rollback story was "flip a variable", and the build cache breaks it. The omnibus git cache digest covers a definition file, its version and its dependency shasums — not the environment, not the Go toolchain — and neither .branch-cache nor .tag-cache carried the toggle. Both configurations of a FIPS job shared one cache key.

That is not theoretical. A package built from this work carried a native-module registry restored from another pipeline's cache next to golang-fips binaries built in place, with no error anywhere. Thanks to @achugitlab for installing the package and finding it.

CACHE_KEY_SUFFIX therefore moves to -v4, once. Without it the first build after this change restores golang-fips binaries for every component whose recipe and version did not change.

Why the _fips images stay

FIPS_BUILDER_IMAGE_SUFFIX is removed, but every FIPS job still names its _fips image — now literally. Those images carry more than the Go toolchain: their curl_<platform>_fips snippets install the libidn2, libpsl, brotli, libnghttp2 and libssh2 development packages that FIPS builds need to link the distribution's curl (doc/curl_fips.md in the builder repo).

Measured on almalinux_9 5.67.0, running this repo's own CurlHelper inside both images:

_fips image base image
CurlHelper.pkg_config_files 7 files 1 file (libcurl.pc)
threshold met (>= 5) yes no
OpenSSLHelper.pkg_config_files 3 3 — identical

gitaly.rb, git.rb and gitlab-rails.rb copy only the resolved .pc files into an overrides directory and then replace PKG_CONFIG_PATH, so a base-image FIPS build would run with 4 of the 10 files it expects. PKG_CONFIG_THRESHOLD = 5 is not enforced anywhere, so nothing would have stopped it.

The *_fips package check jobs likewise name their images literally: those jobs install and exercise a built package, so the image is a verification environment, not a build toolchain.

The registry and labkit

Every Go component now sets its own FIPS switch, thus the registry sets one too. The registry reads no FIPS_MODE. Its Makefile passes BUILDTAGS to go build -tags, so registry.rb appends fips there.

That tag needs a labkit that builds without crypto/boring. Before labkit v1.64.10, the //go:build fips files import crypto/boring, which has no buildable files under upstream Go. The registry pinned labkit v1.64.8, so the first attempt broke all four FIPS builds and was reverted. v4.42.0-gitlab moves the registry to labkit v1.65.3, which puts the BoringCrypto probe behind fips && boringcrypto. This MR takes that release and restores the tag.

Measured in almalinux_9_fips:5.69.0, which carries upstream Go 1.26.7:

Case Result
v4.42.0-gitlab, fips tag on Builds. go version -m gives -tags=include_gcs,include_oss,fips,fips140v1.0, GOFIPS140=v1.0.0-c2097c7c, labkit v1.65.3
v4.41.0-gitlab, fips tag on, same image and environment Fails: imports crypto/boring: build constraints exclude all Go files
The built binary, run with database.enabled: false Starts and logs FIPS mode is enabled. Using the native Go Cryptographic Module.
labkit v1.65.3 probe, tag off fips.Enabled() is false, although crypto/fips140.Enabled() is true

Row 2 shows that the labkit bump is the cause of the correction, not the newer builder image. Row 3 is the built registry reporting its own FIPS status correctly. Row 4 shows the defect this closes: without the tag the module is active, but the component reports FIPS as off, thus the S3 driver does not select the AWS FIPS endpoints.

Commits

Each commit is scoped to one change and is independently lint-clean and test-green. Commits 9 to 11 address review on this MR and sit on top of the original series rather than being folded into it.

  1. Drop the golang-fips fallback from Go builds — GOEXPERIMENT/boringcrypto_supported?; gitlab-pages decides its own backend from go env GOFIPS140
  2. Make use_go_fips_module? the single gate for Go FIPS builds
  3. Retire the FIPS builder image suffix variable
  4. Retire FIPS build caches written under the old toggle — CACHE_KEY_SUFFIX -v4
  5. Export GOFIPS140 once for all Go builds — central export, plus a spec that fails any definition setting GOFIPS140 without a justified off bypass
  6. Stop a FIPS build when the toolchain lacks the module — go list std resolves the module where go version accepts anything
  7. Gate FIPS_MODE on use_go_fips_module?
  8. Set FIPS_MODE=1 for the gitlab-kas build — the fips tag its own build/verify_fips_binary.sh asserts on
  9. Decouple the Go FIPS module gate from system OpenSSL — narrows commit 2, so use_system_ssl? no longer implies the module
  10. Set gitaly's FIPS_MODE from either FIPS gate — narrows commit 7 for the one component that builds both Go and C
  11. Name the checks that turn on the Go FIPS module — doc wording, per @achugitlab
  12. Build FIPS packages on images with upstream Go — BUILDER_IMAGE_REVISION to 5.69.0
  13. Build the registry with the fips tag — the first attempt
  14. Revert the registry fips tag: labkit is too old — it broke all four FIPS builds, and the cause was the labkit pin
  15. Bound the Go FIPS toolchain probe and report its error
  16. Update dependency container-registry to v4.42.0-gitlab — labkit v1.64.8 to v1.65.3
  17. Build the registry with the fips tag — restored, now that labkit supports it

Closes #10091 (closed). Closes #10090 (closed). Related to #10002 (closed) and gitlab-org#22761 (closed).

Checklist

See Definition of done.

  • Merge gitlab-omnibus-builder!512 (merged)
  • Cut a tag from !512 (closed) and set BUILDER_IMAGE_REVISION to it (5.69.0)
  • Verify a FIPS package's Go binaries report GOFIPS140 via go version -m, and that registry carries the fips tag
  • Documented in doc/development/ci-variables.md and doc/development/new-software-definition.md
  • Added the ~"workflow::ready for review" label
  • Pipeline is green
Edited by Jason Plum

Merge request reports

Loading
Loading