Signed Releases¶
Bindy releases are cryptographically signed using Cosign with keyless signing (Sigstore). This ensures:
- Authenticity: Verify that releases come from the official Bindy GitHub repository
- Integrity: Detect any tampering with release artifacts
- Non-repudiation: Cryptographic proof that artifacts were built by official CI/CD
- Transparency: All signatures are recorded in the Sigstore transparency log (Rekor)
What Is Signed¶
Every Bindy release includes signed artifacts (ADR-0010):
- Container Images (release tags only; PR and
mainimages are not signed): ghcr.io/firestoned/bindy:v*(Chainguard base, default)-
ghcr.io/firestoned/bindy:v*-distroless(Google Distroless base, alternative) -
Binary Tarballs:
bindy-linux-amd64.tar.gz,bindy-linux-arm64.tar.gzbindy-macos-amd64.tar.gz,bindy-macos-arm64.tar.gz-
bindy-windows-amd64.tar.gz -
Signature Artifacts (uploaded to releases):
*.tar.gz.bundle- Cosign signature bundles for binaries-
Container signatures are stored in the OCI registry
-
SBOMs (CycloneDX JSON, one per shipped artifact):
bindy-<os>-<arch>.cdx.jsonfor each binary tarballbindy-image-chainguard.cdx.json,bindy-image-distroless.cdx.jsonfor the images-
Each is also a signed attestation bound to its artifact's digest
-
SLSA Build L3 provenance:
<version>.intoto.jsonlcovering the tarballs, install manifests and SBOMs- Image provenance pushed to GHCR next to each image
Every signature and attestation names the release workflow as signer:
https://github.com/firestoned/bindy/.github/workflows/build.yaml@refs/tags/<version>.
Releases before v0.6.0 were signed by release.yaml instead of build.yaml.
The examples below anchor on the signer, so a fork or a similarly named
repository cannot pass verification:
SIGNER='^https://github\.com/firestoned/bindy/\.github/workflows/build\.yaml@refs/tags/'
ISSUER='https://token.actions.githubusercontent.com'
# Releases before v0.6.0:
# SIGNER='^https://github\.com/firestoned/bindy/\.github/workflows/release\.yaml@refs/tags/'
Installing Cosign¶
To verify signatures, install Cosign:
# macOS
brew install cosign
# Linux (download binary)
LATEST_VERSION=$(curl -s https://api.github.com/repos/sigstore/cosign/releases/latest | grep tag_name | cut -d '"' -f 4)
curl -Lo cosign https://github.com/sigstore/cosign/releases/download/${LATEST_VERSION}/cosign-linux-amd64
chmod +x cosign
sudo mv cosign /usr/local/bin/
# Verify installation
cosign version
Verifying Container Images¶
Cosign uses keyless signing with Sigstore, which means: - No private keys to manage or distribute - Signatures are verified against the GitHub Actions OIDC identity - All signatures are logged in the public Rekor transparency log
Quick Verification¶
# Verify a release (Chainguard)
cosign verify \
--certificate-identity-regexp="$SIGNER" \
--certificate-oidc-issuer="$ISSUER" \
ghcr.io/firestoned/bindy:v0.8.0
# Verify the Distroless variant
cosign verify \
--certificate-identity-regexp="$SIGNER" \
--certificate-oidc-issuer="$ISSUER" \
ghcr.io/firestoned/bindy:v0.8.0-distroless
# Or, from a checkout of this repository
make verify-image IMAGE_TAG=v0.8.0
Understanding the Verification Output¶
When verification succeeds, Cosign returns JSON output with signature details:
[
{
"critical": {
"identity": {
"docker-reference": "ghcr.io/firestoned/bindy"
},
"image": {
"docker-manifest-digest": "sha256:abcd1234..."
},
"type": "cosign container image signature"
},
"optional": {
"Bundle": {
"SignedEntryTimestamp": "...",
"Payload": {
"body": "...",
"integratedTime": 1234567890,
"logIndex": 12345678,
"logID": "..."
}
},
"Issuer": "https://token.actions.githubusercontent.com",
"Subject": "https://github.com/firestoned/bindy/.github/workflows/build.yaml@refs/tags/v0.1.0"
}
}
]
Key fields to verify: - Subject: Shows the exact GitHub workflow that created the signature - Issuer: Confirms it came from GitHub Actions - integratedTime: Unix timestamp when signature was created - logIndex: Entry in the Rekor transparency log (publicly auditable)
Verification Failures¶
If verification fails, you'll see an error like:
Do NOT use unverified images in production. This indicates: - The image was not signed by the official Bindy release workflow - The image may have been tampered with - The image may be a counterfeit
Verifying Binary Releases¶
Binary tarballs are signed with Cosign blob signing. Each release includes .bundle files containing the signature.
Download and Verify¶
# Download the binary tarball and signature bundle from GitHub Releases
VERSION="v0.8.0"
PLATFORM="linux-amd64" # or linux-arm64, macos-amd64, macos-arm64, windows-amd64
# Download tarball
curl -LO "https://github.com/firestoned/bindy/releases/download/${VERSION}/bindy-${PLATFORM}.tar.gz"
# Download signature bundle
curl -LO "https://github.com/firestoned/bindy/releases/download/${VERSION}/bindy-${PLATFORM}.tar.gz.bundle"
# Verify the signature
cosign verify-blob \
--bundle "bindy-${PLATFORM}.tar.gz.bundle" \
--certificate-identity-regexp="$SIGNER" \
--certificate-oidc-issuer="$ISSUER" \
"bindy-${PLATFORM}.tar.gz"
Verification Success¶
If successful, you'll see:
You can now safely extract and use the binary:
Automated Verification Script¶
Create a script to download and verify releases automatically:
#!/bin/bash
set -euo pipefail
VERSION="${1:-latest}"
PLATFORM="${2:-linux-amd64}"
SIGNER='^https://github\.com/firestoned/bindy/\.github/workflows/build\.yaml@refs/tags/'
if [ "$VERSION" = "latest" ]; then
VERSION=$(curl -s https://api.github.com/repos/firestoned/bindy/releases/latest | grep tag_name | cut -d '"' -f 4)
fi
echo "Downloading Bindy $VERSION for $PLATFORM..."
# Download artifacts
curl -LO "https://github.com/firestoned/bindy/releases/download/${VERSION}/bindy-${PLATFORM}.tar.gz"
curl -LO "https://github.com/firestoned/bindy/releases/download/${VERSION}/bindy-${PLATFORM}.tar.gz.bundle"
# Verify signature
echo "Verifying signature..."
cosign verify-blob \
--bundle "bindy-${PLATFORM}.tar.gz.bundle" \
--certificate-identity-regexp="$SIGNER" \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
"bindy-${PLATFORM}.tar.gz"
# Extract
echo "Extracting..."
tar xzf "bindy-${PLATFORM}.tar.gz"
echo "✓ Bindy $VERSION successfully verified and installed"
./bindy --version
Additional Security Verification¶
Check SHA256 Checksums¶
Every release includes a checksums.sha256 file with SHA256 hashes of all artifacts:
# Download checksums
curl -LO "https://github.com/firestoned/bindy/releases/download/${VERSION}/checksums.sha256"
# Verify the tarball checksum
sha256sum -c checksums.sha256 --ignore-missing
Inspect Rekor Transparency Log¶
All signatures are recorded in the public Rekor transparency log:
# Search for Bindy signatures
rekor-cli search --email noreply@github.com --rekor_server https://rekor.sigstore.dev
# Or use the web interface:
# https://search.sigstore.dev/?email=noreply@github.com
Verify SLSA Build L3 Provenance¶
Every release file and image has SLSA v1.0
Build L3 provenance from the isolated slsa-github-generator workflows. Verify
it with slsa-verifier
(make slsa-verifier-install):
# A tarball, install manifest or SBOM, against the release's provenance file
curl -LO "https://github.com/firestoned/bindy/releases/download/${VERSION}/${VERSION#v}.intoto.jsonl"
slsa-verifier verify-artifact "bindy-${PLATFORM}.tar.gz" \
--provenance-path "${VERSION#v}.intoto.jsonl" \
--source-uri github.com/firestoned/bindy \
--source-tag "${VERSION}"
# An image: always by digest, never by tag
DIGEST=$(crane digest ghcr.io/firestoned/bindy:${VERSION})
slsa-verifier verify-image "ghcr.io/firestoned/bindy@${DIGEST}" \
--source-uri github.com/firestoned/bindy \
--source-tag "${VERSION}"
# The same checks, from a checkout of this repository
make verify-provenance ARTIFACT=bindy-${PLATFORM}.tar.gz VERSION=${VERSION}
make verify-image-provenance IMAGE=ghcr.io/firestoned/bindy@${DIGEST} VERSION=${VERSION}
Verify the SBOM¶
Each SBOM is a signed attestation bound to the artifact it describes, so you can prove the SBOM you read is the one generated for the binary or image you run:
# Binary: the SBOM attestation for this exact tarball
gh attestation verify "bindy-${PLATFORM}.tar.gz" --repo firestoned/bindy \
--predicate-type https://cyclonedx.org/bom \
--signer-workflow firestoned/bindy/.github/workflows/build.yaml
# Image: the attestation is also stored in GHCR next to the image
gh attestation verify "oci://ghcr.io/firestoned/bindy@${DIGEST}" --repo firestoned/bindy \
--predicate-type https://cyclonedx.org/bom
# Or
make verify-sbom-attestation ARTIFACT=bindy-${PLATFORM}.tar.gz
The binary SBOMs list the operator's full Rust dependency tree. The image SBOMs list the base image's OS packages; Syft cannot see inside a Rust binary, so for the operator's own dependencies read the binary SBOM of the same release.
Kubernetes Deployment Verification¶
Three complementary layers, in the order most deployments adopt them:
- FluxCD (GitOps) — verify the deployment source (signed OCI artifact) before anything is applied.
- ValidatingAdmissionPolicy — enforce image provenance (trusted registries, immutable references) with zero extra controllers. Ships with bindy.
- Kyverno / sigstore policy-controller — cryptographic signature verification at pod admission (CEL cannot verify signatures, so this layer needs a verifying controller).
FluxCD (GitOps) Verification¶
Flux verifies Cosign signatures on the OCI sources it pulls
(OCIRepository / HelmChart) — tampered or unsigned artifacts are never
applied. Note Flux does not verify container-image signatures at pod
admission; combine it with the layers below.
Wrap bindy's released manifests in an OCI artifact you sign, and let Flux refuse anything unsigned:
# One-time (or per bindy upgrade): package + sign the manifests you deploy
VERSION=v0.6.0
curl -sSLO https://github.com/firestoned/bindy/releases/download/${VERSION}/install.yaml
flux push artifact oci://ghcr.io/<your-org>/bindy-manifests:${VERSION} \
--path=. --source="github.com/firestoned/bindy" --revision="${VERSION}"
cosign sign ghcr.io/<your-org>/bindy-manifests:${VERSION}
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: bindy-manifests
namespace: flux-system
spec:
interval: 10m
url: oci://ghcr.io/<your-org>/bindy-manifests
ref:
tag: v0.6.0
verify:
provider: cosign
# Keyless: pin the signing identity. For artifacts signed in YOUR CI,
# match your workflow; for interactive `cosign sign`, match your OIDC
# identity via a secretRef / matchOIDCIdentity as appropriate.
matchOIDCIdentity:
- issuer: "https://token.actions.githubusercontent.com"
subject: "https://github.com/<your-org>/<your-repo>/.github/workflows/.*"
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: bindy
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: OCIRepository
name: bindy-manifests
path: ./
prune: true
With spec.verify set, Flux marks the source SourceVerifiedFailed and
refuses to reconcile if the signature is missing or does not match the pinned
identity.
ValidatingAdmissionPolicy (no extra controller)¶
Bindy ships an image-provenance policy —
deploy/admission-policies/15-bindy-image-provenance-policy.yaml
— enforced by the API server itself (Kubernetes 1.30+, CEL; no webhook or
controller to operate):
- bindcar sidecar overrides must come from
ghcr.io/firestoned/ - BIND9 operand overrides must be
internetsystemsconsortium/bind9or aghcr.io/firestoned/mirror - every override must be immutable: digest-pinned (
@sha256:) or a named non-:latesttag
kubectl apply -f deploy/admission-policies/
# or the combined release artifact:
kubectl apply -f https://github.com/firestoned/bindy/releases/latest/download/admission-policies.yaml
Scope: CEL cannot perform cryptographic verification — this policy pins where images come from and that references are immutable, not who signed them. For signature verification at admission, add Kyverno or the sigstore policy-controller below.
Kyverno Policy Example¶
When deploying to Kubernetes, use policy-controller or Kyverno to enforce signature verification:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-bindy-images
spec:
validationFailureAction: enforce
background: false
rules:
- name: verify-bindy-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/firestoned/bindy*"
attestors:
- entries:
- keyless:
# Releases are signed by the consolidated build.yaml
# workflow. Releases published before the consolidation
# were signed by the former release.yaml identity.
subject: "https://github.com/firestoned/bindy/.github/workflows/build.yaml@refs/tags/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
This policy ensures: - Only signed Bindy images can run in the cluster - Signatures must come from the official release workflow - Signatures are verified against the Rekor transparency log
Troubleshooting¶
"Error: no matching signatures"¶
Cause: Image/artifact is not signed or signature doesn't match the identity.
Solution:
- Verify you're using an official release from ghcr.io/firestoned/bindy*
- Check the tag/version exists on the GitHub releases page
- Ensure you're not using a locally-built image
"Error: unable to verify bundle"¶
Cause: Signature bundle is corrupted or doesn't match the artifact.
Solution:
- Re-download the artifact and bundle
- Verify the SHA256 checksum matches checksums.sha256
- Report the issue if checksums match but verification fails
"Error: fetching bundle: context deadline exceeded"¶
Cause: Network issue connecting to Sigstore services.
Solution:
- Check your internet connection
- Verify you can reach https://rekor.sigstore.dev and https://fulcio.sigstore.dev
- Try again with increased timeout: COSIGN_TIMEOUT=60s cosign verify ...
Security Contact¶
If you discover a security issue with signed releases:
- DO NOT open a public GitHub issue
- Report to: security@firestoned.io
- Include: artifact name, version, verification output, and steps to reproduce
See SECURITY.md for our security policy and vulnerability disclosure process.
SPDX License Headers¶
All Bindy source files include SPDX license identifiers for automated license compliance tracking.
What is SPDX?¶
SPDX (Software Package Data Exchange) is an ISO standard (ISO/IEC 5962:2021) for communicating software license information. SPDX identifiers enable:
- Automated SBOM generation: Tools like
cargo-cyclonedxdetect licenses automatically - License compliance auditing: Verify no GPL contamination in this Apache-2.0-licensed project
- Supply chain transparency: Clear license identification at file granularity
- Tooling integration: GitHub, Snyk, Trivy, and other tools recognize SPDX headers
Required Header Format¶
All source files MUST include SPDX headers in the first 10 lines:
Rust files (.rs):
Shell scripts (.sh, .bash):
#!/usr/bin/env bash
# Copyright (c) 2025 Erick Bourgeois, firestoned
# SPDX-License-Identifier: Apache-2.0
Makefiles (Makefile, *.mk):
GitHub Actions workflows (.yaml, .yml):
# Copyright (c) 2025 Erick Bourgeois, firestoned
# SPDX-License-Identifier: Apache-2.0
name: My Workflow
Automated Verification¶
Bindy enforces SPDX headers via CI/CD:
Workflow: .github/workflows/license-check.yaml
Checks:
- All Rust files (.rs)
- All Shell scripts (.sh, .bash)
- All Makefiles (Makefile, *.mk)
- All GitHub Actions workflows (.yaml, .yml)
Enforcement:
- Runs on every pull request
- Runs on every push to main
- Pull requests fail if any source files lack SPDX headers
- Provides clear error messages with examples for missing headers
Output Example:
✅ All 347 source files have SPDX license headers
File types checked:
- Rust files (.rs)
- Shell scripts (.sh, .bash)
- Makefiles (Makefile, *.mk)
- GitHub Actions workflows (.yaml, .yml)
License: Apache-2.0¶
Bindy is licensed under the Apache License 2.0, a permissive license with an express patent grant.
Permissions: - ✅ Commercial use - ✅ Modification - ✅ Distribution - ✅ Private use
Conditions: - 📋 Include license text and NOTICE file - 📋 State significant changes
Limitations: - ❌ No liability - ❌ No warranty
Full license text: LICENSE
Compliance Evidence¶
SOX 404 (Sarbanes-Oxley): - Control: License compliance and intellectual property tracking - Evidence: All source files tagged with SPDX identifiers, automated verification - Audit Trail: Git history shows when SPDX headers were added
PCI-DSS 6.4.6 (Payment Card Industry): - Requirement: Code review and approval processes - Evidence: SPDX verification blocks unapproved code (missing headers) from merging - Automation: CI/CD enforces license compliance before code review
SLSA / SBOM (Supply Chain Security): - Requirement: Dependency inventory and license data for every shipped artifact - Evidence: SPDX headers give the SBOMs machine-readable license data for bindy's own crates; provenance itself is covered under Verify SLSA Build L3 Provenance - Transparency: Every dependency's license is machine-readable