444 lines
12 KiB
Bash
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 "$@" |