Skip to content

Add FIPS-approved NIST P-curve key exchanges to the SSH proxy default - #1220

Merged
ameowlia merged 1 commit into
cloudfoundry:developfrom
Viktor-Velkov:ssh-proxy-fips-key-exchanges
Oct 28, 2026
Merged

ameowlia merged 1 commit into
cloudfoundry:developfrom
Viktor-Velkov:ssh-proxy-fips-key-exchanges

Conversation

@Viktor-Velkov

Copy link
Copy Markdown
Contributor

Summary

The SSH proxy and cell sshd default their key-exchange list to
curve25519-sha256@libssh.org only when diego.ssh_proxy.allowed_keyexchanges
is unset. A FIPS-mode client (e.g. Go with GODEBUG=fips140=on, which removes
curve25519) then shares no common key-exchange algorithm with the proxy, and
cf ssh fails at the handshake before authentication.

This widens the default KEX list to add the FIPS-approved NIST P-curves
(ecdh-sha2-nistp256/384/521), keeping curve25519 first so non-FIPS clients
negotiate exactly as before. Operators can still override via
diego.ssh_proxy.allowed_keyexchanges.

Changes:

  • cmd/ssh-proxy/main.go — widen the default KEX list.
  • cmd/sshd/main.go — mirror the same default on the cell daemon.
  • cmd/ssh-proxy/main_test.go — update the existing default-KEX expectation
    and add a regression test proving a nistp-only (FIPS-mode) client completes
    the handshake with no explicit config.

golang.org/x/crypto already registers the NIST P-curves, so no dependency
bump is needed.

Fixes #1219
Related to #1135

Testing

  • go build and go vet pass for cmd/ssh-proxy and cmd/sshd.
  • The crypto negotiation was verified against golang.org/x/crypto with a
    standalone handshake harness: a nistp-only (FIPS-mode) client fails against a
    curve25519-only server (reproducing the bug) and succeeds once the P-curves
    are added, while a non-FIPS client continues to negotiate curve25519.

Note: when running the diego-ssh integration suite locally on develop, the
tests fail before reaching the handshake because the checked-in test fixtures
src/code.cloudfoundry.org/diego-ssh/cmd/ssh-proxy/fixtures/metron/* expired on
2026-09-12 (certificate has expired). This pre-existing fixture expiry is
unrelated to this change — the same failure reproduces on a clean develop
checkout without these commits. Happy to regenerate those fixtures in a separate
PR if useful.

Backward Compatibility

Breaking Change? No

curve25519 remains first in the list, so existing non-FIPS clients negotiate it
unchanged; this only adds fallback algorithms. Safe to apply to all
deployments immediately.

@ameowlia ameowlia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. please resolve conflicts
  2. have you run the tests? I suspect this test will fail.
  3. you updated both sshproxy and sshd, but you only added a test for sshproxy, but not a test for sshd. please add a test for sshd.

@Viktor-Velkov

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all three addressed:

  1. Conflicts resolved — rebased on develop; the only conflict was the KEX assertion line in ssh-proxy/main_test.go.
  2. Test failure — the real cause was the recent x/crypto bump (v0.58.0) changing the handshake error format to quote algorithm names (["curve25519..."]). The assertions now use the quoted format. Verified passing against v0.58.0.
  3. sshd test added — added a FIPS-mode (nistp-only client) regression test for cmd/sshd, mirroring the ssh-proxy one, and fixed the existing sshd default-KEX assertion too.

KEX specs pass locally (ssh-proxy 2/2, sshd 4/4). Note: the full ssh-proxy suite still needs the expired fixtures/metron/* certs (expired 2026-09-12) regenerated — pre-existing on develop.

The ssh-proxy and cell sshd default their key-exchange list to
curve25519-sha256@libssh.org only when allowed_keyexchanges is unset.
A FIPS-mode client (e.g. Go with GODEBUG=fips140=on, which removes
curve25519) then shares no common key-exchange algorithm with the proxy
and cf ssh fails at the handshake before authentication.

Widen the default KEX list to add the FIPS-approved NIST P-curves
(ecdh-sha2-nistp256/384/521), keeping curve25519 first so non-FIPS
clients negotiate exactly as before. Operators can still override via
diego.ssh_proxy.allowed_keyexchanges. x/crypto already registers the
nistp curves, so no dependency bump is needed.

- cmd/ssh-proxy/main.go: widen the default KEX list
- cmd/sshd/main.go: mirror the same default on the cell daemon
- cmd/ssh-proxy/main_test.go: update the default-KEX expectation to the
  quoted error format and add a regression test proving a nistp-only
  (FIPS-mode) client completes the handshake with no explicit config
- cmd/sshd/main_test.go: mirror the same default-KEX expectation and add
  the matching nistp-only regression test for the cell daemon
@Viktor-Velkov
Viktor-Velkov force-pushed the ssh-proxy-fips-key-exchanges branch from 041f8a7 to ece5c6e Compare October 23, 2026 10:25
@github-project-automation github-project-automation Bot moved this from Inbox to Pending Merge | Prioritized in Application Runtime Platform Working Group Oct 28, 2026
@ameowlia
ameowlia merged commit e2d487d into cloudfoundry:develop Oct 28, 2026
10 checks passed
@github-project-automation github-project-automation Bot moved this from Pending Merge | Prioritized to Done in Application Runtime Platform Working Group Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

Add FIPS-approved NIST P-curve key exchanges to the SSH proxy default

2 participants