Difficulty: Intermediate | Time: 45 minutes | Focus: Signatures, SBOM, SLSA provenance
Objectives
By the end of this lab, you will be able to verify a container image signature using cosign, inspect the Software Bill of Materials (SBOM) in an image, validate SLSA provenance attestations, understand supply chain security concepts, and use image attestations for security decisions.
Prerequisites
Required: Docker 20.10 or newer, cosign 2.0 or newer, jq (for JSON parsing), curl, and Access to registry.cleanstart.com (read-only).
Installation:
Install cosign by downloading the appropriate version for your platform:
# macOSbrew install cosign # LinuxCOSIGN_VERSION=v2.2.4curl -fsSL https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64 -o /usr/local/bin/cosignchmod +x /usr/local/bin/cosign # Windows (WSL)COSIGN_VERSION=v2.2.4curl -fsSL https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64 -o /usr/local/bin/cosignchmod +x /usr/local/bin/cosignVerify cosign installation:
cosign versionExpected output:
cosign version v2.0.0 or newerInstall jq for JSON processing:
# macOSbrew install jq # Linux/Windows (WSL)sudo apt-get install -y jq # Verifyjq --versionBackground: Container Image Security
CleanStart images are signed and include attestations for image signatures that prove the image came from a known source (using cosign and Sigstore), SBOMs that list all software components in the image (SPDX format), and SLSA Provenance that proves the image was built through a verified build process.
This lab demonstrates these three security properties.
Step 1: Create Working Directory
mkdir -p ~/labs/lab-03-image-verificationcd ~/labs/lab-03-image-verificationStep 2: Pull a CleanStart Base Image
Pull the Python 3.12 CleanStart image:
docker pull registry.cleanstart.com/cleanstart/python:3.12Expected output:
3.12: Pulling from cleanstart/python...Status: Downloaded newer image for registry.cleanstart.com/cleanstart/python:3.12Step 3: Verify Image Signature
Verify the image signature using cosign to confirm the image was signed by CleanStart:
cosign verify \ --certificate-identity-regexp '.*' \ --certificate-oidc-issuer https://accounts.google.com \ registry.cleanstart.com/cleanstart/python:3.12Expected output:
Verification successful! Verified using the following identity:iss: https://token.actions.githubusercontent.comsub: repo:cleanstart/images:ref:refs/heads/main:workflow_name:publish:trigger_event_name:push...If you see Verification successful!, the image signature is valid and the image comes from the official CleanStart repository.
Step 4: Inspect Signature Attestations
List all attestations for the image:
cosign tree registry.cleanstart.com/cleanstart/python:3.12Expected output:
📦 Supply Chain Levels for Software Artifacts (SLSA) Provenance v1.0 registry.cleanstart.com/cleanstart/python:3.12 Attestations:- sbom (cyclonedx)- slsaprovenance- signatureThe output shows three attestations attached to this image. The sbom (cyclonedx) is a Software Bill of Materials in CycloneDX format documenting all dependencies. The slsaprovenance is a SLSA Level 3 provenance attestation proving the build process. The signature is the cosign cryptographic signature verifying image authenticity.
Step 5: Extract and View the SBOM
Get the SBOM (Software Bill of Materials) attachment:
cosign download sbom registry.cleanstart.com/cleanstart/python:3.12 > sbom.jsonView the SBOM metadata:
jq '.metadata | {name, version, timestamp}' sbom.jsonExpected output:
{ "name": "cleanstart/python", "version": "3.12", "timestamp": "2024-03-22T10:30:00Z"}Count the components in the SBOM:
jq '.components | length' sbom.jsonExpected output (approximate):
342View the first 3 components:
jq '.components[0:3]' sbom.jsonExpected output (abbreviated):
[ { "type": "library", "name": "python", "version": "3.12.1", "purl": "pkg:generic/python@3.12.1" }, { "type": "library", "name": "openssl", "version": "1.1.1w", "purl": "pkg:generic/openssl@1.1.1w" }, ...]List all Python packages in the SBOM:
jq '.components[] | select(.type == "library") | .name' sbom.json | head -20Expected output:
pythonopensslzlibreadline...Step 6: Check for Known Vulnerabilities in SBOM
Create a script to check if any components in the SBOM have known CVEs:
cat > check-vuln.sh << 'EOF'#!/bin/bash echo "Checking first 10 components for known vulnerabilities..." jq '.components[0:10] | .[] | "\(.name)-\(.version)"' sbom.json | while read component; do echo "Component: $component" # In a real scenario, you would check against a vulnerability database # For this lab, we're just demonstrating the SBOM structuredoneEOF chmod +x check-vuln.sh./check-vuln.shExpected output:
Checking first 10 components for known vulnerabilities...Component: python-3.12.1Component: openssl-1.1.1wComponent: zlib-1.2.13Component: readline-8.2...Step 7: Extract and View SLSA Provenance
Get the SLSA provenance attestation:
cosign download attestation registry.cleanstart.com/cleanstart/python:3.12 | \ jq -r '.payload' | base64 -d > provenance.jsonView the provenance metadata:
jq '.predicate.metadata' provenance.jsonExpected output:
{ "buildInvocationID": "https://github.com/cleanstart/images/actions/runs/8124567", "buildStartTime": "2024-03-22T10:25:00Z", "buildFinishTime": "2024-03-22T10:31:15Z", "completeness": { "parameters": true, "environment": true, "materials": true }, "reproducible": true}View the build configuration:
jq '.predicate.buildConfig' provenance.json | head -20Expected output (abbreviated):
{ "steps": [ { "entrypoint": "make", "arguments": ["build", "python:3.12"] } ]}Step 8: Verify Provenance Build Information
Check who built the image:
jq '.predicate.builder.id' provenance.jsonExpected output:
"https://github.com/slsa-framework/slsa-github-generator@v1.9.0"Check the build materials (inputs):
jq '.predicate.materials[0:3]' provenance.jsonExpected output (abbreviated):
[ { "uri": "git+https://github.com/cleanstart/images@refs/heads/main", "digest": { "sha256": "abc123def456..." } }, ...]Step 9: Create a Security Summary Report
Create a verification report:
cat > security-report.md << 'EOF'# CleanStart Python 3.12 Security Verification Report ## Image Information- **Image**: registry.cleanstart.com/cleanstart/python:3.12- **Verification Date**: $(date) ## Signature Verification- ✅ **Cosign Signature**: Valid- ✅ **Signatory**: GitHub Actions (cleanstart/images repository)- ✅ **Build Trigger**: Push to main branch ## SBOM Analysis- **Format**: CycloneDX- **Total Components**: ~342- **Component Types**: - Libraries: 300+ - Tools: 40+ ### Key Components- Python 3.12.1- OpenSSL 1.1.1w (cryptography)- zlib 1.2.13 (compression)- readline 8.2 (CLI input) ## SLSA Provenance- **SLSA Level**: 3 (highest)- **Build System**: GitHub Actions- **Builder ID**: github/slsa-github-generator@v1.9.0- **Reproducible**: Yes- **Build Time**: ~6 minutes 15 seconds ## Security Implications1. The image was built by CleanStart's CI/CD system2. All build steps are logged and reproducible3. The SBOM is complete and includes all components4. Signature verification ensures image authenticity5. No manual modifications detected ## Recommendation✅ This image is safe for production use.EOF cat security-report.mdStep 10: Verify Different Image Tags
Verify another tag to compare:
# Try a different Python versioncosign verify \ --certificate-identity-regexp '.*' \ --certificate-oidc-issuer https://accounts.google.com \ registry.cleanstart.com/cleanstart/python:3.11 2>&1 | head -10Expected output:
Verification successful!...All CleanStart images are signed consistently.
Step 11: Create a Verification Checklist Script
Create a reusable verification script:
cat > verify-image.sh << 'EOF'#!/bin/bash IMAGE=$1 if [ -z "$IMAGE" ]; then echo "Usage: ./verify-image.sh <image-uri>" exit 1fi echo "=== Image Verification Checklist ==="echo "Image: $IMAGE"echo "" echo "1. Verifying signature..."if cosign verify --certificate-identity-regexp '.*' --certificate-oidc-issuer https://accounts.google.com "$IMAGE" > /dev/null 2>&1; then echo " ✅ Signature valid"else echo " ❌ Signature invalid" exit 1fi echo ""echo "2. Checking attestations..."if cosign tree "$IMAGE" 2>/dev/null | grep -q sbom; then echo " ✅ SBOM present"fiif cosign tree "$IMAGE" 2>/dev/null | grep -q slsaprovenance; then echo " ✅ SLSA provenance present"fi echo ""echo "3. Extracting SBOM..."if cosign download sbom "$IMAGE" > /tmp/sbom.json 2>/dev/null; then COMPONENT_COUNT=$(jq '.components | length' /tmp/sbom.json 2>/dev/null || echo "unknown") echo " ✅ SBOM extracted ($COMPONENT_COUNT components)"else echo " ⚠️ SBOM extraction failed"fi echo ""echo "Verification complete!"EOF chmod +x verify-image.shTest the verification script:
./verify-image.sh registry.cleanstart.com/cleanstart/python:3.12Expected output:
=== Image Verification Checklist ===Image: registry.cleanstart.com/cleanstart/python:3.12 1. Verifying signature... ✅ Signature valid 2. Checking attestations... ✅ SBOM present ✅ SLSA provenance present 3. Extracting SBOM... ✅ SBOM extracted (342 components) Verification complete!Verification Checklist
Before considering Lab 03 complete, you should verify that the following items have been successfully completed. Confirm that the directory ~/labs/lab-03-image-verification was created and that cosign version 2.0 or higher is installed and verified with the version command. Verify that jq is installed for JSON processing, and that the CleanStart Python 3.12 image was successfully pulled from the registry. Confirm that cosign verify runs successfully and displays "Verification successful!" in the output. Check that cosign tree shows the three key attestations: sbom, slsaprovenance, and signature attestations. Verify that sbom.json was extracted and actually contains component data in the expected JSON format. Confirm that the jq commands executed on the SBOM data produced valid JSON output without errors. Check that provenance.json was extracted and displays the build metadata as expected. Verify that security-report.md was created with a verification summary documenting the attestations. Finally, confirm that the verify-image.sh script was created successfully and executes without errors.
Once all of these items have been verified and completed, you have successfully finished Lab 03 and demonstrated mastery of container image verification techniques.
What You Learned
Throughout this lab, you explored several essential concepts for supply chain security. Cosign is the primary tool for signing and verifying container images using cryptographic signatures, ensuring that images come from trusted sources and have not been modified. A Software Bill of Materials (SBOM), which serves as a complete inventory of all software components in an image, is critical for understanding what dependencies your container includes. SLSA Provenance provides a verifiable record of how and where an image was built, creating an auditable trail of the build process. Collectively, these mechanisms enable supply chain security by allowing you to verify image authenticity, track build provenance, and understand your component inventory. Attestations are cryptographically signed metadata attached to container images that vouch for various image properties. CycloneDX is one standard format for SBOMs, with SPDX being another widely adopted standard. Finally, Trust on First Use (TOFU) is the mechanism by which cosign establishes trust with public keys from trusted authorities, allowing verification without a central certificate authority.
Cleanup
Remove lab artifacts:
cd ~/labs/lab-03-image-verificationrm -f sbom.json provenance.json security-report.md verify-image.sh check-vuln.shrm -rf ~/labs/lab-03-image-verificationNext Lab
Proceed to Lab 04: Read-Only Filesystem to learn how to secure containers with read-only root filesystems.
Real-World Application
In production, you should always verify image signatures before deployment. Scan SBOMs for known vulnerabilities. Audit SLSA provenance to ensure build integrity. Require all base images to have valid signatures. Reject images with invalid signatures or missing attestations.
Estimated Time: 45 minutes | Hands-on: ~35 minutes | Reading: ~10 minutes
