Skip to content

Commit b33008f

Browse files
authored
Fix and enhance SBOM and SLSA (#282)
### SBOM and SLSA Generation Improvements - Extended capabilities of existing SBOM functionality - In some cases, SLSA was failing to complete, this is now fixed. **Image Attestation** - New image attestation functionality for enhanced security verification - Ensures image integrity through cryptographic validation - Public keys are now located in the /certs directory for verification purposes - Centralizes access to verification materials **New Verification Script** - Added new certs/verify-image.sh script to help users verify SBOM and SLSA attestations - Simplifies the verification process ### Component Updates - Updated Dagger APKO components to newer version
1 parent e4dc19c commit b33008f

11 files changed

+752
-27
lines changed

RELEASE.md

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ The release process is automated through GitHub Actions. When a new version tag
1313

1414
## Creating a New Release
1515

16+
### Required Credentials
17+
18+
To create releases, you'll need:
19+
20+
1. **1Password Service Account Tokens**:
21+
- For staging environment access
22+
- For production environment access
23+
24+
2. **GitHub Token**:
25+
- With permissions to trigger the SLSA GitHub workflow
26+
1627
### Version Tag Format
1728

1829
Supported version tag formats (without 'v' prefix):
@@ -132,6 +143,12 @@ The release process is handled by the `Publish` function in `dagger/publish.go`.
132143
}
133144
```
134145
146+
Important notes about SLSA provenance:
147+
- This step is automatically triggered by the Dagger pipeline during production releases
148+
- Requires a GitHub token with permissions to trigger the SLSA workflow (`slsa.yml`)
149+
- The workflow is triggered via GitHub API to generate and attach provenance to the image
150+
- This step is skipped for development and staging releases
151+
135152
### Security and Attestations
136153
137154
Each release includes:
@@ -158,6 +175,22 @@ After a release is published, verify:
158175
helm pull oci://registry.replicated.com/library/replicated --version X.Y.Z
159176
```
160177
178+
3. Image Attestations:
179+
Use the verification script in the `certs` directory to verify SLSA provenance, image signatures, and SBOM:
180+
```bash
181+
./certs/verify-image.sh --env <dev|stage|prod> --version X.Y.Z --digest <image-digest>
182+
```
183+
184+
The script verifies:
185+
- SLSA provenance attestation
186+
- Image signatures using environment-specific public keys
187+
- SBOM content and signatures
188+
189+
To view the full SBOM content:
190+
```bash
191+
./certs/verify-image.sh --env <dev|stage|prod> --version X.Y.Z --digest <image-digest> --show-sbom
192+
```
193+
161194
## Troubleshooting
162195
163196
If the release workflow fails:
@@ -168,6 +201,8 @@ If the release workflow fails:
168201
- Docker registry authentication issues
169202
- Helm chart validation failures
170203
- Version format issues with melange/apko builds
204+
- Missing or invalid credentials
205+
- Insufficient GitHub token permissions
171206
172207
## Post-Release
173208
@@ -182,7 +217,7 @@ After a successful release:
182217
183218
If you encounter issues with the release process:
184219
1. Check the GitHub Actions logs
185-
2. Review the error messages
220+
2. Review the workflow error messages
186221
3. Contact the maintainers team
187222
188223
## Rolling Back
@@ -191,5 +226,4 @@ If issues are discovered after release:
191226
192227
1. Tag and push a new patch release with fixes
193228
194-
195229
Note: Always prefer forward fixes over rollbacks when possible.

certs/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Certificates and Verification Tools
2+
3+
This directory contains certificates and verification tools used for validating the authenticity and integrity of Replicated SDK container images.
4+
5+
## Contents
6+
7+
### Verification Script
8+
- `verify-image.sh` - Script to verify SLSA attestation, image signatures, and SBOM for SDK images
9+
10+
### Public Keys
11+
- `cosign-dev.pub` - Development environment public key for image signature verification
12+
- `cosign-stage.pub` - Staging environment public key for image signature verification
13+
- `cosign-prod.pub` - Production environment public key for image signature verification
14+
15+
## Usage
16+
17+
The verification script supports three environments (dev, stage, prod) and can be run as follows:
18+
19+
```bash
20+
./verify-image.sh --env <environment> --version <version> --digest <image-digest>
21+
```
22+
23+
For detailed usage instructions and examples, run:
24+
```bash
25+
./verify-image.sh --help
26+
```
27+
28+
## Environment-Specific Verification
29+
30+
- **Development**: Uses `cosign-dev.pub` for signature verification
31+
- **Staging**: Uses `cosign-stage.pub` for signature verification
32+
- **Production**: Uses `cosign-prod.pub` for signature verification
33+
34+
## Security Notes
35+
36+
- The public keys in this directory are used only for verification

certs/cosign-dev.pub

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDOWLY7EN/o0AZJP+gCmtQLXk+GJS
3+
Qz2IGHLOY0qIZkS+W9ul674xM5w8xWvCXU/c9ALeIMcDgu4Rmr7xYqDW5A==
4+
-----END PUBLIC KEY-----

certs/cosign-prod.pub

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV0TrL3yMZDRShRVr5iR7r2T1QjK/
3+
1fZ5W3idZ4qnX0sSvGvTbmkq9oMv/nYSDvnIqOQbyciCmUSjSouom4/IDA==
4+
-----END PUBLIC KEY-----

certs/cosign-stage.pub

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtXxwcbE+n6uKR4TZ1sN5nyXj5Hiv
3+
FfQSafmXTwTTz1SE+zPXSTl7y5Zn4KzBT4J/dz3tbaezLhfBCIUGzBaztQ==
4+
-----END PUBLIC KEY-----

certs/verify-image.sh

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#!/bin/bash
2+
3+
#####################################################################
4+
# verify-image.sh
5+
#
6+
# Description:
7+
# This script verifies the authenticity and integrity of the Replicated SDK
8+
# container images by performing three key security checks:
9+
# 1. SLSA Provenance verification - Ensures build chain integrity
10+
# 2. Image signature verification - Validates image authenticity
11+
# 3. SBOM attestation verification - Checks software bill of materials
12+
#
13+
# Usage:
14+
# ./verify-image.sh [OPTIONS]
15+
#
16+
# Required Arguments:
17+
# -e, --env ENV Environment to verify (dev|stage|prod)
18+
# -v, --version VER Version to verify
19+
# -d, --digest DIG Image digest to verify
20+
#
21+
# Optional Arguments:
22+
# --show-sbom Show full SBOM content
23+
# -h, --help Show this help message
24+
#
25+
# Environment-specific Behavior:
26+
# - dev: Uses ttl.sh registry with dev signing keys
27+
# - stage: Uses staging registry with staging signing keys
28+
# - prod: Uses production registry with keyless verification
29+
#
30+
# Examples:
31+
# ./verify-image.sh --env dev \
32+
# --version 1.5.3-beta.3 \
33+
# --digest sha256:5b064832df6bfb934c081fa0263134bc9845525211f09a752d5684306310f3c5
34+
#
35+
# ./verify-image.sh --env prod \
36+
# --version 1.5.3 \
37+
# --digest sha256:7cb8e0c8e0fba8e4a7157b4fcef9e7345538f7543a4e5925bb8b30c9c1375400
38+
#
39+
# Exit Codes:
40+
# 0 - All verifications passed
41+
# 1 - Verification failed or invalid arguments
42+
#
43+
# Author: Replicated, Inc.
44+
# License: Apache-2.0
45+
#####################################################################
46+
47+
# Help function to display usage information and examples
48+
show_help() {
49+
echo "Usage: $0 [OPTIONS]"
50+
echo "Verify SLSA attestation and SBOM for Replicated SDK"
51+
echo
52+
echo "Options:"
53+
echo " -e, --env ENV Environment to verify (dev|stage|prod) [Required]"
54+
echo " -v, --version VER Version to verify [Required]"
55+
echo " -d, --digest DIG Image digest to verify [Required]"
56+
echo " --show-sbom Show full SBOM content"
57+
echo " -h, --help Show this help message"
58+
echo
59+
echo "Examples:"
60+
echo " $0 --env dev --version 1.5.3-beta.3 --digest sha256:5b064832df6bfb934c081fa0263134bc9845525211f09a752d5684306310f3c5"
61+
echo " $0 --env stage --version 1.5.3-beta.3 --digest sha256:7cb8e0c8e0fba8e4a7157b4fcef9e7345538f7543a4e5925bb8b30c9c1375400"
62+
}
63+
64+
# Parse command line arguments using a while loop for flexibility
65+
while [[ $# -gt 0 ]]; do
66+
case $1 in
67+
-e|--env)
68+
ENV="$2"
69+
shift 2
70+
;;
71+
-v|--version)
72+
TEST_VERSION="$2"
73+
shift 2
74+
;;
75+
-d|--digest)
76+
DIGEST="$2"
77+
shift 2
78+
;;
79+
--show-sbom)
80+
SHOW_SBOM=true
81+
shift
82+
;;
83+
-h|--help)
84+
show_help
85+
exit 0
86+
;;
87+
*)
88+
echo "Unknown option: $1"
89+
show_help
90+
exit 1
91+
;;
92+
esac
93+
done
94+
95+
# Validate required arguments to ensure script can proceed
96+
if [ -z "$ENV" ] || [ -z "$TEST_VERSION" ] || [ -z "$DIGEST" ]; then
97+
echo "Error: Environment (-e), version (-v), and digest (-d) are required"
98+
show_help
99+
exit 1
100+
fi
101+
102+
# Validate environment value to prevent invalid configurations
103+
if [[ ! "$ENV" =~ ^(dev|stage|prod)$ ]]; then
104+
echo "Error: Environment (-e, --env) must be one of: dev, stage, prod"
105+
exit 1
106+
fi
107+
108+
# Set environment-specific variables for registry paths and image names
109+
case $ENV in
110+
dev)
111+
# Development environment uses ttl.sh for temporary image storage
112+
REGISTRY="ttl.sh"
113+
IMAGE="${REGISTRY}/replicated/replicated-sdk"
114+
;;
115+
stage)
116+
# Staging environment uses staging registry for pre-release testing
117+
REGISTRY="registry.staging.replicated.com"
118+
IMAGE="${REGISTRY}/library/replicated-sdk-image"
119+
;;
120+
prod)
121+
# Production environment uses main registry for released images
122+
REGISTRY="registry.replicated.com"
123+
IMAGE="${REGISTRY}/library/replicated-sdk-image"
124+
;;
125+
esac
126+
127+
# Construct full image reference with digest for unique identification
128+
IMAGE_WITH_DIGEST="${IMAGE}@${DIGEST}"
129+
130+
# Define source repository for SLSA verification
131+
SOURCE_REPO=github.com/replicatedhq/replicated-sdk
132+
133+
echo "==============================================="
134+
echo "Starting verification for ${IMAGE_WITH_DIGEST}"
135+
echo "==============================================="
136+
137+
# Step 1: SLSA Provenance Verification
138+
# This step ensures the image was built through our secure build pipeline
139+
echo -e "\n📋 Step 1: Verifying SLSA provenance..."
140+
if [ "$ENV" != "dev" ]; then
141+
# SLSA verification is skipped for dev environment as it uses different build process
142+
if COSIGN_REPOSITORY=${REGISTRY}/library/replicated-sdk-image slsa-verifier verify-image "${IMAGE_WITH_DIGEST}" \
143+
--source-uri ${SOURCE_REPO} \
144+
--source-tag ${TEST_VERSION} \
145+
--print-provenance | tee /tmp/slsa_output.json | jq -r '
146+
# Format and display relevant build information from SLSA attestation
147+
"✅ SLSA Verification: SUCCESS",
148+
"Build Details:",
149+
" • Builder: \(.predicate.builder.id | split("@")[0])",
150+
" • Organization: \(.predicate.invocation.environment.github_event_payload.organization.login)",
151+
" • Last Commit Author: \(.predicate.invocation.environment.github_event_payload.head_commit.author.name)",
152+
" • Last Commit Email: \(.predicate.invocation.environment.github_event_payload.head_commit.author.email)",
153+
" • Release Created By: \(.predicate.invocation.environment.github_event_payload.pusher.name)",
154+
" • Source: \(.predicate.invocation.configSource.uri | split("@")[0])",
155+
" • Commit: \(.predicate.invocation.configSource.digest.sha1)",
156+
" • Built From: \(.predicate.invocation.environment.github_ref)",
157+
" • Image Digest: \(.subject[0].digest.sha256 // "N/A")",
158+
" • Commit Timestamp: \(.predicate.invocation.environment.github_event_payload.head_commit.timestamp)"
159+
'; then
160+
echo "✅ SLSA verification successful"
161+
else
162+
echo "❌ SLSA verification failed"
163+
exit 1
164+
fi
165+
else
166+
echo "ℹ️ SLSA verification skipped in dev mode"
167+
fi
168+
169+
# Step 2: Image Signature Verification
170+
# Validates the image signature using environment-specific keys
171+
echo -e "\n🔏 Step 2: Verifying image signature..."
172+
if [ "$ENV" = "dev" ]; then
173+
# Development environment uses a development signing key for verification
174+
if VERIFICATION_OUTPUT=$(cosign verify-attestation \
175+
--key ./cosign-dev.pub \
176+
--type spdxjson \
177+
${IMAGE_WITH_DIGEST} 2>/dev/null); then
178+
echo "✅ Image signature verification successful"
179+
echo "Signature details:"
180+
echo "$VERIFICATION_OUTPUT" | jq -r '
181+
" • Attestation type: \(.payloadType)",
182+
" • Signature timestamp: \(.optional.sig.timestamp // "N/A")"
183+
'
184+
else
185+
echo "❌ Image signature verification failed"
186+
exit 1
187+
fi
188+
elif [ "$ENV" = "stage" ]; then
189+
# Staging environment uses staging-specific signing key
190+
if VERIFICATION_OUTPUT=$(cosign verify-attestation \
191+
--key ./cosign-stage.pub \
192+
--type spdxjson \
193+
${IMAGE_WITH_DIGEST} 2>/dev/null); then
194+
echo "✅ Image signature verification successful"
195+
echo "Signature details:"
196+
echo "$VERIFICATION_OUTPUT" | jq -r '
197+
" • Attestation type: \(.payloadType)",
198+
" • Signature timestamp: \(.optional.sig.timestamp // "N/A")"
199+
'
200+
else
201+
echo "❌ Image signature verification failed"
202+
exit 1
203+
fi
204+
else
205+
# Production environment uses production-specific signing key
206+
if VERIFICATION_OUTPUT=$(cosign verify-attestation \
207+
--key ./cosign-prod.pub \
208+
--type spdxjson \
209+
${IMAGE_WITH_DIGEST} 2>/dev/null); then
210+
echo "✅ Image signature verification successful"
211+
echo "Signature details:"
212+
echo "$VERIFICATION_OUTPUT" | jq -r '
213+
" • Attestation type: \(.payloadType)",
214+
" • Signature timestamp: \(.optional.sig.timestamp // "N/A")"
215+
'
216+
else
217+
echo "❌ Image signature verification failed"
218+
exit 1
219+
fi
220+
fi
221+
222+
# Step 3: SBOM Attestation Verification
223+
# Verifies and displays the Software Bill of Materials attached to the image
224+
echo -e "\n📦 Step 3: Verifying SBOM attestation..."
225+
226+
# Try both SPDX predicate types for compatibility with different SBOM formats
227+
if ! RAW_ATTESTATION=$(cosign download attestation \
228+
--predicate-type spdxjson \
229+
${IMAGE_WITH_DIGEST} 2>/dev/null) && \
230+
! RAW_ATTESTATION=$(cosign download attestation \
231+
--predicate-type https://spdx.dev/Document \
232+
${IMAGE_WITH_DIGEST} 2>/dev/null); then
233+
echo "❌ No SPDX attestation found on image"
234+
echo "This may indicate the SBOM wasn't properly attached during build"
235+
exit 1
236+
fi
237+
238+
# Ensure the attestation is not empty
239+
if [ -z "$RAW_ATTESTATION" ]; then
240+
echo "❌ Empty SPDX attestation found"
241+
echo "This may indicate an issue during the build process"
242+
exit 1
243+
fi
244+
245+
echo "✅ SBOM verification successful"
246+
DECODED_PAYLOAD=$(echo "$RAW_ATTESTATION" | jq -r '.payload' | base64 -d)
247+
248+
# Display formatted SBOM information focusing on key details
249+
echo "SBOM details:"
250+
echo "$DECODED_PAYLOAD" | jq -r '
251+
" • Document Name: \(.predicate.name // "N/A")",
252+
" • Created: \(.predicate.creationInfo.created // "N/A")",
253+
" • Created By: \(.predicate.creationInfo.creators | map(select(startswith("Organization: Replicated"))) | .[0] // "N/A")",
254+
" • Tool: \(.predicate.creationInfo.creators | map(select(startswith("Tool:"))) | .[0] // "N/A")",
255+
" • Total Packages: \(.predicate.packages | length) packages"
256+
'
257+
258+
# Optionally display full SBOM content if requested
259+
if [ "$SHOW_SBOM" = "true" ]; then
260+
echo -e "\nFull SBOM Content:"
261+
echo "$DECODED_PAYLOAD" | jq '.'
262+
fi
263+
264+
echo "ℹ️ Use '--show-sbom' flag to view full SBOM contents"
265+
266+
echo -e "\n✨ All verifications completed successfully!"
267+
echo "==============================================="

0 commit comments

Comments
 (0)