SAW-Kinoite/package-verification/verify-signature.sh
2026-04-02 17:23:23 -05:00

399 lines
11 KiB
Bash

#!/bin/bash
# Package Signature Verification Script for SAW
# Version: 1.0
# Date: 2026-04-02
#
# This script verifies package signatures against your custom Certificate Authority (CA).
# It ensures that only packages signed by your trusted CA can be installed on the system.
#
# Usage: sudo ./verify-signature.sh <package.rpm>
# sudo ./verify-signature.sh --verify-all
# sudo ./verify-signature.sh --check-all
#
# SECURITY RATIONALE:
# Package verification is critical for security because it:
# 1. Prevents installation of malicious packages (tampering detection)
# 2. Ensures package integrity (not modified in transit)
# 3. Authenticates package source (only your CA can sign)
# 4. Provides non-repudiation (can prove who signed the package)
# 5. Prevents supply chain attacks
# 6. Blocks unsigned packages completely
#
# Implementation Details:
# - Uses RPM GPG signature verification
# - Verifies against custom CA public key
# - Checks CRL (Certificate Revocation List) for revoked certificates
# - Logs all verification attempts
#
# Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/
set -e
# Configuration
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
LOG_FILE="/var/log/package-verification.log"
VERIFY_SCRIPT_DIR="/usr/local/bin"
CONFIG_FILE="/etc/saw/package-verification.conf"
# 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
}
# Check if CA key exists
check_ca_key() {
if [ ! -f "$CA_KEY_PATH" ]; then
log_error "CA key not found: $CA_KEY_PATH"
log_info "Package verification requires CA key"
log_info "Copy your CA public key to: $CA_KEY_PATH"
exit 1
fi
log_info "CA key found: $CA_KEY_PATH"
}
# Verify a single package
#
# SECURITY RATIONALE:
# Verifying individual package signatures ensures:
# 1. Package wasn't tampered with during download
# 2. Package comes from trusted source
# 3. Package wasn't modified by attacker
# 4. Package hasn't been replaced with malicious version
#
# The verification process:
# 1. Extract package header
# 2. Verify RSA signature using CA public key
# 3. Compare hash of package contents
# 4. Check signature validity
#
# If verification fails, the package should NOT be installed.
verify_package() {
local package="$1"
log_info "Verifying package: $package"
# Check if file exists
if [ ! -f "$package" ]; then
log_error "Package not found: $package"
return 1
fi
# Verify package signature
if ! rpm --checksig "$package" > /dev/null 2>&1; then
log_error "Package signature verification failed: $package"
log_info "Package may be tampered or unsigned"
return 1
fi
# Extract signature info
local sig_info=$(rpm --checksig "$package" 2>&1)
# Check if signed with our CA
if echo "$sig_info" | grep -q "$CA_KEY_PATH"; then
log_info "Package verified successfully (signed with CA)"
else
log_warning "Package signed but not with our CA"
log_info "Signature info: $sig_info"
return 1
fi
# Verify CRL if available
if [ -f "$CRL_FILE" ]; then
log_info "Checking CRL for revoked certificates..."
# Extract certificate ID from signature
local cert_id=$(rpm --queryformat='%{SIGPGP:pgpsig}\n' -p "$package" 2>/dev/null | head -1 | awk '{print $2}' | cut -d: -f2)
if [ -n "$cert_id" ]; then
# Check if certificate is revoked
if openssl crl -in "$CRL_FILE" -noout -text 2>/dev/null | grep -q "$cert_id"; then
log_error "Package signed with REVOKED certificate: $cert_id"
return 1
fi
log_info "Certificate not revoked: $cert_id"
fi
fi
log_info "Package verified successfully: $package"
return 0
}
# Verify all installed packages
#
# SECURITY RATIONALE:
# Regular verification of all installed packages ensures:
# 1. No packages were tampered with after installation
# 2. No malicious packages were added
# 3. All packages are still properly signed
# 4. Any compromised packages are detected
#
# This should be run regularly (weekly/monthly) to ensure
# the integrity of the entire system.
verify_all_packages() {
log_info "Verifying all installed packages..."
local failed=0
local total=0
local revoked=0
# Get list of all installed packages
local packages=$(rpm -qa --queryformat='%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\n' 2>/dev/null)
for pkg in $packages; do
# Get package file path
local pkg_path=$(rpm -ql "$pkg" 2>/dev/null | head -1 | xargs dirname)
local pkg_name=$(echo "$pkg" | sed 's/\.rpm$//')
# Check if package is signed
if rpm --queryformat='%{SIGPGP:pgpsig}\n' -q "$pkg_name" 2>/dev/null | grep -q "0x0"; then
log_warning "Package $pkg_name has no signature"
((failed++))
else
# Check signature
if ! rpm --queryformat='%{SIGPGP:pgpsig}\n' -q "$pkg_name" 2>/dev/null | grep -q "0x"; then
log_warning "Package $pkg_name signature verification failed"
((failed++))
else
log_info "Package $pkg_name verified"
fi
fi
((total++))
done
echo ""
echo "=== Verification Summary ==="
echo "Total packages: $total"
echo "Verified: $((total - failed))"
echo "Failed: $failed"
echo "Revoked: $revoked"
echo ""
if [ $failed -gt 0 ]; then
log_error "Some packages failed verification!"
return 1
fi
log_info "All packages verified successfully"
return 0
}
# Check DNF configuration
check_dnf_config() {
log_info "Checking DNF configuration..."
if [ ! -f /etc/dnf/dnf.conf ]; then
log_warning "DNF configuration not found"
return 1
fi
# Check if signature verification is enabled
if grep -q "^gpgcheck=1" /etc/dnf/dnf.conf; then
log_info "DNF signature verification enabled"
else
log_error "DNF signature verification NOT enabled"
return 1
fi
# Check if repo signature verification is enabled
if grep -q "^repo_gpgcheck=1" /etc/dnf/dnf.conf; then
log_info "DNF repo signature verification enabled"
else
log_error "DNF repo signature verification NOT enabled"
return 1
fi
return 0
}
# Verify DNF cache
verify_dnf_cache() {
log_info "Verifying DNF cache..."
# Check if DNF cache exists
if [ ! -d /var/cache/dnf ]; then
log_warning "DNF cache not found"
return 1
fi
# List cached packages
log_info "Cached packages:"
find /var/cache/dnf -name "*.rpm" -type f 2>/dev/null | while read -r pkg; do
local pkg_name=$(basename "$pkg")
if rpm --checksig "$pkg" > /dev/null 2>&1; then
log_info " $pkg_name: VERIFIED"
else
log_error " $pkg_name: FAILED"
fi
done
return 0
}
# Create package verification script for users
create_user_script() {
log_info "Creating user package verification script..."
cat > "$VERIFY_SCRIPT_DIR/verify-package.sh" << 'VERIFYSCRIPT'
#!/bin/bash
# Package verification script for SAW
# Usage: ./verify-package.sh <package.rpm>
CA_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
# Check if CA key exists
if [ ! -f "$CA_KEY" ]; then
echo "ERROR: CA key not found: $CA_KEY"
echo "Contact system administrator"
exit 1
fi
# Check if file is provided
if [ $# -lt 1 ]; then
echo "Usage: $0 <package.rpm>"
exit 1
fi
PACKAGE="$1"
# Check if file exists
if [ ! -f "$PACKAGE" ]; then
echo "ERROR: Package not found: $PACKAGE"
exit 1
fi
# Verify package signature
if ! rpm --checksig "$PACKAGE" > /dev/null 2>&1; then
echo "ERROR: Package signature verification failed: $PACKAGE"
echo "Package may be tampered or unsigned"
exit 1
fi
# Extract signature info
SIG_INFO=$(rpm --checksig "$PACKAGE" 2>&1)
# Check if signed with our CA
if echo "$SIG_INFO" | grep -q "$CA_KEY"; then
echo "OK: Package verified successfully (signed with CA)"
else
echo "WARNING: Package signed but not with our CA"
echo "Signature info: $SIG_INFO"
exit 1
fi
# Verify CRL if available
if [ -f "$CRL_FILE" ]; then
echo "Checking CRL for revoked certificates..."
if ! openssl crl -in "$CRL_FILE" -CAfile "$CA_KEY" -noout 2>/dev/null; then
echo "WARNING: CRL verification failed"
fi
fi
echo "Package verification complete"
exit 0
VERIFYSCRIPT
chmod +x "$VERIFY_SCRIPT_DIR/verify-package.sh"
log_info "User verification script created"
}
# Display help
show_help() {
cat << 'HELP'
Package Signature Verification Script for SAW
==============================================
Usage: ./verify-signature.sh [OPTIONS] <package.rpm>
Options:
--verify-all Verify all installed packages
--check-all Check all packages (same as --verify-all)
--check-dnf Check DNF configuration
--verify-dnf Verify DNF cache
--help Show this help message
Examples:
# Verify a single package
./verify-signature.sh package.rpm
# Verify all installed packages
./verify-signature.sh --verify-all
# Check DNF configuration
./verify-signature.sh --check-dnf
Security Notes:
- This script requires root privileges
- Only packages signed by your CA will pass verification
- Unsigned packages will be rejected
- Packages with revoked certificates will be rejected
Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/
HELP
}
# Main execution
main() {
echo ""
echo "=========================================="
echo " Package Signature Verification"
echo " Fedora Kinoite SAW"
echo "=========================================="
echo ""
check_root
check_ca_key
case "${1:-}" in
--verify-all|--check-all)
verify_all_packages
;;
--check-dnf)
check_dnf_config
;;
--verify-dnf)
verify_dnf_cache
;;
--help|-h)
show_help
;;
*)
if [ $# -lt 1 ]; then
show_help
exit 0
fi
verify_package "$1"
;;
esac
}
# Run main function
main "$@"