- SLSA Level 3 Compliance: Generates non-forgeable provenance using the official
slsa-framework/slsa-github-generator. - Keyless Signing: Automatic image signing via Cosign using GitHub OIDC. No key management is required.
- Transparency: Signatures are recorded in the Rekor public transparency log.
- SBOM Generation: Optionally generates a Software Bill of Materials (SBOM) attestation.
- Multi-Registry Support: Works with Docker Hub, GHCR, and GCR.
- Enterprise Ready: Uses security best practices, including non-root users and reproducible builds.
This reusable workflow automates the secure build and publishing process:
- Build: Docker image build with BuildKit optimizations.
- Push: Push to the container registry with generated tags.
- SBOM: Generate SBOM (optional).
- Provenance: Generate SLSA L3 provenance in an isolated job.
- Sign: Keyless signing with Cosign.
- Publish: Attach attestations (provenance, SBOM) to the registry.
A job utilizing the workflow:
jobs:
build:
permissions:
contents: read
packages: write
id-token: write # Required for keyless signing/provenance
actions: read
uses: fystack/slsa-workflows/.github/workflows/docker-build-slsa.yml@main
with:
image-name: myorg/myapp
context-path: .
dockerfile: Dockerfile
registry: dockerhub
secrets:
registry-username: ${{ secrets.DOCKER_USERNAME }}
registry-password: ${{ secrets.DOCKER_TOKEN }}| Input | Required | Description |
|---|---|---|
image-name |
Yes | Full Docker image name (e.g., fystack/apex-api). |
context-path |
Yes | Build context path relative to repository root. |
dockerfile |
Yes | Path to Dockerfile relative to context. |
platforms |
No | Target platforms (comma-separated, default: linux/amd64). |
enable-sbom |
No | Generate SBOM attestation (default: true). |
registry |
No | Container registry: dockerhub, ghcr, or gcr (default: dockerhub). |
| Secret | Required | Description |
|---|---|---|
registry-username |
Yes | Registry username (Docker Hub username, GitHub actor, etc.). |
registry-password |
Yes | Registry password or token. |
The primary function of SLSA provenance is to create an unforgeable record that details how, when, and from what source code an artifact (Docker image) was built. This provides the highest level of assurance against tampering and injection attacks.
This workflow enforces a three-layer security model:
| Layer | Verification Method | What It Proves | Attack Scenario Stopped |
|---|---|---|---|
| 1. Digest | Pull by digest (image@sha256:...) |
The specific image contents have not changed after the build. | Image tampering after push to registry. |
| 2. Signature | cosign verify |
The image was built by the trusted CI/CD workflow (signer). | Unauthorized builds or compromised registry pushing unsigned images. |
| 3. Provenance (Commit) | Compare commit in provenance vs. git tag | The image was built from the expected source code commit. | Code injection before the build that would produce a signed image from unexpected source code. This is the critical defense against supply chain attacks. |
Verification is essential to complete the security chain.
We provide modular verification scripts for easy validation:
./scripts/verify-image.sh fystack/apex-rescanner:v0.1.8 fystack/apexEach verification can be run independently:
# 1. Verify signature only
./scripts/verify-signature.sh fystack/apex-rescanner:v0.1.8
# 2. Verify provenance only
./scripts/verify-provenance.sh fystack/apex-rescanner:v0.1.8
# 3. Verify SBOM only
./scripts/verify-sbom.sh fystack/apex-rescanner:v0.1.8
# 4. Verify Rekor transparency log
./scripts/verify-rekor.sh <log_index> fystack/apex-rescanner:v0.1.8Proves the image was signed by the official SLSA workflow.
cosign verify \
--certificate-identity-regexp='https://github.com/fystack/slsa-workflows/.github/workflows/docker-build-slsa.yml@.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
myorg/myapp:v1.0.0Proves the image was built from specific source code, not tampered with.
cosign verify-attestation \
--type slsaprovenance \
--certificate-identity-regexp='https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
myorg/myapp:v1.0.0 \
| jq '.payload | @base64d | fromjson'This step compares the commit SHA recorded in the image's provenance with the expected commit for the given Git tag. If they don't match, malicious code was injected before the build process.
# 1. Extract the commit SHA from the image's SLSA provenance
ACTUAL_COMMIT=$(cosign verify-attestation \
--type slsaprovenance \
--certificate-identity-regexp='https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
myorg/myapp:v1.0.0 2>/dev/null \
| jq -r '.payload | @base64d | fromjson | .predicate.invocation.configSource.digest.sha1')
# 2. Get the expected commit SHA for the tag from your source control
EXPECTED_COMMIT=$(git rev-parse v1.0.0)
# 3. Compare
if [ "$ACTUAL_COMMIT" != "$EXPECTED_COMMIT" ]; then
echo "❌ WARNING: Code injection detected! Expected: $EXPECTED_COMMIT, Actual: $ACTUAL_COMMIT"
exit 1
else
echo "✅ Commit verified - no code injection"
fiLists all packages and dependencies in the image for vulnerability scanning.
cosign verify-attestation \
--type spdx \
--certificate-identity-regexp='https://github.com/fystack/slsa-workflows/.github/workflows/docker-build-slsa.yml@.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
myorg/myapp:v1.0.0 \
| jq '.payload | @base64d | fromjson | .predicate'For the workflow to meet SLSA L3 and ensure build reproducibility, Dockerfiles must follow best practices:
- Multi-Stage Builds: Separate build and runtime environments.
- Pin Base Images by Digest: Use
FROM image:tag@sha256:...to guarantee the same base image is used every time. - Non-Root User: Use
USERto run the application as a non-root user for reduced privilege. - BuildKit Cache Mounts: Use
--mount=type=cache,target=/pathfor optimized and reproducible caching.