Skip to content

feat: build the OpenSSL FIPS provider from FIPS 140-3 validated sources (#45) - #46

Merged
Xavier Fournet (xfournet) merged 6 commits into
mainfrom
issue/45-fips-140-3-validated-provider
Aug 28, 2026
Merged

feat: build the OpenSSL FIPS provider from FIPS 140-3 validated sources (#45)#46
Xavier Fournet (xfournet) merged 6 commits into
mainfrom
issue/45-fips-140-3-validated-provider

Conversation

@xfournet

@xfournet Xavier Fournet (xfournet) commented Aug 25, 2026

Copy link
Copy Markdown
Member

Important

The images are published under new names: filigran/alpine-python-fips and filigran/alpine-python-nodejs-fips. The changes below would break consumers tracking the latest tag of filigran/python-fips and filigran/python-nodejs-fips, so those two keep being built — unchanged — from legacy/, under their own workflow. That is a migration window, not a maintained line: their FIPS provider is not built from the validated sources, which is what this PR addresses.

Proposed changes

  • Build the OpenSSL FIPS provider from the 3.1.2 sources, the only version validated under FIPS 140-3 (CMVP #4985), with the tarball SHA-256 pinned and verified.
  • Take OpenSSL, Python and Node.js from Alpine packages; drop the OpenSSL and CPython builds. The module boundary is fips.so, so the libraries calling into it can be the packaged ones.
  • Install the provider into the paths compiled into Alpine's libcrypto, so FIPS mode is active with no environment variable to set.
  • Merge the two Dockerfiles into one with two targets, python-nodejs-fips deriving from python-fips.
  • Reduce openssl.cnf to the provider configuration alone. It carried a copy of the upstream template several releases behind the packaged one, including ess_cert_id_alg = sha1 where Alpine has sha256.
  • Keep cryptography built from source, and assert at build time that it links the system OpenSSL — a pre-built musllinux wheel would carry its own and sit outside the boundary.
  • Drop what the images do not need: npm, yarn, and the Rust toolchain, which is now transient.
  • Assert the FIPS setup at build time, so a broken setup fails the build instead of shipping.
  • Build on pull requests too, publishing only when the ref is main. Renovate automerges dependency bumps here, so an unbuildable bump could otherwise reach main unverified.
  • Read the version part of the tags from the Dockerfile, which asserts those versions at build time, so a tag cannot drift from the content it describes. This also fixes nodejs22, which the published image contradicted.
  • Keep the previous images building from legacy/, under a separate workflow so that the slow legacy build and the fast one share no job, no concurrency group and no trigger. Renovate manages the OpenSSL and CPython pins those Dockerfiles compile, scoped to legacy/ so the root Dockerfile is left alone.
  • Add FIPS.md, align the README and renovate.json5 with the tech playbook, delete .github/dependabot.yml.

Related issues

How to test this PR

The workflow now runs on pull requests, so it builds both images on this PR without publishing them.

podman build --target python-nodejs-fips -t fips-check .
podman run --rm fips-check sh -c '
  openssl list -providers
  node --enable-fips -p "crypto.getFips()"
  echo test | openssl dgst -md5 || echo "MD5 refused, as expected"
'

The build is itself a test: it fails if the active provider is not the expected version, if MD5 is accepted, or if cryptography is not linked against the system OpenSSL.

Checklist

  • The PR title follows the Conventional Commits convention type(scope?): description (#issue)
  • I signed my commits
  • This PR is linked to an issue
  • I consider the submitted work as finished
  • I tested the code for its functionality
  • I added/updated the relevant documentation
  • Where necessary, I refactored code to improve the overall quality

Verification

Both targets built from this Dockerfile, on Alpine 3.23.5:

providers      base 3.5.7 (Alpine) + fips 3.1.2 (compiled), both active
runtimes       Python 3.12.14, Node.js v24.18.1, OpenSSL 3.5.7
cryptography   50.0.1, reporting OpenSSL 3.5.7 — the system one, not a bundled copy
MD5            refused by OpenSSL, by Node.js and through cryptography
node           --enable-fips -p crypto.getFips() === 1, linked to /usr/lib/libssl.so.3
TLS 1.3        OK via s_client, Python urllib and Node.js https
image size     python-fips 86.8 MB, python-nodejs-fips 154 MB (from 1.12 GB)
CI             green on the runner, publication skipped off `main`

The module integrity check was verified by flipping one byte inside fips.so: the provider then disappears from openssl list -providers, cryptographic operations fail with inner_evp_generic_fetch:unsupported, and Node.js reports OpenSSL error when trying to enable FIPS.

Further comments

What the images can claim. They perform cryptography through the OpenSSL FIPS provider 3.1.2, the module validated under CMVP #4985, built from the validated source distribution by the procedure its Security Policy hands to integrators — ./Configure enable-fips, make, make install_fips — unmodified, with integrity verification and the required run-time checks enabled. What to avoid claiming is that the image is FIPS 140-3 validated: certificates are awarded to modules. FIPS.md states both, and lists the limits worth knowing such as hashlib.md5 bypassing OpenSSL.

Impact on derived images, and the reason for the rename. npm and yarn are no longer installed, and neither is a build toolchain: an image that compiles native wheels must bring its own. pip and cryptography remain, and the PEP 668 marker is removed so pip install keeps working. Anything installing crypto from a pre-built wheel or a statically linked binary falls outside the module boundary — use pip install --no-binary cryptography for that package.

Python minor version now follows Alpine (3.23 → 3.12.14). Decoupling it would require reintroducing a CPython build.

Renovate and Dependabot. vulnerabilityAlerts is now disabled and prHourlyLimit raised to 100, both playbook-mandatory. OPENSSL_FIPS_VERSION is deliberately left unmanaged. Dependabot alerts have been enabled on the repository; security updates stay disabled. cryptography is not pinned, matching the previous behaviour — worth revisiting if reproducibility across daily rebuilds matters.

The migration window has no end date. The README says the legacy workflow is meant to be deleted but does not say when, and without a date that will not happen. Note also that a scheduled workflow only fires from the default branch, which is why the legacy build lives on main rather than on a legacy branch.

CircleCI leftovers removed from the repository: the webhook and the deploy key were still active and reporting a failing CircleCI Pipeline status on every commit.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 25, 2026 21:52
@xfournet Xavier Fournet (xfournet) added filigran team Item from the Filigran team. vibe-coded PR: AI-assisted change — the author reviews it before requesting others' review. labels Aug 25, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR reworks the image build to compile only the OpenSSL FIPS provider from the FIPS 140-3 validated 3.1.2 sources (CMVP #4985) while using Alpine-packaged OpenSSL/Python/Node.js, and updates documentation and automation to match the new posture and build pipeline.

Changes:

  • Replace custom OpenSSL/CPython builds with Alpine packages and compile only the FIPS provider (with SHA-256 tarball verification) in a new multi-target Dockerfile.
  • Simplify openssl.cnf to provider-only configuration and add explicit posture documentation in FIPS.md, updating README.md accordingly.
  • Update CI workflow to build/push both targets in one job and align dependency automation by simplifying renovate.json5 and removing Dependabot config.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Dockerfile New multi-target build: python-fips and python-nodejs-fips, builds FIPS provider from pinned 3.1.2 sources and asserts FIPS behavior at build time.
openssl.cnf Reduced to provider-only config (FIPS + base, FIPS default properties).
FIPS.md New detailed posture/limitations documentation for downstream compliance assessment.
README.md Updated usage/testing examples and compliance messaging; points readers to FIPS.md.
renovate.json5 Simplified Renovate config and aligns scheduling/limits and vulnerability alert behavior.
.github/workflows/docker-build-push.yml Builds and pushes both images sequentially from the unified Dockerfile targets.
Dockerfile_python Removed legacy Dockerfile that built OpenSSL + CPython from source.
Dockerfile_python_nodejs Removed legacy Dockerfile that built OpenSSL + CPython from source and installed Node toolchain.
.github/dependabot.yml Removed Dependabot config in favor of Renovate coverage.
Suppressed comments (1)

README.md:28

  • Same naming consistency issue: the project uses “Node.js” elsewhere (e.g., FIPS.md). Updating this bullet to “Node.js” keeps terminology consistent across docs.
* For NodeJS, ensure to run your NodeJS programs with `--enable-fips` or
  `--force-fips`.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread README.md Outdated
Comment thread renovate.json5
@xfournet
Xavier Fournet (xfournet) force-pushed the issue/45-fips-140-3-validated-provider branch 5 times, most recently from c42b40a to e1316e1 Compare August 25, 2026 22:15
@xfournet
Xavier Fournet (xfournet) force-pushed the issue/45-fips-140-3-validated-provider branch 9 times, most recently from 2669650 to 8385845 Compare August 25, 2026 23:46
…es (#45)

The cryptographic module is fips.so, so the boundary stops at it and the
OpenSSL libraries calling into it can be the ones Alpine packages. Build the
provider from the 3.1.2 sources, the only version validated under FIPS 140-3
(CMVP #4985), and verify the tarball checksum since unmodified source is a
condition of the CMVP porting rule.

OpenSSL, Python and NodeJS now come from Alpine packages, which removes both
the OpenSSL and the CPython builds. The two Dockerfiles become one with two
targets, and openssl.cnf keeps only the provider configuration instead of a
copy of the upstream template that had drifted several releases behind.

The build asserts that the expected provider is active, that MD5 is refused,
and that NodeJS enters FIPS mode against the system OpenSSL, so a broken setup
fails the build rather than shipping.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xfournet
Xavier Fournet (xfournet) force-pushed the issue/45-fips-140-3-validated-provider branch from 8385845 to 05c45f7 Compare August 26, 2026 12:43
The changes in this branch break consumers tracking `latest`: npm, yarn and the
build toolchain are gone, and the FIPS provider no longer accepts the same set of
algorithms. Publishing under filigran/alpine-python-fips and
filigran/alpine-python-nodejs-fips leaves the previous images frozen at their
last build rather than breaking whoever depends on them, and the prefix leaves
room for a second base image should one follow.

The version part of the tags is now read from the Dockerfile, which already
asserts those versions at build time, so a tag cannot drift from the content it
describes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Freezing filigran/python-fips and filigran/python-nodejs-fips would leave their
consumers on an image that no longer receives Alpine updates. Keep building them
from legacy/, under their own workflow so the slow legacy build and the fast one
never share a job or a concurrency group, and so a change to one cannot trigger
the other.

The legacy Dockerfiles compile OpenSSL and CPython from pinned sources, so the
Renovate managers for those two pins come back, scoped to legacy/ only: the root
Dockerfile's PYTHON_VERSION asserts a tag rather than naming a source version.

Note that a scheduled workflow only fires from the default branch, which is why
this lives on main rather than on a legacy branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
legacy/Dockerfile_python_nodejs installs nodejs from Alpine 3.23, which is
24.18.1, while the tag said nodejs22.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The playbook now requires GitHub Actions updates to sit in the weekly
dev-dependency group, being development tooling. This repository has no
devDependencies, so the group holds only actions, but the group name is kept as
the blueprint has it so that the convention reads the same across repositories.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xfournet
Xavier Fournet (xfournet) force-pushed the issue/45-fips-140-3-validated-provider branch 3 times, most recently from 50dd1ac to 1fab0a5 Compare August 28, 2026 00:49
@xfournet
Xavier Fournet (xfournet) force-pushed the issue/45-fips-140-3-validated-provider branch 6 times, most recently from 4e3305f to e27c844 Compare August 28, 2026 01:04
The images run the module validated under CMVP #4985, at the validated version,
from the validated sources, installed by the procedure its Security Policy gives
integrators, with integrity verification and the required run-time checks
enabled. FIPS.md states that, the claims it supports, and the two limits that
matter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xfournet
Xavier Fournet (xfournet) force-pushed the issue/45-fips-140-3-validated-provider branch from e27c844 to fa9d27b Compare August 28, 2026 01:05
@xfournet
Xavier Fournet (xfournet) merged commit 5febb15 into main Aug 28, 2026
4 checks passed
@xfournet
Xavier Fournet (xfournet) deleted the issue/45-fips-140-3-validated-provider branch August 28, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

filigran team Item from the Filigran team. vibe-coded PR: AI-assisted change — the author reviews it before requesting others' review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: build the OpenSSL FIPS provider from FIPS 140-3 validated sources

2 participants