2026-04-02 17:23:23 -05:00

444 lines
12 KiB
Bash

#!/bin/bash
# CRL (Certificate Revocation List) Update Script for SAW
# Version: 1.0
# Date: 2026-04-02
#
# This script downloads, verifies, and installs the Certificate Revocation List (CRL)
# from your Certificate Authority (CA) server. It ensures that packages signed with
# revoked certificates are not installed.
#
# Usage: sudo ./verify-crl.sh [--download] [--verify] [--update] [--status]
#
# SECURITY RATIONALE:
# CRL checking is critical for security because it:
# 1. Detects compromised CA keys
# 2. Revokes access for compromised certificates
# 3. Prevents installation of packages signed with revoked keys
# 4. Provides a mechanism to respond to security incidents
# 5. Meets compliance requirements (PCI-DSS, HIPAA, etc.)
#
# How CRL works:
# 1. CA maintains a list of revoked certificates
# 2. CRL is signed by CA to prevent tampering
# 3. System downloads CRL regularly
# 4. Before installing package, checks if certificate is revoked
# 5. If revoked, installation is blocked
#
# Reference: https://www.openssl.org/docs/man1.1.1/man1/crl.html
set -e
# Configuration
CRL_URL="https://your-ca-server.com/crl.pem"
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
CRL_CACHE_FILE="/var/cache/crl/crl.pem"
LOG_FILE="/var/log/crl-update.log"
CRL_UPDATE_SCRIPT="/usr/local/bin/daily-crl-update.sh"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Print functions
log_info() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${GREEN}[$timestamp]${NC} ${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
}
log_warning() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${YELLOW}[$timestamp]${NC} ${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}
log_error() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${RED}[$timestamp]${NC} ${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}
# Check if running as root
check_root() {
if [ "$EUID" -ne 0 ]; then
log_error "This script must be run as root"
exit 1
fi
}
# Download CRL from CA server
download_crl() {
log_info "Downloading CRL from: $CRL_URL"
# Check if URL is configured
if [ "$CRL_URL" = "https://your-ca-server.com/crl.pem" ]; then
log_error "CRL URL not configured"
log_info "Edit the script and set CRL_URL to your CA server"
exit 1
fi
# Create cache directory
mkdir -p /var/cache/crl
# Download CRL
if ! curl -s -o "$CRL_CACHE_FILE" "$CRL_URL"; then
log_error "Failed to download CRL"
exit 1
fi
log_info "CRL downloaded to: $CRL_CACHE_FILE"
# Verify download was successful
if [ ! -f "$CRL_CACHE_FILE" ]; then
log_error "CRL file not created"
exit 1
fi
if [ ! -s "$CRL_CACHE_FILE" ]; then
log_error "CRL file is empty"
exit 1
fi
log_info "CRL file size: $(du -h "$CRL_CACHE_FILE" | cut -f1)"
}
# Verify CRL signature
#
# SECURITY RATIONALE:
# Verifying the CRL signature ensures:
# 1. CRL hasn't been tampered with during download
# 2. CRL comes from your trusted CA
# 3. CRL is authentic and hasn't been replaced by attacker
# 4. CRL is valid and hasn't expired
#
# The verification process:
# 1. Check if CRL is properly formatted
# 2. Verify CRL signature using CA public key
# 3. Check CRL validity period
# 4. Ensure CRL hasn't expired
verify_crl() {
log_info "Verifying CRL signature..."
# Check if CA key exists
if [ ! -f "$CA_KEY_PATH" ]; then
log_error "CA key not found: $CA_KEY_PATH"
exit 1
fi
# Check if CRL exists
if [ ! -f "$CRL_CACHE_FILE" ]; then
log_error "CRL not found: $CRL_CACHE_FILE"
log_info "Run with --download first"
exit 1
fi
# Verify CRL format
if ! openssl crl -in "$CRL_CACHE_FILE" -noout 2>/dev/null; then
log_error "CRL format is invalid"
exit 1
fi
# Verify CRL signature
if ! openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then
log_error "CRL signature verification failed"
exit 1
fi
# Get CRL information
local crl_info=$(openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null)
# Extract CRL dates
local this_update=$(echo "$crl_info" | grep "Last Update:" | head -1)
local next_update=$(echo "$crl_info" | grep "Next Update:" | head -1)
log_info "CRL Last Update: $this_update"
log_info "CRL Next Update: $next_update"
# Check if CRL is expired
local next_update_date=$(echo "$next_update" | awk '{print $4, $5, $6, $7, $8, $9}')
local current_date=$(date)
if [[ "$current_date" > "$next_update_date" ]]; then
log_warning "CRL has expired!"
log_warning "CRL should be updated immediately"
else
log_info "CRL is valid"
fi
log_info "CRL signature verified successfully"
}
# Update CA trust with CRL
update_ca_trust() {
log_info "Updating CA trust database..."
# Copy CRL to CA trust source
mkdir -p /etc/pki/ca-trust/source/anchors
cp "$CRL_CACHE_FILE" /etc/pki/ca-trust/source/anchors/crl.pem
# Update CA trust
update-ca-trust extract
log_info "CA trust database updated"
}
# Check revoked certificates
check_revoked() {
log_info "Checking for revoked certificates..."
# Check if CRL exists
if [ ! -f "$CRL_FILE" ]; then
log_warning "CRL not found: $CRL_FILE"
log_info "Run with --download and --verify first"
return 1
fi
# Check if CA key exists
if [ ! -f "$CA_KEY_PATH" ]; then
log_error "CA key not found: $CA_KEY_PATH"
exit 1
fi
# Get list of installed packages with signatures
local revoked_count=0
local total_count=0
log_info "Scanning installed packages..."
# Get all installed packages
local packages=$(rpm -qa --queryformat='%{NAME} %{SIGPGP:pgpsig}\n' 2>/dev/null | head -100)
for pkg in $packages; do
# Extract certificate ID from signature
local cert_id=$(echo "$pkg" | awk '{print $2}' | cut -d: -f2)
if [ -n "$cert_id" ]; then
# Check if certificate is in CRL
if openssl crl -in "$CRL_FILE" -noout -text 2>/dev/null | grep -q "$cert_id"; then
log_error "REVOKED: Package signed with revoked certificate: $cert_id"
((revoked_count++))
fi
fi
((total_count++))
done
echo ""
echo "=== Revocation Check Summary ==="
echo "Total packages checked: $total_count"
echo "Revoked certificates found: $revoked_count"
echo ""
if [ $revoked_count -gt 0 ]; then
log_error "WARNING: $revoked_count packages have revoked certificates!"
log_info "These packages should be removed and reinstalled"
return 1
fi
log_info "No revoked certificates found"
return 0
}
# Display CRL status
show_status() {
echo ""
echo "=== CRL Status ==="
echo ""
# Check if CRL exists
if [ -f "$CRL_CACHE_FILE" ]; then
echo "CRL Cache File: $CRL_CACHE_FILE"
echo "Size: $(du -h "$CRL_CACHE_FILE" | cut -f1)"
echo "Modified: $(stat -c %y "$CRL_CACHE_FILE")"
echo ""
# Show CRL information
if openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null | head -10; then
echo ""
fi
# Check if valid
if openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then
echo "Status: VALID"
echo "Signed by: $CA_KEY_PATH"
else
echo "Status: INVALID"
fi
else
echo "CRL Cache File: NOT FOUND"
echo "Status: CRL has not been downloaded"
fi
echo ""
echo "CA Key: $CA_KEY_PATH"
if [ -f "$CA_KEY_PATH" ]; then
echo "Status: FOUND"
else
echo "Status: NOT FOUND"
fi
echo ""
}
# Create daily CRL update script
create_daily_script() {
log_info "Creating daily CRL update script..."
cat > "$CRL_UPDATE_SCRIPT" << 'DAILYSCHEDULE'
#!/bin/bash
# Daily CRL update script for SAW
# This script is run by cron to keep CRL up to date
CRL_URL="https://your-ca-server.com/crl.pem"
CRL_CACHE_FILE="/var/cache/crl/crl.pem"
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
LOG_FILE="/var/log/crl-update.log"
# Function to log
log_info() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
log_error() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" >> "$LOG_FILE"
}
log_warning() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $1" >> "$LOG_FILE"
}
log_info "Starting CRL update"
# Check if CRL URL is configured
if [ "$CRL_URL" = "https://your-ca-server.com/crl.pem" ]; then
log_error "CRL URL not configured"
exit 1
fi
# Download CRL
if ! curl -s -o "$CRL_CACHE_FILE" "$CRL_URL"; then
log_error "Failed to download CRL"
exit 1
fi
log_info "CRL downloaded successfully"
# Verify CRL
if ! openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then
log_error "CRL verification failed"
exit 1
fi
log_info "CRL verified successfully"
# Update CA trust
update-ca-trust extract
log_info "CRL update completed successfully"
DAILYSCHEDULE
chmod +x "$CRL_UPDATE_SCRIPT"
log_info "Daily update script created: $CRL_UPDATE_SCRIPT"
}
# Set up cron job for daily updates
setup_cron() {
log_info "Setting up cron job for daily CRL updates..."
# Create cron job
cat > /etc/cron.d/saw-crl-update << 'CRONJOB'
# Daily CRL update for SAW
# Run at 2:00 AM
0 2 * * * root /usr/local/bin/daily-crl-update.sh >> /var/log/crl-update.log 2>&1
CRONJOB
chmod 644 /etc/cron.d/saw-crl-update
log_info "Cron job created"
log_info "CRL will be updated daily at 2:00 AM"
}
# Display help
show_help() {
cat << 'HELP'
CRL Update Script for SAW
=========================
Usage: ./verify-crl.sh [OPTIONS]
Options:
--download Download CRL from CA server
--verify Verify CRL signature
--update Update CA trust database
--check Check for revoked certificates
--status Show CRL status
--cron Set up daily cron job
--help Show this help message
Examples:
# Download and verify CRL
./verify-crl.sh --download --verify --update
# Check status
./verify-crl.sh --status
# Check for revoked certificates
./verify-crl.sh --check
Security Notes:
- CRL should be updated daily
- CRL is signed by CA to prevent tampering
- Packages signed with revoked certificates will be blocked
- CRL expiration should be monitored
Reference: https://www.openssl.org/docs/man1.1.1/man1/crl.html
HELP
}
# Main execution
main() {
echo ""
echo "=========================================="
echo " CRL Update Script"
echo " Fedora Kinoite SAW"
echo "=========================================="
echo ""
check_root
case "${1:-}" in
--download)
download_crl
;;
--verify)
check_root
verify_crl
;;
--update)
check_root
verify_crl
update_ca_trust
;;
--check)
check_root
check_revoked
;;
--status)
show_status
;;
--cron)
check_root
create_daily_script
setup_cron
;;
--help|-h)
show_help
;;
*)
show_help
;;
esac
}
# Run main function
main "$@"