save progress
This commit is contained in:
384
docs/airgap/VEX_SIGNATURE_VERIFICATION_OFFLINE_MODE.md
Normal file
384
docs/airgap/VEX_SIGNATURE_VERIFICATION_OFFLINE_MODE.md
Normal file
@@ -0,0 +1,384 @@
|
||||
# VEX Signature Verification: Offline Mode
|
||||
|
||||
**Sprint:** SPRINT_1227_0004_0001_BE_signature_verification
|
||||
**Task:** T11 - Document offline mode with bundled trust anchors
|
||||
**Date:** 2025-12-28
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes how to configure VEX signature verification for air-gapped (offline) deployments where network access to public trust infrastructure (Sigstore, Fulcio, Rekor) is unavailable.
|
||||
|
||||
---
|
||||
|
||||
## Offline Mode Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Air-Gapped Environment │
|
||||
│ │
|
||||
│ ┌───────────────┐ ┌────────────────────────────────┐ │
|
||||
│ │ VEX Documents │────▶│ ProductionVexSignatureVerifier │ │
|
||||
│ └───────────────┘ └────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────┴────────────────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌─────────────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Bundled Trust Anchors │ │ Bundled Issuer Dir │ │
|
||||
│ │ /var/stellaops/trust/ │ │ /var/stellaops/ │ │
|
||||
│ │ ├── fulcio-root.pem │ │ bundles/issuers.json│ │
|
||||
│ │ ├── sigstore-root.pem │ └─────────────────────┘ │
|
||||
│ │ └── internal-ca.pem │ │
|
||||
│ └─────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### 1. Enable Offline Mode
|
||||
|
||||
**File:** `etc/excititor.yaml`
|
||||
|
||||
```yaml
|
||||
VexSignatureVerification:
|
||||
Enabled: true
|
||||
DefaultProfile: "world"
|
||||
OfflineMode: true # Critical: Enable offline verification
|
||||
|
||||
# Offline-specific settings
|
||||
OfflineBundle:
|
||||
Enabled: true
|
||||
BundlePath: "/var/stellaops/bundles"
|
||||
RefreshOnStartup: false
|
||||
|
||||
# Trust anchors for signature verification
|
||||
TrustAnchors:
|
||||
Fulcio:
|
||||
- "/var/stellaops/trust/fulcio-root.pem"
|
||||
- "/var/stellaops/trust/fulcio-intermediate.pem"
|
||||
Sigstore:
|
||||
- "/var/stellaops/trust/sigstore-root.pem"
|
||||
Internal:
|
||||
- "/var/stellaops/trust/internal-ca.pem"
|
||||
- "/var/stellaops/trust/internal-intermediate.pem"
|
||||
|
||||
# IssuerDirectory in offline mode
|
||||
IssuerDirectory:
|
||||
OfflineBundle: "/var/stellaops/bundles/issuers.json"
|
||||
FallbackToBundle: true
|
||||
# ServiceUrl not needed in offline mode
|
||||
```
|
||||
|
||||
### 2. Directory Structure
|
||||
|
||||
```
|
||||
/var/stellaops/
|
||||
├── bundles/
|
||||
│ ├── issuers.json # Issuer directory bundle
|
||||
│ ├── revocations.json # Key revocation data
|
||||
│ └── tuf-metadata/ # TUF metadata for updates
|
||||
│ ├── root.json
|
||||
│ ├── targets.json
|
||||
│ └── snapshot.json
|
||||
├── trust/
|
||||
│ ├── fulcio-root.pem # Sigstore Fulcio root CA
|
||||
│ ├── fulcio-intermediate.pem
|
||||
│ ├── sigstore-root.pem # Sigstore root
|
||||
│ ├── rekor-pubkey.pem # Rekor public key
|
||||
│ ├── internal-ca.pem # Internal enterprise CA
|
||||
│ └── internal-intermediate.pem
|
||||
└── cache/
|
||||
└── verification-cache.db # Local verification cache
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bundle Preparation
|
||||
|
||||
### 1. Download Trust Anchors
|
||||
|
||||
Run this on a connected machine to prepare the bundle:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# prepare-offline-bundle.sh
|
||||
|
||||
BUNDLE_DIR="./offline-bundle"
|
||||
mkdir -p "$BUNDLE_DIR/trust" "$BUNDLE_DIR/bundles"
|
||||
|
||||
# Download Sigstore trust anchors
|
||||
echo "Downloading Sigstore trust anchors..."
|
||||
curl -sSL https://fulcio.sigstore.dev/api/v2/trustBundle \
|
||||
-o "$BUNDLE_DIR/trust/fulcio-root.pem"
|
||||
|
||||
curl -sSL https://rekor.sigstore.dev/api/v1/log/publicKey \
|
||||
-o "$BUNDLE_DIR/trust/rekor-pubkey.pem"
|
||||
|
||||
# Download TUF metadata
|
||||
echo "Downloading TUF metadata..."
|
||||
cosign initialize --mirror=https://tuf-repo.sigstore.dev \
|
||||
--root="$BUNDLE_DIR/bundles/tuf-metadata"
|
||||
|
||||
# Export issuer directory
|
||||
echo "Exporting issuer directory..."
|
||||
stellaops-cli issuer-directory export \
|
||||
--format json \
|
||||
--output "$BUNDLE_DIR/bundles/issuers.json"
|
||||
|
||||
# Export revocation data
|
||||
echo "Exporting revocation data..."
|
||||
stellaops-cli revocations export \
|
||||
--format json \
|
||||
--output "$BUNDLE_DIR/bundles/revocations.json"
|
||||
|
||||
# Create manifest
|
||||
echo "Creating bundle manifest..."
|
||||
cat > "$BUNDLE_DIR/manifest.json" <<EOF
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"expiresAt": "$(date -u -d '+90 days' +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"contents": {
|
||||
"trustAnchors": ["fulcio-root.pem", "rekor-pubkey.pem"],
|
||||
"bundles": ["issuers.json", "revocations.json"],
|
||||
"tufMetadata": true
|
||||
},
|
||||
"checksum": "$(find $BUNDLE_DIR -type f -exec sha256sum {} \; | sha256sum | cut -d' ' -f1)"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Package bundle
|
||||
echo "Creating tarball..."
|
||||
tar -czvf "stellaops-trust-bundle-$(date +%Y%m%d).tar.gz" -C "$BUNDLE_DIR" .
|
||||
|
||||
echo "Bundle ready: stellaops-trust-bundle-$(date +%Y%m%d).tar.gz"
|
||||
```
|
||||
|
||||
### 2. Transfer to Air-Gapped Environment
|
||||
|
||||
```bash
|
||||
# On air-gapped machine
|
||||
sudo mkdir -p /var/stellaops/{trust,bundles,cache}
|
||||
sudo tar -xzvf stellaops-trust-bundle-20250128.tar.gz -C /var/stellaops/
|
||||
|
||||
# Verify bundle integrity
|
||||
stellaops-cli bundle verify /var/stellaops/manifest.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issuer Directory Bundle Format
|
||||
|
||||
**File:** `/var/stellaops/bundles/issuers.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"exportedAt": "2025-01-28T10:30:00Z",
|
||||
"issuers": [
|
||||
{
|
||||
"id": "redhat-security",
|
||||
"name": "Red Hat Product Security",
|
||||
"description": "Official Red Hat security advisories",
|
||||
"jurisdiction": "us",
|
||||
"trustLevel": "high",
|
||||
"keys": [
|
||||
{
|
||||
"keyId": "rh-vex-signing-key-2024",
|
||||
"algorithm": "ECDSA-P256",
|
||||
"publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0...\n-----END PUBLIC KEY-----",
|
||||
"notBefore": "2024-01-01T00:00:00Z",
|
||||
"notAfter": "2026-01-01T00:00:00Z",
|
||||
"revoked": false
|
||||
}
|
||||
],
|
||||
"csafPublisher": {
|
||||
"providerMetadataUrl": "https://access.redhat.com/.well-known/csaf/provider-metadata.json",
|
||||
"tlpWhite": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "internal-security",
|
||||
"name": "Internal Security Team",
|
||||
"description": "Internal VEX attestations",
|
||||
"jurisdiction": "internal",
|
||||
"trustLevel": "high",
|
||||
"keys": [
|
||||
{
|
||||
"keyId": "internal-vex-key-001",
|
||||
"algorithm": "Ed25519",
|
||||
"publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA...\n-----END PUBLIC KEY-----",
|
||||
"notBefore": "2024-06-01T00:00:00Z",
|
||||
"notAfter": "2025-06-01T00:00:00Z",
|
||||
"revoked": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"revokedKeys": [
|
||||
{
|
||||
"keyId": "old-compromised-key",
|
||||
"revokedAt": "2024-03-15T00:00:00Z",
|
||||
"reason": "key_compromise"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification Behavior in Offline Mode
|
||||
|
||||
### Supported Verification Methods
|
||||
|
||||
| Method | Offline Support | Notes |
|
||||
|--------|-----------------|-------|
|
||||
| DSSE | Full | Uses bundled keys |
|
||||
| PGP | Full | Uses bundled keyrings |
|
||||
| X.509 | Partial | Requires bundled CA chain |
|
||||
| Cosign (keyed) | Full | Uses bundled public keys |
|
||||
| Cosign (keyless) | Limited | Requires bundled Fulcio root |
|
||||
| Rekor Verification | No | Transparency log unavailable |
|
||||
|
||||
### Fallback Behavior
|
||||
|
||||
```yaml
|
||||
VexSignatureVerification:
|
||||
OfflineFallback:
|
||||
# When Rekor is unavailable
|
||||
SkipRekorVerification: true
|
||||
WarnOnMissingTransparency: true
|
||||
|
||||
# When issuer key not in bundle
|
||||
UnknownIssuerAction: "warn" # warn | block | allow
|
||||
|
||||
# When certificate chain incomplete
|
||||
IncompleteChainAction: "warn"
|
||||
```
|
||||
|
||||
### Verification Result Fields
|
||||
|
||||
```json
|
||||
{
|
||||
"verified": true,
|
||||
"method": "dsse",
|
||||
"mode": "offline",
|
||||
"warnings": [
|
||||
"transparency_log_skipped"
|
||||
],
|
||||
"issuerName": "Red Hat Product Security",
|
||||
"keyId": "rh-vex-signing-key-2024",
|
||||
"bundleVersion": "2025.01.28",
|
||||
"bundleAge": "P3D"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bundle Updates
|
||||
|
||||
### Manual Update Process
|
||||
|
||||
1. **Export new bundle** on connected machine
|
||||
2. **Transfer** via secure media (USB, CD)
|
||||
3. **Verify** bundle signature on air-gapped machine
|
||||
4. **Deploy** with rollback capability
|
||||
|
||||
```bash
|
||||
# On air-gapped machine
|
||||
cd /var/stellaops
|
||||
|
||||
# Backup current bundle
|
||||
sudo cp -r bundles bundles.backup-$(date +%Y%m%d)
|
||||
|
||||
# Deploy new bundle
|
||||
sudo tar -xzvf new-bundle.tar.gz -C /tmp/new-bundle
|
||||
sudo stellaops-cli bundle verify /tmp/new-bundle/manifest.json
|
||||
|
||||
# Apply with verification
|
||||
sudo stellaops-cli bundle apply /tmp/new-bundle --verify
|
||||
sudo systemctl restart stellaops-excititor
|
||||
|
||||
# Rollback if needed
|
||||
# sudo stellaops-cli bundle rollback --to bundles.backup-20250115
|
||||
```
|
||||
|
||||
### Recommended Update Frequency
|
||||
|
||||
| Component | Recommended Frequency | Criticality |
|
||||
|-----------|----------------------|-------------|
|
||||
| Trust anchors | Quarterly | High |
|
||||
| Issuer directory | Monthly | Medium |
|
||||
| Revocation data | Weekly | Critical |
|
||||
| TUF metadata | Monthly | Medium |
|
||||
|
||||
---
|
||||
|
||||
## Monitoring and Alerts
|
||||
|
||||
### Bundle Expiration Warning
|
||||
|
||||
```yaml
|
||||
# prometheus-alerts.yaml
|
||||
groups:
|
||||
- name: stellaops-verification
|
||||
rules:
|
||||
- alert: TrustBundleExpiringSoon
|
||||
expr: stellaops_trust_bundle_expiry_days < 30
|
||||
for: 1h
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Trust bundle expires in {{ $value }} days"
|
||||
|
||||
- alert: TrustBundleExpired
|
||||
expr: stellaops_trust_bundle_expiry_days <= 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Trust bundle has expired - verification may fail"
|
||||
```
|
||||
|
||||
### Metrics
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| `stellaops_trust_bundle_expiry_days` | Days until bundle expiration |
|
||||
| `stellaops_verification_offline_mode` | 1 if running in offline mode |
|
||||
| `stellaops_verification_bundle_key_count` | Number of issuer keys in bundle |
|
||||
| `stellaops_verification_revoked_key_count` | Number of revoked keys |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"Unknown issuer" for known vendor**
|
||||
- Update issuer directory bundle
|
||||
- Add vendor's keys to bundle
|
||||
|
||||
2. **"Expired certificate" for recent VEX**
|
||||
- Certificate may have rotated after bundle export
|
||||
- Update trust anchors bundle
|
||||
|
||||
3. **"Chain validation failed"**
|
||||
- Missing intermediate certificate
|
||||
- Add intermediate to bundle
|
||||
|
||||
4. **Stale revocation data**
|
||||
- Key may be compromised but bundle doesn't know
|
||||
- Update revocation bundle urgently
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [VEX Signature Verification Configuration](../operations/vex-verification-config.md)
|
||||
- [Air-Gap Deployment Guide](../airgap/deployment-guide.md)
|
||||
- [TUF Repository Management](../operations/tuf-repository.md)
|
||||
Reference in New Issue
Block a user