Skip to content

ssh-util: FIPS OpenSSH build on AWS-LC-FIPS 3.x (draft, pending NIST cert)#36859

Draft
jasonhernandez wants to merge 2 commits into
mainfrom
jason/sec-236-fips-ssh-draft
Draft

ssh-util: FIPS OpenSSH build on AWS-LC-FIPS 3.x (draft, pending NIST cert)#36859
jasonhernandez wants to merge 2 commits into
mainfrom
jason/sec-236-fips-ssh-draft

Conversation

@jasonhernandez
Copy link
Copy Markdown
Contributor

@jasonhernandez jasonhernandez commented Jun 2, 2026

Summary

The FIPS portion carved out of #35858, reworked per the post-merge QA review (#35858 (comment)) and re-targeted at the AWS-LC-FIPS 3.x line.

Warning

Not production-FIPS-ready yet. AWS-LC-FIPS 3.x is still in process with NIST CMVP (the static module is in Comment Resolution), so it provides no validated-module compliance until the certificate is awarded. This PR stays a draft until then. Track status: https://csrc.nist.gov/Projects/cryptographic-module-validation-program/modules-in-process/modules-in-process-list

Depends on / pairs with #36858 (removes this FIPS code from main). Once #36858 merges, this branch will be rebased so its diff is a clean "add FIPS".

Why AWS-LC-FIPS 3.x + OpenSSH 10

Materialize's SSH connection keys are Ed25519 (mz-ssh-util keys.rs). Ed25519 is FIPS-approved under FIPS 186-5, but only enters a validated module boundary on the AWS-LC-FIPS 3.x line (2.0 / cert #4816 does not include EdDSA). It also only executes inside the module with OpenSSH ≥ 10.0, which routes ed25519 through libcrypto instead of OpenSSH's bundled implementation. Both bumps are required for the ed25519 client key to be in-boundary.

Changes (addressing the QA findings)

  • F1 (HIGH): strip only non-FIPS builds — stripping removes the symbols the FIPS integrity self-test needs to locate the module, so a stripped FIPS binary fails its power-on self-test.
  • F2 (MED): pin the FIPS build to a FIPS-branch tag (AWS-LC-FIPS-3.3.0) via a dedicated AWS_LC_FIPS_VERSION arg, not a generic release tag (-DFIPS=1 on a release tag yields a non-validated "FIPS-style" module).
  • F3 (MED): inject -DBN_FLG_CONSTTIME=0 via configure CPPFLAGS instead of make CFLAGS=..., preserving hardening/optimization flags.
  • F4 (LOW): keep ssh-ed25519 in both HostKeyAlgorithms and PubkeyAcceptedAlgorithms (required for the ed25519 client key; valid for host keys on the 3.x module).
  • Bump OpenSSH to V_10_3_P1.

Part of SEC-236.

Test plan

  • cargo check -p mz-ssh-util passes (rustc 1.96.0)
  • cargo fmt -p mz-ssh-util --check clean
  • Docker build of misc/images/openssh-static/ --build-arg AWS_LC_FIPS=1 produces a working static binary (verified: OpenSSH_10.3p1, AWS-LC FIPS 3.3.0; 6.5M, statically linked, not stripped, integrity symbols intact)
  • FIPS load-time integrity self-test passes (binary runs to exit 0 rather than aborting in the self-test loop). Full power-on KAT suite under a real connection still untested.
  • Block on AWS-LC-FIPS 3.x NIST certificate before un-drafting

🤖 Generated with Claude Code

Addresses post-merge QA review of #35858. Improves the FIPS path of the
static OpenSSH image and the FIPS SSH config:

- Strip only non-FIPS builds. Stripping removes the symbols the FIPS
  integrity self-test needs to locate the module boundary, so a stripped
  FIPS binary fails its power-on self-test at first crypto use.
- Pin the FIPS build to a FIPS-branch tag (AWS-LC-FIPS-3.3.0) via a
  dedicated AWS_LC_FIPS_VERSION arg, not a generic release tag. -DFIPS=1
  on a release tag yields a non-validated "FIPS-style" module. 3.x is the
  line that includes EdDSA/Ed25519 in the module boundary.
- Bump OpenSSH to V_10_3_P1 so ed25519 routes through libcrypto (AWS-LC)
  rather than OpenSSH's bundled implementation.
- Inject -DBN_FLG_CONSTTIME=0 via configure CPPFLAGS instead of
  `make CFLAGS=...`, which replaced configure's hardening/optimization
  flags and produced an unhardened binary.
- Keep ssh-ed25519 in both HostKeyAlgorithms and PubkeyAcceptedAlgorithms.
  Materialize connection keys are Ed25519, so it must stay in the client
  list; Ed25519 is FIPS-approved (186-5) and in the AWS-LC-FIPS 3.x
  boundary, so it is valid for host keys too.

NOTE: AWS-LC-FIPS 3.x is still "in process" with NIST CMVP, so this is not
production-FIPS-ready until the certificate is awarded. Draft pending cert.

Part of SEC-236.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…WS-LC)

OpenSSH >= 10.0 stubs BN_set_flags() to a no-op under OPENSSL_IS_AWSLC
(openbsd-compat/openssl-compat.h), so BN_FLG_CONSTTIME is never emitted and
needs no definition. The -DBN_FLG_CONSTTIME=0 define was dead weight here and
a footgun: a global value-define of a security-critical OpenSSL macro to 0,
harmless against AWS-LC but a latent timing-side-channel landmine if the
backend ever changed to real OpenSSL. Build with a plain configure + make.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jasonhernandez jasonhernandez force-pushed the jason/sec-236-fips-ssh-draft branch from d412eb0 to 6c71317 Compare June 2, 2026 04:09
jasonhernandez added a commit that referenced this pull request Jun 2, 2026
…6858)

## Summary

Carves the FIPS work out of #35858 and back into a separate draft
(#36859), which is gated on AWS-LC-FIPS 3.x completing NIST CMVP
validation. The static OpenSSH image **stays on main** because it is the
prerequisite for the distroless migration of environmentd/clusterd,
independent of FIPS.

This addresses the post-merge QA review on #35858
(#35858 (comment)),
which found the FIPS path could not actually produce a validated module
yet.

### Changes

- **Dockerfile**: remove the `AWS_LC_FIPS` build arg and `-DFIPS=1`
cmake path; the image now builds only a non-FIPS static `ssh` against a
regular AWS-LC release. Bump OpenSSH to `V_10_3_P1`, which natively
stubs `BN_set_flags()` for AWS-LC, so the prior `-DBN_FLG_CONSTTIME=0`
shim is dropped (it was a footgun: a global value-define of a
security-critical OpenSSL macro to `0`).
- **Keep the configure-CPPFLAGS hardening fix** from the QA review
(Finding 3): inject `-DBN_FLG_CONSTTIME=0` at configure time so
OpenSSH's hardening/optimization flags (`-O2`,
`-fstack-protector-strong`, `-fPIE`, `-ftrapv`, ...) are preserved. A
`make CFLAGS=...` override silently replaced them, producing an
unhardened binary.
- **tunnel.rs**: remove the dormant `MZ_FIPS` SSH-config enforcement
(`fips_mode_enabled` / `write_fips_ssh_config` and the call site).

Opened as a **draft** at the author's request.

Part of SEC-236.

## Test plan

- [x] `cargo check -p mz-ssh-util` passes (rustc 1.96.0)
- [x] `cargo fmt -p mz-ssh-util --check` clean
- [x] Docker build of `misc/images/openssh-static/` produces a working
static binary (verified: statically-linked `OpenSSH_10.3p1` against
AWS-LC 1.54.0, 3.9M)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Jason Hernandez <7144515+jasonhernandez@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant