feat: build the OpenSSL FIPS provider from FIPS 140-3 validated sources (#45) - #46
Merged
Xavier Fournet (xfournet) merged 6 commits intoAug 28, 2026
Merged
Conversation
There was a problem hiding this comment.
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.cnfto provider-only configuration and add explicit posture documentation inFIPS.md, updatingREADME.mdaccordingly. - Update CI workflow to build/push both targets in one job and align dependency automation by simplifying
renovate.json5and 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.
Xavier Fournet (xfournet)
force-pushed
the
issue/45-fips-140-3-validated-provider
branch
5 times, most recently
from
August 25, 2026 22:15
c42b40a to
e1316e1
Compare
7 tasks
Xavier Fournet (xfournet)
force-pushed
the
issue/45-fips-140-3-validated-provider
branch
9 times, most recently
from
August 25, 2026 23:46
2669650 to
8385845
Compare
…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>
Xavier Fournet (xfournet)
force-pushed
the
issue/45-fips-140-3-validated-provider
branch
from
August 26, 2026 12:43
8385845 to
05c45f7
Compare
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>
Xavier Fournet (xfournet)
force-pushed
the
issue/45-fips-140-3-validated-provider
branch
3 times, most recently
from
August 28, 2026 00:49
50dd1ac to
1fab0a5
Compare
Xavier Fournet (xfournet)
force-pushed
the
issue/45-fips-140-3-validated-provider
branch
6 times, most recently
from
August 28, 2026 01:04
4e3305f to
e27c844
Compare
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>
Xavier Fournet (xfournet)
force-pushed
the
issue/45-fips-140-3-validated-provider
branch
from
August 28, 2026 01:05
e27c844 to
fa9d27b
Compare
Xavier Fournet (xfournet)
deleted the
issue/45-fips-140-3-validated-provider
branch
August 28, 2026 16:34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
The images are published under new names:
filigran/alpine-python-fipsandfiligran/alpine-python-nodejs-fips. The changes below would break consumers tracking thelatesttag offiligran/python-fipsandfiligran/python-nodejs-fips, so those two keep being built — unchanged — fromlegacy/, 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
fips.so, so the libraries calling into it can be the packaged ones.python-nodejs-fipsderiving frompython-fips.openssl.cnfto the provider configuration alone. It carried a copy of the upstream template several releases behind the packaged one, includingess_cert_id_alg = sha1where Alpine hassha256.cryptographybuilt from source, and assert at build time that it links the system OpenSSL — a pre-builtmusllinuxwheel would carry its own and sit outside the boundary.npm,yarn, and the Rust toolchain, which is now transient.main. Renovate automerges dependency bumps here, so an unbuildable bump could otherwise reachmainunverified.Dockerfile, which asserts those versions at build time, so a tag cannot drift from the content it describes. This also fixesnodejs22, which the published image contradicted.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 tolegacy/so the rootDockerfileis left alone.FIPS.md, align the README andrenovate.json5with 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.
The build is itself a test: it fails if the active provider is not the expected version, if MD5 is accepted, or if
cryptographyis not linked against the system OpenSSL.Checklist
type(scope?): description (#issue)Verification
Both targets built from this
Dockerfile, on Alpine 3.23.5:The module integrity check was verified by flipping one byte inside
fips.so: the provider then disappears fromopenssl list -providers, cryptographic operations fail withinner_evp_generic_fetch:unsupported, and Node.js reportsOpenSSL 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.mdstates both, and lists the limits worth knowing such ashashlib.md5bypassing OpenSSL.Impact on derived images, and the reason for the rename.
npmandyarnare no longer installed, and neither is a build toolchain: an image that compiles native wheels must bring its own.pipandcryptographyremain, and the PEP 668 marker is removed sopip installkeeps working. Anything installing crypto from a pre-built wheel or a statically linked binary falls outside the module boundary — usepip install --no-binary cryptographyfor that package.Python minor version now follows Alpine (3.23 → 3.12.14). Decoupling it would require reintroducing a CPython build.
Renovate and Dependabot.
vulnerabilityAlertsis now disabled andprHourlyLimitraised to 100, both playbook-mandatory.OPENSSL_FIPS_VERSIONis deliberately left unmanaged. Dependabot alerts have been enabled on the repository; security updates stay disabled.cryptographyis 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
mainrather than on alegacybranch.CircleCI leftovers removed from the repository: the webhook and the deploy key were still active and reporting a failing
CircleCI Pipelinestatus on every commit.🤖 Generated with Claude Code