- Executive Summary
- Quick Start
- Architecture
- Implementation Guide
- GitHub Actions Integration
- Multi-KMS Support
- Air-Gap Bundle Export
- SBOM and Security Attestations
- Enterprise Features
- Policy Examples
- Troubleshooting
- Production Checklist
- Resources
Witness provides cryptographic attestation for software supply chains, ensuring package integrity and build provenance through in-toto attestations. This guide covers RPM signing, keyless authentication, air-gap deployments, and enterprise integration patterns.
Key Features:
- ✅ RPM attestation without GPG dependency
- ✅ Keyless signing via Fulcio with TSA counter-signing
- ✅ Full offline verification for air-gapped environments
- ✅ Multi-KMS support (AWS, Azure, GCP, Vault)
- ✅ GitHub Actions integration via witness-run-action
- ✅ NIST compliance metadata generation
# Install Witness
curl -L https://github.com/testifysec/witness/releases/latest/download/witness_$(uname -s)_$(uname -m).tar.gz | tar -xz
sudo mv witness /usr/local/bin/
# Install Syft for SBOM generation
brew install syft # macOS
# or
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s
# Install RPM tools
brew install rpm # macOS
# or
sudo apt-get install rpm # Ubuntu/Debian
# or
sudo yum install rpm-build # RHEL/CentOS
# Verify installations
witness version
syft version
rpmbuild --version# Generate keys
openssl genpkey -algorithm ed25519 -out witness-key.pem
openssl pkey -in witness-key.pem -pubout > witness-pub.pem
# Create attestation during RPM build
witness run --step build \
-o attestation.json \
-a environment,material,command-run,product \
--signer-file-key-path witness-key.pem \
-- rpmbuild -bb package.spec
# Verify
witness verify -f package.rpm \
-a attestation.json \
-p policy.json \
-k witness-pub.pem- name: Build and Sign
uses: testifysec/witness-run-action@v1
with:
step: build
command: rpmbuild -bb package.specSource → Build Process → Attestation → Verification
↓ ↓ ↓
Materials Products Policy Check
Commands Signatures Trust Validation
Environment Timestamps Offline Support
Complete key-based workflow
#!/bin/bash
# 1. Generate ED25519 keys
openssl genpkey -algorithm ed25519 -out key.pem
openssl pkey -in key.pem -pubout > pub.pem
# 2. Get key ID
echo "{}" > config.yaml
witness run --step test -c config.yaml -o test.json \
-a environment --signer-file-key-path key.pem -- echo test
KEY_ID=$(jq -r '.signatures[0].keyid' test.json)
# 3. Create policy
PUB_KEY=$(base64 < pub.pem | tr -d '\n')
cat > policy.json << EOF
{
"expires": "2026-12-31T23:59:59Z",
"steps": {
"build": {
"attestations": [
{"type": "https://witness.dev/attestations/material/v0.1"},
{"type": "https://witness.dev/attestations/command-run/v0.1"},
{"type": "https://witness.dev/attestations/product/v0.1"}
],
"functionaries": [
{"type": "publickey", "publickeyid": "${KEY_ID}"}
]
}
},
"publickeys": {
"${KEY_ID}": {"keyid": "${KEY_ID}", "key": "${PUB_KEY}"}
}
}
EOF
# 4. Sign policy
witness sign -f policy.json --signer-file-key-path key.pem \
--outfile policy-signed.json
# 5. Build with attestation (with Archivista)
witness run --step build -c config.yaml -o attestation.json \
-a environment,material,command-run,product \
--enable-archivista \
--archivista-server https://archivista.testifysec.io \
--signer-file-key-path key.pem \
-- rpmbuild -bb package.spec
# 6. Verify
witness verify -f package.rpm -a attestation.json \
-p policy-signed.json -k pub.pemKey Insight: Uses TSA counter-signing instead of Rekor transparency logs for offline verification.
Fulcio keyless workflow
# Interactive (browser auth)
witness run --step build \
--signer-fulcio-url https://fulcio.sigstore.dev \
--signer-fulcio-oidc-issuer https://oauth2.sigstore.dev/auth \
--signer-fulcio-oidc-client-id sigstore \
--timestamp-servers "http://time.certum.pl" \
-o attestation.json \
-a environment,material,command-run,product \
-- rpmbuild -bb package.spec
# GitHub Actions (automatic OIDC)
# IMPORTANT: Requires permissions: id-token: write
witness run --step build \
--signer-fulcio-url https://fulcio.sigstore.dev \
--signer-fulcio-oidc-issuer https://token.actions.githubusercontent.com \
--signer-fulcio-oidc-client-id sigstore \
--timestamp-servers "http://time.certum.pl" \
--enable-archivista \
--archivista-server https://archivista.testifysec.io \
-o attestation.json \
-a environment,material,command-run,product \
-- rpmbuild -bb package.specCertificate Validity: 10 minutes (validated 2025-08-11)
TSA Servers (tested):
- ✅
http://time.certum.pl(recommended) - ✅
http://timestamp.digicert.com ⚠️ http://freetsa.org/tsr(variable)
The official GitHub Action provides zero-configuration attestation with automatic Sigstore and Archivista integration.
Live Example: View a working implementation at github.com/colek42/witness-action-test
name: Build with Attestation
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write # REQUIRED for Fulcio OIDC
contents: read # Access repository
packages: write # If publishing containers
steps:
- uses: actions/checkout@v3
- name: Test with Default Settings
uses: testifysec/witness-run-action@v1
with:
step: test
command: make test
# Default settings (no need to specify):
# enable-sigstore: true # Fulcio signing enabled
# enable-archivista: true # Storage enabled
# archivista-server: https://archivista.testifysec.io
- name: Build with Custom Archivista
uses: testifysec/witness-run-action@v1
with:
step: build
command: make build
archivista-server: https://archivista.internal.com # Optional: custom server
- name: Package without Archivista
uses: testifysec/witness-run-action@v1
with:
step: package
command: rpmbuild -bb package.spec
enable-archivista: false # Disable if needed (keeps attestation local only)
- name: Full Control Example
uses: testifysec/witness-run-action@v1
with:
step: release
command: make release
enable-sigstore: true # Explicitly enable (default)
enable-archivista: true # Explicitly enable (default)
archivista-server: https://archivista.testifysec.ioDefault Behavior:
- ✅
enable-sigstore: true- Automatic Fulcio signing via GitHub OIDC - ✅
enable-archivista: true- Automatic storage to public Archivista - ✅ FreeTSA timestamping included
- ✅ No configuration needed for standard use
Critical Requirements:
- Must set
permissions: id-token: writefor OIDC token - Sigstore requires internet access to Fulcio
- Archivista stores attestations publicly by default (use custom server for private)
Verified Example: See the complete workflow at witness-action-test/.github/workflows/witness-test.yml with successful run results
| KMS | Configuration | Air-Gap Support |
|---|---|---|
| Fulcio | --signer-fulcio-url https://fulcio.sigstore.dev |
✅ Yes (with TSA) |
| AWS KMS | --signer-kms-ref awskms:///arn:aws:kms:... |
|
| Azure | --signer-kms-ref azurekms://vault.azure.net/... |
|
| GCP | --signer-kms-ref gcpkms://projects/.../keys/... |
|
| Vault | --signer-kms-ref hashivault://key-name |
✅ Yes |
| File | --signer-file-key-path key.pem |
✅ Yes |
Create self-contained verification bundles for offline environments:
#!/bin/bash
# create-bundle.sh
BUNDLE="release-$(date +%Y%m%d)"
mkdir -p ${BUNDLE}/{attestations,policies,certs,artifacts}
# Copy artifacts and attestations
cp *.rpm ${BUNDLE}/artifacts/
cp *.json ${BUNDLE}/attestations/
# Export certificates
curl -s https://fulcio.sigstore.dev/api/v1/rootCert > ${BUNDLE}/certs/fulcio.pem
curl -s http://time.certum.pl/cert > ${BUNDLE}/certs/tsa.pem
# Create verification script
cat > ${BUNDLE}/verify.sh << 'EOF'
#!/bin/bash
for rpm in artifacts/*.rpm; do
witness verify -f "$rpm" \
-a "attestations/$(basename $rpm .rpm).json" \
-p policies/policy.json \
--verifier-fulcio-roots certs/fulcio.pem
done
EOF
chmod +x ${BUNDLE}/verify.sh
tar czf ${BUNDLE}.tar.gz ${BUNDLE}/
sha256sum ${BUNDLE}.tar.gz > ${BUNDLE}.tar.gz.sha256Witness integrates with Syft to generate Software Bill of Materials:
# Install Syft
brew install syft # or use curl installer
# Build RPM first
witness run --step build \
-o attestation-build.json \
-a environment,material,command-run,product \
--signer-file-key-path key.pem \
-- rpmbuild -bb package.spec
# Generate SBOM with attestation
witness run --step sbom \
-o attestation-sbom.json \
-a material,command-run,product,sbom \
--attestor-sbom-export \
--signer-file-key-path key.pem \
-- syft packages package.rpm -o spdx-json > sbom.jsonInclude secret scanning and vulnerability detection:
witness run --step security-scan \
-a material,command-run,product,secretscan,sarif \
--attestor-secretscan-fail-on-detection \
--attestor-secretscan-allowlist-regex "test.*" \
--signer-file-key-path key.pem \
-- make buildSee SBOM and Security Attestations Guide for complete documentation.
Centralized attestation storage with query capabilities:
# Enable during attestation (stores attestation remotely)
witness run --step build \
--enable-archivista \
--archivista-server https://archivista.testifysec.io \
--signer-file-key-path key.pem \
-o attestation.json \
-a environment,material,command-run,product \
-- make build
# Query attestations by subject digest
curl -X POST https://archivista.testifysec.io/query \
-H "Content-Type: application/json" \
-d '{"subject": {"digest": {"sha256": "abc123..."}}}'
# Self-hosted Archivista
docker run -d -p 8080:8080 \
ghcr.io/testifysec/archivista:latest
# Use with self-hosted
witness run --step build \
--enable-archivista \
--archivista-server http://localhost:8080 \
--signer-file-key-path key.pem \
-- make buildWitness attestations provide rich metadata that can be mapped to compliance controls:
# Extract compliance-relevant metadata
cat attestation.json | jq -r '.payload' | base64 -d | jq '{
build_time: .predicate.startTime,
builder: .predicate.environment.USER,
materials: [.predicate.materials[].uri],
products: [.predicate.products[].uri],
command: .predicate.command | join(" ")
}'TestifySec Enterprise: Automatically maps policy-as-code, configuration-as-code, and build/test metadata to NIST and other compliance frameworks, providing continuous compliance validation.
Transition from GPG to Witness:
# Phase 1: Dual signing
rpmsign --key-id KEY_ID package.rpm # Legacy GPG
witness run --step build ... -- echo "Already signed" # Add attestation
# Phase 2: Verification wrapper
if witness verify ... || rpm --checksig ...; then
echo "Package verified"
fi
# Phase 3: Pure Witness (no GPG)
witness run --step build ... -- rpmbuild -bb package.spec{
"expires": "2026-12-31T23:59:59Z",
"steps": {
"build": {
"attestations": [
{"type": "https://witness.dev/attestations/material/v0.1"},
{"type": "https://witness.dev/attestations/command-run/v0.1"},
{"type": "https://witness.dev/attestations/product/v0.1"}
],
"functionaries": [
{"type": "publickey", "publickeyid": "KEY_ID"}
]
}
},
"publickeys": {
"KEY_ID": {"keyid": "KEY_ID", "key": "BASE64_PUBLIC_KEY"}
}
}{
"expires": "2026-12-31T23:59:59Z",
"steps": {
"build": {
"attestations": [...],
"functionaries": [{
"type": "keyless",
"certConstraints": {
"emails": ["*@example.com"],
"uris": ["https://github.com/org/*"]
}
}]
}
},
"roots": {
"fulcio": "BASE64_FULCIO_ROOT"
}
}| Issue | Solution |
|---|---|
| "predicate type is not a collection" | Use individual attestation types in policy |
| "no verifiers present" | Create artifacts during witness run |
| "repository does not exist" | Use -a flag to specify attestors |
| "signature outside validity" | Ensure TSA counter-signing enabled |
| "OIDC token expired" | Re-authenticate or use fresh token |
- Choose signing method (keys vs keyless)
- Configure TSA for timestamp proof
- Set up Archivista for attestation storage
- Create and sign verification policies
- Implement air-gap bundle export
- Test verification workflow
- Document recovery procedures
Complete workflow for HPC environments:
# .github/workflows/openchami-witness.yml
name: OpenCHAMI Release
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Test
uses: testifysec/witness-run-action@v1
with:
step: test
command: go test ./...
- name: Build
uses: testifysec/witness-run-action@v1
with:
step: build
command: goreleaser build --clean
- name: Package RPM
uses: testifysec/witness-run-action@v1
with:
step: package
command: |
rpmbuild -bb \
--define "version ${GITHUB_REF_NAME#v}" \
ochami.spec- Documentation: https://witness.dev
- Source Code: https://github.com/testifysec/witness
- GitHub Action: https://github.com/testifysec/witness-run-action
- Working Example: https://github.com/colek42/witness-action-test
- Archivista: https://github.com/testifysec/archivista
- Support: https://testifysec.com
Version 3.1 - Streamlined Edition
Last Updated: 2025-08-11
Status: Production Ready - Fully Validated
All Examples Tested: ✅ Working
Live Implementation: https://github.com/colek42/witness-action-test