Skip to content

Fix panics under GODEBUG=fips140=only in SSHKey/Certificate generation - #749

Open
sameerforge wants to merge 1 commit into
carvel-dev:developfrom
sameerforge:fix/fips140-without-enforcement-md5-sha1
Open

Fix panics under GODEBUG=fips140=only in SSHKey/Certificate generation#749
sameerforge wants to merge 1 commit into
carvel-dev:developfrom
sameerforge:fix/fips140-without-enforcement-md5-sha1

Conversation

@sameerforge

@sameerforge sameerforge commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Problem

When running under Go's native FIPS 140-3 "only" enforcement mode (GOFIPS140=v1.0.0 + GODEBUG=fips140=only), reconciling any SSHKey or Certificate resource panics unconditionally:

  • SSHKeyGenerator.fingerprintMD5 (github.com/cloudfoundry/config-server, vendor/.../ssh_key_generator.go:72) uses crypto/md5 to compute the SSH public key fingerprint exposed as SSHKey.PublicKeyFingerprint — the same convention as ssh-keygen -l -E md5.
  • CertificateGenerator.bigIntHash(vendor/.../certificate_generator.go:66) uses crypto/sha1 to derive the X.509 SubjectKeyId per RFC 5280 §4.2.1.2, method (1).

Neither use is a cryptographic operation in the security sense — they're non-forgeability-sensitive identifiers, not signatures or authentication. But Go's fips140=only mode blocks the primitive itself regardless of call-site intent, so both md5.Sum/sha1.Sum calls panic as soon as they execute, breaking every SSHKey and Certificate reconcile under strict FIPS enforcement.

Fix

Rather than touching the vendored cloudfoundry/config-server library, this wraps the two call sites — in this project's own pkg/generator code — with crypto/fips140.WithoutEnforcement (added in Go 1.26):

  • pkg/generator/ssh_key_reconciler.go: wrap gen.Generate(nil) in SSHKeyReconciler.generate
  • pkg/generator/certificate_reconciler.go: wrap gen.Generate(params) in CertificateReconciler.generate

WithoutEnforcement scopes the FIPS 140-3 "only" exemption to just the wrapped callback, and per its own documented semantics (and Go stdlib source) is a no-op whenever strict fips140=only enforcement isn't active. So this has no effect on non-FIPS builds, or on GODEBUG=fips140=on — confirmed both by reading crypto/fips140's implementation and by empirical testing (identical behavior wrapped vs. unwrapped in both non-FIPS and fips140=on modes; only fips140=only diverges, which is exactly the mode this fixes).

Testing

  • Added pkg/generator/fips140_internal_test.go with two tests exercising SSHKeyReconciler.generate and CertificateReconciler.generate directly, gated on fips140.Enforced():
    • Skip as a no-op under normal test runs (no FIPS enforcement active).
    • Pass under GOFIPS140=v1.0.0 GODEBUG=fips140=only, where they previously would have panicked.
  • Ran the existing pkg/generator, pkg/expansion, pkg/satoken, pkg/sharing, pkg/tracker test suites both at baseline and under GOFIPS140=v1.0.0 GODEBUG=fips140=only — all pass in both configurations.
  • Verified via a standalone reproduction that wrapped vs. unwrapped md5/sha1 calls behave identically under no-FIPS and fips140=on, and diverge (panic vs. no panic) only under fips140=only.

Under Go's native FIPS 140-3 "only" enforcement mode, two vendored
non-security primitives panic on every reconcile:

- SSHKeyGenerator.fingerprintMD5 (github.com/cloudfoundry/config-server
  vendor/.../ssh_key_generator.go:72) uses crypto/md5 to compute the
  SSH public key fingerprint shown in SSHKey.PublicKeyFingerprint
  (the same convention as `ssh-keygen -l -E md5`).
- CertificateGenerator.bigIntHash (vendor/.../certificate_generator.go:66)
  uses crypto/sha1 to derive the X.509 SubjectKeyId per RFC 5280
  4.2.1.2 method (1).

Neither is a cryptographic operation in the security sense - they're
non-forgeability-sensitive identifiers, not signatures or authentication
- but Go's fips140=only mode blocks the primitive itself regardless of
call-site intent, so both panic unconditionally on every SSHKey and
Certificate reconcile.

Wrap both call sites (in our own pkg/generator code, not the vendored
library) in crypto/fips140.WithoutEnforcement, added in Go 1.26. It
scopes the FIPS 140-3 "only" exemption to just that call and is a
documented no-op when strict enforcement isn't active, so this has no
effect on non-FIPS builds.

Found via a downstream FIPS build's real end-to-end run: the project's
own test/e2e suite (TestSSHKey, TestSSHKeyTemplate, TestCertificate)
against the actual shipped artifact, deployed to a real cluster. Added
fips140_internal_test.go, gated on fips140.Enforced(), to exercise the
same paths directly (skips as a no-op unless run with
GOFIPS140=v1.0.0 GODEBUG=fips140=only).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Sameer <sameer.khan@broadcom.com>
@sameerforge

Copy link
Copy Markdown
Contributor Author

@joaopapereira Please review.

@sameerforge

Copy link
Copy Markdown
Contributor Author

@joaopapereira Please review the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants