A collection of enterprise-grade, zero-CVE, FIPS 140-3 compliant container base images built upon Chainguard's Wolfi OS undistro. Designed for zero-trust security postures, regulated industries (FedRAMP, DoD, HIPAA, PCI-DSS), and high-security microservice architectures.
| Runtime | Versions | Flavors Available | FIPS 140-3 Compliant | Base OS |
|---|---|---|---|---|
| OpenJDK | 8, 17, 21, 25 (LTS) |
dev, standard, distroless |
β | Wolfi Linux + BCFIPS |
| OpenSSL | 3.5.9 |
dev, standard, distroless |
β | Wolfi Linux + FIPS Module |
| Node.js FIPS | 22, 24 |
dev, standard, distroless |
β | Wolfi Linux + OpenSSL FIPS Module |
| Node.js | 18, 20, 22, 24, 26 |
dev, prod |
Optional | Wolfi Linux |
| Go | 1.22, 1.23, 1.24 |
dev, prod |
Optional | Wolfi Linux |
| Python | 3.10, 3.11, 3.12, 3.13, 3.14 |
dev, prod |
Optional | Wolfi Linux |
| Bun | 1 |
dev, prod |
Optional | Wolfi Linux |
All images are built directly on Wolfi Linux, a declarative Linux undistro optimized for container security:
- Zero Shell / Distroless: Production containers (
prod/distroless) ship with no shell (sh/bash) and no package manager (apk). - Non-Root Execution: All production runtime targets execute strictly under non-privileged UID/GID
65532:65532. - Rapid Patching: Wolfi rolling packages remediate upstream CVEs within hours of public disclosure.
- OpenJDK FIPS: Powered by Eclipse Adoptium OpenJDK 8, 17, 21 and 25 and Bouncy Castle FIPS (
bc-fips,bcpkix-fips,bctls-fips). Overrides JVM security configuration (conf/security/java.security) to force BCFIPS as Provider 1, BCFKS as default keystore, and SP 800-90A DRBG entropy. - OpenSSL FIPS: Built from source with
--enable-fips. Strictopenssl.cnfpolicy locks down legacy algorithms (MD5, DES, 3DES, RC4) and permits only approved cryptographic mechanisms. - Node.js FIPS: Wolfi's Node.js links the system OpenSSL, which loads the validated OpenSSL FIPS 3.1.2 provider from the OpenSSL FIPS image.
fips=yesis the default property and--force-fips(viaNODE_OPTIONS) stops application code from callingcrypto.setFips(false), so MD5, ChaCha20 and RSA-1024 are rejected. Every build runs/usr/share/nodejs-fips/fips-check.jsand fails if FIPS mode is not enforced.
- SLSA Provenance: Cryptographically signed attestations generated via GitHub Actions OIDC and published to the GitHub Container Registry (GHCR).
- CycloneDX SBOMs: Every image contains a machine-readable Software Bill of Materials (SBOM) generated in CycloneDX JSON format.
- Continuous Scanning: Integrated Trivy vulnerability scanning, CIS Docker benchmark audits, and license compliance checks.
# OpenJDK 21 FIPS (Production Distroless)
docker pull ghcr.io/taha2samy-3/wolfi-openjdk-fips:21-distroless
# OpenSSL 3.5 FIPS (Production Distroless)
docker pull ghcr.io/taha2samy-3/openssl-fips:3.5.9-distroless
# Node.js 22 (Production)
docker pull ghcr.io/taha2samy-3/node:22
# Node.js 24 FIPS (Production Distroless)
docker pull ghcr.io/taha2samy-3/node-fips:24-distrolessThis repository utilizes Docker Buildx Bake (docker-bake.hcl) for multi-platform, declarative container builds:
# Build all OpenJDK FIPS targets (8, 17, 21, 25), or a single one
docker buildx bake openjdk
docker buildx bake openjdk-21-prod
# Build all OpenSSL FIPS targets
docker buildx bake openssl-dev openssl-standard openssl-prod
# Build all Node.js targets
docker buildx bake dev-22 prod-22
# Build all Node.js FIPS targets (needs the OpenSSL FIPS image)
docker buildx bake node-fips
# Build the entire default group across all runtimes
docker buildx bakeEvery FIPS image has a test suite. CI runs it on amd64 and arm64 after each build and on every pull request that changes the image, and the results appear on the dashboard's FIPS tests tab:
| Image | Suite | What it checks |
|---|---|---|
| OpenSSL FIPS | openssl/tests/ |
Provider status and self-tests, approved / rejected digests, ciphers, MACs, KDFs, key sizes and curves, PQC, TLS |
| OpenJDK FIPS | openjdk/tests/ |
BCFIPS provider order, approved-only mode, rejected algorithms, BCFKS keystores, TLS 1.2 / 1.3, DRBG |
| Node.js FIPS | nodejs/fips/tests/ |
crypto.getFips(), setFips(false) blocked, rejected digests / ciphers / curves / scrypt, RSA sizes, HMAC, PBKDF2, TLS 1.3 handshake, non-root and distroless checks |
# Run a suite the way CI does, against every flavor of one image (pull or build the images first)
pip install pytest allure-pytest pyyaml
python .github/scripts/fips_tests.py --unit openssl-3.5 --arch amd64 --out fips-results
python .github/scripts/fips_tests.py --unit openjdk-21 --arch amd64 --out fips-results
python .github/scripts/fips_tests.py --unit node-fips-24 --arch amd64 --out fips-results
# Or call pytest directly against one image
pytest openssl/tests -m "not network" --image ghcr.io/taha2samy-3/openssl-fips:3.5.9
pytest nodejs/fips/tests --image ghcr.io/taha2samy-3/node-fips:24-distroless --flavor prodA few OpenSSL tests are marked as strict expected failures. They cover known limits of the certified OpenSSL FIPS provider 3.1.2 (see openssl/README.md), and the run fails if one of them starts passing.
Every change rebuilds, re-attests, rescans and republishes on the dashboard only the images it affects:
.github/scripts/plan_images.pygives every bake target a fingerprint: its resolved bake definition, the Dockerfile stages it builds, the local files those stages copy, and the fingerprints of images it consumes (Node.js FIPS consumes the OpenSSL FIPS image).- Build and Push Images compares that fingerprint with the
io.github.taha2samy-3.build.fingerprintlabel of the published image and rebuilds only targets that differ or are not published yet. A build that failed or was cancelled is retried on the next run. - Generate Security Dashboard and Deploy scans only the rebuilt images (plus any image without a stored report) and keeps the last reports of everything else, which are published with the site under
reports/.
Examples: bumping NODE_22_FULL_VERSION rebuilds node:22, node:22-dev and node-fips:22*; editing openjdk/conf/java-8.security rebuilds the three Java 8 images; editing a README rebuilds nothing.
Both workflows can be run by hand: Build and Push Images takes rebuild (changed, all, or ids such as node-22 openjdk) and the dashboard workflow takes rescan (missing, all, or ids).
Each image is built twice, natively and without QEMU: on ubuntu-24.04 for linux/amd64 and on ubuntu-24.04-arm for linux/arm64. Both builds push by digest, and .github/scripts/merge_manifests.py joins them into one multi-platform tag, then checks that both platforms are present. Trivy scans, CIS checks, SBOM attestations and FIPS tests run once per architecture, and the dashboard has an amd64 / arm64 switch. Provenance is attached to the multi-platform index.
.github/dependencies.yml lists every pinned version and checksum in docker-bake.hcl and says how to update it:
- Certified FIPS modules: the OpenSSL FIPS Provider 3.1.2 (CMVP certificate #4985) and Bouncy Castle
bc-fips2.1.1 (certificate #4943). These never change automatically. Only the validated version may be used, so they change by hand when NIST lists a new certificate. - Everything else is updated by Daily Dependency Update (
.github/scripts/update_dependencies.py): Wolfi packages (including every package installed in the OpenSSL images), base images by digest, the OpenSSL 3.5 LTS core source and its SHA-256, Eclipse Temurin JDK / JRE downloads and checksums, the Bouncy Castle TLS / utility jars (checked against Maven Central's.sha256), and the envoke release.
The script opens one pull request per image family (runtimes, openssl-fips, openjdk-fips, base) on the branch automation/deps-<group>, and handles it the way Dependabot does: an open PR is updated in place, a closed one is replaced by a fresh PR, and a group with no updates has its PR closed. Each PR lists the version changes and the images it will rebuild. Nothing is merged automatically.
The run also checks coverage, and fails if docker-bake.hcl gains a pinned variable (*_VER, *_VERSION, *_SHA, *_SHA256, *_URL, *_IMAGE) that dependencies.yml does not list. Run it locally with:
python .github/scripts/update_dependencies.py --dry-run # every group
python .github/scripts/update_dependencies.py --dry-run --group openjdk-fips # one groupPull Request Check builds the images a pull request changes (linux/amd64, nothing is pushed) and runs the FIPS test suites against them. Make it a required status check in the main branch protection rules, so that a dependency update cannot be merged while it breaks an image or its FIPS behaviour.
- FIPS Benchmarks runs every week on both architectures. It measures each FIPS image against a non-FIPS baseline of the same software: stock OpenSSL on Alpine, this repository's regular Node.js image, and Eclipse Temurin with SunJCE. Results appear on the dashboard's Benchmarks tab, where the FIPS / baseline ratio is the number to compare.
- FIPS Certificate Watch (
.github/scripts/cmvp_watch.py) checks the CMVP certificates every week. It opens or updates an issue labelledfips-certificatesif a certificate is no longer Active, is less than a year from its sunset date, no longer lists the pinned version, or if the vendor has a newer certificate. The dashboard's FIPS module tab shows the same status.
The repository includes a modern web-based compliance dashboard built with React, Vite, and Tailwind-inspired styling located under web/:
cd web
npm install
npm run devThe dashboard shows, for each image and each architecture (amd64 / arm64):
- Vulnerability posture and CVE breakdown.
- CIS benchmark pass/fail scores.
- Interactive SBOM package explorer.
- SLSA provenance and SBOM attestations, with the
gh attestation verifycommands. - The FIPS module, its CMVP certificate status and sunset date.
- FIPS test results and benchmark results against non-FIPS baselines.
βββ .github/
β βββ dependencies.yml # Every pinned version: certified FIPS modules vs. automatically updated
β βββ workflows/
β β βββ build.yml # Native amd64 + arm64 builds, multi-platform merge, attestation, FIPS tests
β β βββ docs.yml # Security scans, CIS checks and the dashboard deployment
β β βββ pr-check.yml # Builds and tests the images a pull request changes
β β βββ daily-image-update.yml # Daily dependency update pull requests
β β βββ benchmarks.yml # Weekly FIPS vs. non-FIPS benchmarks
β β βββ fips-certificates.yml # Weekly CMVP certificate watch
β βββ actions/ # bake-build, trivy-scan, attest-image, upload-dependency-graph
β βββ scripts/
β βββ plan_images.py # Decides which images to build, test, attest and scan
β βββ merge_manifests.py # Joins per-architecture digests into multi-platform tags
β βββ update_dependencies.py # Resolves updates and manages the dependency PRs
β βββ fips_tests.py # Runs the FIPS test suite of an image and writes the dashboard report
β βββ run_benchmarks.py # FIPS vs. baseline benchmarks (bench/ holds the Node.js and Java programs)
β βββ cmvp_watch.py # Checks the CMVP certificates of the FIPS modules
βββ openjdk/ # Wolfi OpenJDK FIPS 140-3 images
β βββ dockerfile # Multi-stage Wolfi + BCFIPS build
β βββ conf/java-<v>.security # Hardened FIPS 140-3 security policy per Java version
β βββ render_security.py # Renders conf/java-<v>.security from templates/
β βββ tests/ # pytest & JUnit verification suite
β βββ README.md # OpenJDK module documentation
βββ nodejs/ # Node.js images (regular + FIPS stages)
β βββ dockerfile # dev / prod and fips-dev / fips-standard / fips-distroless
β βββ fips/ # OpenSSL FIPS config, build-time FIPS check and tests/
βββ openssl/ # Wolfi OpenSSL FIPS 140-3 images
β βββ dockerfile # Source build with enable-fips, checksum-verified sources
β βββ conf/openssl.cnf # Hardened FIPS provider configuration
β βββ tests/ # pytest FIPS verification suite
β βββ README.md # OpenSSL module documentation
βββ go/, python/, bun/ # Regular runtime images
βββ web/ # React/Vite security dashboard
β βββ src/ # Dashboard pages and components
β βββ runtimes.yaml # Runtime metadata and report mappings
βββ docker-bake.hcl # All bake targets and pinned versions
βββ README.md # Project root documentation
Licensed under the Apache License, Version 2.0. See LICENSE for details.