fix tests. new product advisories enhancements
This commit is contained in:
150
devops/trust-repo-template/scripts/add-target.sh
Normal file
150
devops/trust-repo-template/scripts/add-target.sh
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------
|
||||
# add-target.sh
|
||||
# Sprint: SPRINT_20260125_001_Attestor_tuf_trust_foundation
|
||||
# Task: TUF-006 - Create TUF repository structure template
|
||||
# Description: Add a new target file to the TUF repository
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <source-file> <target-name> [options]"
|
||||
echo ""
|
||||
echo "Add a target file to the TUF repository."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --repo DIR Repository directory (default: current directory)"
|
||||
echo " --custom-hash HASH Override SHA256 hash (for testing only)"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 /path/to/rekor-key.pub rekor-key-v1"
|
||||
echo " $0 /path/to/services.json sigstore-services-v1 --repo /var/lib/tuf"
|
||||
exit 1
|
||||
}
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
SOURCE_FILE=""
|
||||
TARGET_NAME=""
|
||||
REPO_DIR="."
|
||||
CUSTOM_HASH=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--repo)
|
||||
REPO_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--custom-hash)
|
||||
CUSTOM_HASH="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$SOURCE_FILE" ]]; then
|
||||
SOURCE_FILE="$1"
|
||||
elif [[ -z "$TARGET_NAME" ]]; then
|
||||
TARGET_NAME="$1"
|
||||
else
|
||||
log_error "Unknown argument: $1"
|
||||
usage
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$SOURCE_FILE" ]] || [[ -z "$TARGET_NAME" ]]; then
|
||||
log_error "Source file and target name are required"
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! -f "$SOURCE_FILE" ]]; then
|
||||
log_error "Source file not found: $SOURCE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$REPO_DIR/targets.json" ]]; then
|
||||
log_error "Not a TUF repository: $REPO_DIR (targets.json not found)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Calculate file hash and size
|
||||
FILE_SIZE=$(stat -f%z "$SOURCE_FILE" 2>/dev/null || stat -c%s "$SOURCE_FILE")
|
||||
if [[ -n "$CUSTOM_HASH" ]]; then
|
||||
FILE_HASH="$CUSTOM_HASH"
|
||||
else
|
||||
FILE_HASH=$(openssl dgst -sha256 -hex "$SOURCE_FILE" | awk '{print $2}')
|
||||
fi
|
||||
|
||||
log_info "Adding target: $TARGET_NAME"
|
||||
log_info " Source: $SOURCE_FILE"
|
||||
log_info " Size: $FILE_SIZE bytes"
|
||||
log_info " SHA256: $FILE_HASH"
|
||||
|
||||
# Copy file to targets directory
|
||||
TARGETS_DIR="$REPO_DIR/targets"
|
||||
mkdir -p "$TARGETS_DIR"
|
||||
cp "$SOURCE_FILE" "$TARGETS_DIR/$TARGET_NAME"
|
||||
|
||||
# Update targets.json
|
||||
# This is a simplified implementation - production should use proper JSON manipulation
|
||||
TARGETS_JSON="$REPO_DIR/targets.json"
|
||||
|
||||
# Read current version
|
||||
CURRENT_VERSION=$(grep -o '"version"[[:space:]]*:[[:space:]]*[0-9]*' "$TARGETS_JSON" | head -1 | grep -o '[0-9]*')
|
||||
NEW_VERSION=$((CURRENT_VERSION + 1))
|
||||
|
||||
# Calculate new expiry (30 days from now)
|
||||
NEW_EXPIRES=$(date -u -d "+30 days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+30d +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
log_info "Updating targets.json (version $CURRENT_VERSION -> $NEW_VERSION)"
|
||||
|
||||
# Create new targets entry
|
||||
python3 - "$TARGETS_JSON" "$TARGET_NAME" "$FILE_SIZE" "$FILE_HASH" "$NEW_VERSION" "$NEW_EXPIRES" << 'PYTHON_SCRIPT'
|
||||
import json
|
||||
import sys
|
||||
|
||||
targets_file = sys.argv[1]
|
||||
target_name = sys.argv[2]
|
||||
file_size = int(sys.argv[3])
|
||||
file_hash = sys.argv[4]
|
||||
new_version = int(sys.argv[5])
|
||||
new_expires = sys.argv[6]
|
||||
|
||||
with open(targets_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
data['signed']['version'] = new_version
|
||||
data['signed']['expires'] = new_expires
|
||||
data['signed']['targets'][target_name] = {
|
||||
'length': file_size,
|
||||
'hashes': {
|
||||
'sha256': file_hash
|
||||
}
|
||||
}
|
||||
|
||||
# Clear signatures (need to re-sign)
|
||||
data['signatures'] = []
|
||||
|
||||
with open(targets_file, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
print(f"Updated {targets_file}")
|
||||
PYTHON_SCRIPT
|
||||
|
||||
log_info ""
|
||||
log_info "Target added successfully!"
|
||||
log_warn "IMPORTANT: targets.json signatures have been cleared."
|
||||
log_warn "Run the signing script to re-sign metadata before publishing."
|
||||
314
devops/trust-repo-template/scripts/init-tuf-repo.sh
Normal file
314
devops/trust-repo-template/scripts/init-tuf-repo.sh
Normal file
@@ -0,0 +1,314 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------
|
||||
# init-tuf-repo.sh
|
||||
# Sprint: SPRINT_20260125_001_Attestor_tuf_trust_foundation
|
||||
# Task: TUF-006 - Create TUF repository structure template
|
||||
# Description: Initialize a new TUF repository with signing keys
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEMPLATE_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <output-directory> [options]"
|
||||
echo ""
|
||||
echo "Initialize a new TUF repository for StellaOps trust distribution."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --key-type TYPE Key algorithm: ed25519 (default), ecdsa-p256"
|
||||
echo " --root-expiry DAYS Root metadata expiry (default: 365)"
|
||||
echo " --force Overwrite existing repository"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 /var/lib/stellaops/trust-repo --key-type ed25519"
|
||||
exit 1
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
OUTPUT_DIR=""
|
||||
KEY_TYPE="ed25519"
|
||||
ROOT_EXPIRY=365
|
||||
FORCE=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--key-type)
|
||||
KEY_TYPE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--root-expiry)
|
||||
ROOT_EXPIRY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--force)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$OUTPUT_DIR" ]]; then
|
||||
OUTPUT_DIR="$1"
|
||||
else
|
||||
log_error "Unknown argument: $1"
|
||||
usage
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$OUTPUT_DIR" ]]; then
|
||||
log_error "Output directory is required"
|
||||
usage
|
||||
fi
|
||||
|
||||
# Check if directory exists
|
||||
if [[ -d "$OUTPUT_DIR" ]] && [[ "$FORCE" != "true" ]]; then
|
||||
log_error "Directory already exists: $OUTPUT_DIR"
|
||||
log_error "Use --force to overwrite"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create directory structure
|
||||
log_info "Creating TUF repository at: $OUTPUT_DIR"
|
||||
mkdir -p "$OUTPUT_DIR/keys" "$OUTPUT_DIR/targets"
|
||||
|
||||
# Generate keys
|
||||
log_info "Generating signing keys (type: $KEY_TYPE)..."
|
||||
|
||||
generate_key() {
|
||||
local name=$1
|
||||
local key_file="$OUTPUT_DIR/keys/$name"
|
||||
|
||||
case $KEY_TYPE in
|
||||
ed25519)
|
||||
# Generate Ed25519 key pair
|
||||
openssl genpkey -algorithm ED25519 -out "$key_file.pem" 2>/dev/null
|
||||
openssl pkey -in "$key_file.pem" -pubout -out "$key_file.pub" 2>/dev/null
|
||||
;;
|
||||
ecdsa-p256)
|
||||
# Generate ECDSA P-256 key pair
|
||||
openssl ecparam -name prime256v1 -genkey -noout -out "$key_file.pem" 2>/dev/null
|
||||
openssl ec -in "$key_file.pem" -pubout -out "$key_file.pub" 2>/dev/null
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown key type: $KEY_TYPE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
chmod 600 "$key_file.pem"
|
||||
log_info " Generated: $name"
|
||||
}
|
||||
|
||||
generate_key "root"
|
||||
generate_key "snapshot"
|
||||
generate_key "timestamp"
|
||||
generate_key "targets"
|
||||
|
||||
# Calculate expiration dates
|
||||
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
ROOT_EXPIRES=$(date -u -d "+${ROOT_EXPIRY} days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+${ROOT_EXPIRY}d +%Y-%m-%dT%H:%M:%SZ)
|
||||
SNAPSHOT_EXPIRES=$(date -u -d "+7 days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+7d +%Y-%m-%dT%H:%M:%SZ)
|
||||
TIMESTAMP_EXPIRES=$(date -u -d "+1 day" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1d +%Y-%m-%dT%H:%M:%SZ)
|
||||
TARGETS_EXPIRES=$(date -u -d "+30 days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+30d +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Get key IDs (SHA256 of public key)
|
||||
get_key_id() {
|
||||
local pubkey_file=$1
|
||||
openssl pkey -pubin -in "$pubkey_file" -outform DER 2>/dev/null | openssl dgst -sha256 -hex | awk '{print $2}'
|
||||
}
|
||||
|
||||
ROOT_KEY_ID=$(get_key_id "$OUTPUT_DIR/keys/root.pub")
|
||||
SNAPSHOT_KEY_ID=$(get_key_id "$OUTPUT_DIR/keys/snapshot.pub")
|
||||
TIMESTAMP_KEY_ID=$(get_key_id "$OUTPUT_DIR/keys/timestamp.pub")
|
||||
TARGETS_KEY_ID=$(get_key_id "$OUTPUT_DIR/keys/targets.pub")
|
||||
|
||||
# Create root.json
|
||||
log_info "Creating metadata files..."
|
||||
|
||||
cat > "$OUTPUT_DIR/root.json" << EOF
|
||||
{
|
||||
"signed": {
|
||||
"_type": "root",
|
||||
"spec_version": "1.0.0",
|
||||
"version": 1,
|
||||
"expires": "$ROOT_EXPIRES",
|
||||
"keys": {
|
||||
"$ROOT_KEY_ID": {
|
||||
"keytype": "$KEY_TYPE",
|
||||
"scheme": "$KEY_TYPE",
|
||||
"keyval": {
|
||||
"public": "$(base64 -w0 "$OUTPUT_DIR/keys/root.pub")"
|
||||
}
|
||||
},
|
||||
"$SNAPSHOT_KEY_ID": {
|
||||
"keytype": "$KEY_TYPE",
|
||||
"scheme": "$KEY_TYPE",
|
||||
"keyval": {
|
||||
"public": "$(base64 -w0 "$OUTPUT_DIR/keys/snapshot.pub")"
|
||||
}
|
||||
},
|
||||
"$TIMESTAMP_KEY_ID": {
|
||||
"keytype": "$KEY_TYPE",
|
||||
"scheme": "$KEY_TYPE",
|
||||
"keyval": {
|
||||
"public": "$(base64 -w0 "$OUTPUT_DIR/keys/timestamp.pub")"
|
||||
}
|
||||
},
|
||||
"$TARGETS_KEY_ID": {
|
||||
"keytype": "$KEY_TYPE",
|
||||
"scheme": "$KEY_TYPE",
|
||||
"keyval": {
|
||||
"public": "$(base64 -w0 "$OUTPUT_DIR/keys/targets.pub")"
|
||||
}
|
||||
}
|
||||
},
|
||||
"roles": {
|
||||
"root": {
|
||||
"keyids": ["$ROOT_KEY_ID"],
|
||||
"threshold": 1
|
||||
},
|
||||
"snapshot": {
|
||||
"keyids": ["$SNAPSHOT_KEY_ID"],
|
||||
"threshold": 1
|
||||
},
|
||||
"timestamp": {
|
||||
"keyids": ["$TIMESTAMP_KEY_ID"],
|
||||
"threshold": 1
|
||||
},
|
||||
"targets": {
|
||||
"keyids": ["$TARGETS_KEY_ID"],
|
||||
"threshold": 1
|
||||
}
|
||||
},
|
||||
"consistent_snapshot": true
|
||||
},
|
||||
"signatures": []
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create targets.json
|
||||
cat > "$OUTPUT_DIR/targets.json" << EOF
|
||||
{
|
||||
"signed": {
|
||||
"_type": "targets",
|
||||
"spec_version": "1.0.0",
|
||||
"version": 1,
|
||||
"expires": "$TARGETS_EXPIRES",
|
||||
"targets": {}
|
||||
},
|
||||
"signatures": []
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create snapshot.json
|
||||
cat > "$OUTPUT_DIR/snapshot.json" << EOF
|
||||
{
|
||||
"signed": {
|
||||
"_type": "snapshot",
|
||||
"spec_version": "1.0.0",
|
||||
"version": 1,
|
||||
"expires": "$SNAPSHOT_EXPIRES",
|
||||
"meta": {
|
||||
"targets.json": {
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"signatures": []
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create timestamp.json
|
||||
cat > "$OUTPUT_DIR/timestamp.json" << EOF
|
||||
{
|
||||
"signed": {
|
||||
"_type": "timestamp",
|
||||
"spec_version": "1.0.0",
|
||||
"version": 1,
|
||||
"expires": "$TIMESTAMP_EXPIRES",
|
||||
"meta": {
|
||||
"snapshot.json": {
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"signatures": []
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create sample service map
|
||||
cat > "$OUTPUT_DIR/targets/sigstore-services-v1.json" << EOF
|
||||
{
|
||||
"version": 1,
|
||||
"rekor": {
|
||||
"url": "https://rekor.sigstore.dev",
|
||||
"log_id": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
|
||||
"public_key_target": "rekor-key-v1"
|
||||
},
|
||||
"fulcio": {
|
||||
"url": "https://fulcio.sigstore.dev",
|
||||
"root_cert_target": "fulcio-chain.pem"
|
||||
},
|
||||
"ct_log": {
|
||||
"url": "https://ctfe.sigstore.dev"
|
||||
},
|
||||
"overrides": {
|
||||
"staging": {
|
||||
"rekor_url": "https://rekor.sigstage.dev",
|
||||
"fulcio_url": "https://fulcio.sigstage.dev"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"updated_at": "$NOW",
|
||||
"note": "Production Sigstore endpoints"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Copy scripts
|
||||
cp "$TEMPLATE_DIR/scripts/add-target.sh" "$OUTPUT_DIR/scripts/" 2>/dev/null || true
|
||||
cp "$TEMPLATE_DIR/scripts/update-timestamp.sh" "$OUTPUT_DIR/scripts/" 2>/dev/null || true
|
||||
mkdir -p "$OUTPUT_DIR/scripts"
|
||||
|
||||
log_info ""
|
||||
log_info "TUF repository initialized successfully!"
|
||||
log_info ""
|
||||
log_info "Directory structure:"
|
||||
log_info " $OUTPUT_DIR/"
|
||||
log_info " ├── keys/ # Signing keys (keep root key offline!)"
|
||||
log_info " ├── targets/ # Target files"
|
||||
log_info " ├── root.json # Root metadata"
|
||||
log_info " ├── snapshot.json # Snapshot metadata"
|
||||
log_info " ├── timestamp.json # Timestamp metadata"
|
||||
log_info " └── targets.json # Targets metadata"
|
||||
log_info ""
|
||||
log_warn "IMPORTANT: The metadata files are NOT YET SIGNED."
|
||||
log_warn "Run the signing script before publishing:"
|
||||
log_warn " ./scripts/sign-metadata.sh $OUTPUT_DIR"
|
||||
log_info ""
|
||||
log_warn "SECURITY: Move the root key to offline storage after signing!"
|
||||
189
devops/trust-repo-template/scripts/revoke-target.sh
Normal file
189
devops/trust-repo-template/scripts/revoke-target.sh
Normal file
@@ -0,0 +1,189 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------
|
||||
# revoke-target.sh
|
||||
# Sprint: SPRINT_20260125_003_Attestor_trust_workflows_conformance
|
||||
# Task: WORKFLOW-002 - Create key rotation workflow script
|
||||
# Description: Remove a target from the TUF repository
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <target-name> [options]"
|
||||
echo ""
|
||||
echo "Remove a target from the TUF repository."
|
||||
echo ""
|
||||
echo "Arguments:"
|
||||
echo " target-name Name of target to remove (e.g., rekor-key-v1)"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --repo DIR TUF repository directory (default: current directory)"
|
||||
echo " --archive Archive target file instead of deleting"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 rekor-key-v1 --repo /path/to/tuf --archive"
|
||||
exit 1
|
||||
}
|
||||
|
||||
TARGET_NAME=""
|
||||
REPO_DIR="."
|
||||
ARCHIVE=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--repo) REPO_DIR="$2"; shift 2 ;;
|
||||
--archive) ARCHIVE=true; shift ;;
|
||||
-h|--help) usage ;;
|
||||
-*)
|
||||
log_error "Unknown option: $1"
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$TARGET_NAME" ]]; then
|
||||
TARGET_NAME="$1"
|
||||
else
|
||||
log_error "Unexpected argument: $1"
|
||||
usage
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$TARGET_NAME" ]]; then
|
||||
log_error "Target name is required"
|
||||
usage
|
||||
fi
|
||||
|
||||
TARGETS_DIR="$REPO_DIR/targets"
|
||||
TARGETS_JSON="$REPO_DIR/targets.json"
|
||||
|
||||
if [[ ! -d "$TARGETS_DIR" ]]; then
|
||||
log_error "Targets directory not found: $TARGETS_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$TARGETS_JSON" ]]; then
|
||||
log_error "targets.json not found: $TARGETS_JSON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find the target file
|
||||
TARGET_FILE=""
|
||||
for ext in "" ".pub" ".json" ".pem"; do
|
||||
if [[ -f "$TARGETS_DIR/${TARGET_NAME}${ext}" ]]; then
|
||||
TARGET_FILE="$TARGETS_DIR/${TARGET_NAME}${ext}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$TARGET_FILE" ]]; then
|
||||
log_warn "Target file not found in $TARGETS_DIR"
|
||||
log_info "Continuing to remove from targets.json..."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo " TUF Target Revocation"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
log_info "Repository: $REPO_DIR"
|
||||
log_info "Target: $TARGET_NAME"
|
||||
if [[ -n "$TARGET_FILE" ]]; then
|
||||
log_info "File: $TARGET_FILE"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
log_warn "This will remove the target from the TUF repository."
|
||||
log_warn "Clients will no longer be able to fetch this target after sync."
|
||||
read -p "Type 'REVOKE' to proceed: " CONFIRM
|
||||
if [[ "$CONFIRM" != "REVOKE" ]]; then
|
||||
log_error "Aborted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove or archive the file
|
||||
if [[ -n "$TARGET_FILE" ]]; then
|
||||
if [[ "$ARCHIVE" == "true" ]]; then
|
||||
ARCHIVE_DIR="$REPO_DIR/archived"
|
||||
mkdir -p "$ARCHIVE_DIR"
|
||||
TIMESTAMP=$(date -u +%Y%m%d%H%M%S)
|
||||
ARCHIVE_NAME="$(basename "$TARGET_FILE")-revoked-${TIMESTAMP}"
|
||||
mv "$TARGET_FILE" "$ARCHIVE_DIR/$ARCHIVE_NAME"
|
||||
log_info "Archived to: $ARCHIVE_DIR/$ARCHIVE_NAME"
|
||||
else
|
||||
rm -f "$TARGET_FILE"
|
||||
log_info "Deleted: $TARGET_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update targets.json
|
||||
if command -v python3 &>/dev/null; then
|
||||
python3 - "$TARGETS_JSON" "$TARGET_NAME" << 'PYTHON_SCRIPT'
|
||||
import json
|
||||
import sys
|
||||
|
||||
targets_json = sys.argv[1]
|
||||
target_name = sys.argv[2]
|
||||
|
||||
with open(targets_json) as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Find and remove the target
|
||||
targets = data.get('signed', {}).get('targets', {})
|
||||
removed = False
|
||||
|
||||
# Try different name variations
|
||||
names_to_try = [
|
||||
target_name,
|
||||
f"{target_name}.pub",
|
||||
f"{target_name}.json",
|
||||
f"{target_name}.pem"
|
||||
]
|
||||
|
||||
for name in names_to_try:
|
||||
if name in targets:
|
||||
del targets[name]
|
||||
removed = True
|
||||
print(f"Removed from targets.json: {name}")
|
||||
break
|
||||
|
||||
if not removed:
|
||||
print(f"Warning: Target '{target_name}' not found in targets.json")
|
||||
sys.exit(0)
|
||||
|
||||
# Update version
|
||||
if 'signed' in data:
|
||||
data['signed']['version'] = data['signed'].get('version', 0) + 1
|
||||
|
||||
with open(targets_json, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
print(f"Updated: {targets_json}")
|
||||
PYTHON_SCRIPT
|
||||
else
|
||||
log_warn "Python not available. Manual update of targets.json required."
|
||||
log_warn "Remove the '$TARGET_NAME' entry from $TARGETS_JSON"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Target revocation prepared."
|
||||
echo ""
|
||||
log_warn "NEXT STEPS (REQUIRED):"
|
||||
echo " 1. Re-sign targets.json with targets key"
|
||||
echo " 2. Update snapshot.json and sign with snapshot key"
|
||||
echo " 3. Update timestamp.json and sign with timestamp key"
|
||||
echo " 4. Deploy updated metadata to TUF server"
|
||||
echo ""
|
||||
log_info "Clients will stop trusting '$TARGET_NAME' after their next sync."
|
||||
echo ""
|
||||
Reference in New Issue
Block a user