#!/bin/bash # Update Approval System for SAW # Version: 1.0 # Date: 2026-04-02 # # This script implements a controlled workflow for applying system updates, # ensuring that updates are reviewed and approved before being applied. # # Usage: sudo ./approve-update.sh # # SECURITY RATIONALE: # The update approval system provides: # 1. Prevents unauthorized updates # 2. Allows security team review before deployment # 3. Enables testing in staging environment # 4. Provides audit trail for updates # 5. Prevents automatic deployment of compromised updates # 6. Ensures updates don't break security controls # # How it works: # 1. Check for available updates # 2. Display update details to user # 3. Require explicit approval # 4. Log all update activities # 5. Enable easy rollback if needed # # Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/atomic-updates/ set -e # Configuration LOG_FILE="/var/log/update-approval.log" APPROVAL_LOG="/var/log/update-approval-records.log" LOCK_FILE="/var/lock/saw-update-lock" UPDATE_LOG="/var/log/saw-updates.log" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' 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" } log_approval() { local timestamp=$(date '+%Y-%m-%d %H:%M:%S') local user=$(whoami) echo "[$timestamp] User: $user - $1" | tee -a "$APPROVAL_LOG" } # 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 another update is running check_lock() { if [ -f "$LOCK_FILE" ]; then log_error "Another update is already running" log_info "Lock file: $LOCK_FILE" log_info "If this is an error, remove the lock file" exit 1 fi # Create lock file touch "$LOCK_FILE" log_info "Lock file created" # Set up trap to remove lock on exit trap 'rm -f "$LOCK_FILE"; log_info "Lock file removed"; exit' EXIT } # Check for updates check_updates() { log_info "Checking for updates..." # Use rpm-ostree to check for updates if ! rpm-ostree update --check 2>&1 | tee /tmp/update-check.txt; then # No updates available log_info "No updates available" return 1 fi # Save update information cp /tmp/update-check.txt /var/log/saw-latest-update.txt return 0 } # Display update details display_updates() { echo "" echo "==========================================" echo " Available Updates" echo "==========================================" echo "" if [ -f /tmp/update-check.txt ]; then cat /tmp/update-check.txt else echo "No update information available" fi echo "" echo "==========================================" echo "" } # Get approval # # SECURITY RATIONALE: # The approval process ensures: # 1. Authorized personnel review updates # 2. Updates are documented # 3. No surprise updates # 4. Accountability for update decisions # 5. Audit trail for compliance # # We use a simple yes/no approval with logging. get_approval() { echo "==========================================" echo " Update Approval Request" echo "==========================================" echo "" echo "The following updates are available:" echo "" cat /tmp/update-check.txt | head -50 echo "" echo "==========================================" echo "" # Ask for approval read -p "Do you want to apply these updates? (yes/no): " response case "$response" in yes|YES|y|Y) log_approval "APPROVED: User $USER approved update" return 0 ;; no|NO|n|N) log_approval "REJECTED: User $USER rejected update" log_info "Update cancelled by user" exit 0 ;; *) log_error "Invalid response" log_info "Please enter 'yes' or 'no'" exit 1 ;; esac } # Apply updates apply_updates() { log_info "Applying updates..." # Show warning echo "" echo "==========================================" echo " WARNING" echo "==========================================" echo "" echo "This will apply system updates." echo "A reboot may be required." echo "Ensure you have backups." echo "" echo "Updates will be applied to:" echo "- System packages" echo "- Kernel" echo "- Security updates" echo "" echo "This process may take several minutes." echo "" read -p "Are you sure you want to proceed? (yes/no): " confirm if [ "$confirm" != "yes" ]; then log_approval "CANCELLED: User $USER cancelled update" log_info "Update cancelled" exit 0 fi log_approval "STARTED: User $USER started update" # Apply updates if rpm-ostree upgrade; then log_approval "COMPLETED: User $USER completed update" log_info "Updates applied successfully" log_info "A reboot is recommended" return 0 else log_approval "FAILED: User $USER update failed" log_error "Update failed" return 1 fi } # Show update history show_history() { echo "" echo "==========================================" echo " Update History" echo "==========================================" echo "" if [ -f "$APPROVAL_LOG" ]; then cat "$APPROVAL_LOG" | tail -20 else echo "No update history available" fi echo "" } # Show update status show_status() { echo "" echo "==========================================" echo " Update Status" echo "==========================================" echo "" # Show current deployment echo "Current Deployment:" rpm-ostree status | head -5 echo "" # Show pending deployment echo "Pending Deployment:" if [ -f /tmp/update-check.txt ]; then echo "Updates available for review" else echo "No updates pending" fi echo "" # Show last update echo "Last Update:" if [ -f /var/log/saw-latest-update.txt ]; then echo "Last update check: $(stat -c %y /var/log/saw-latest-update.txt)" else echo "No recent update check" fi echo "" } # Setup update check cron job setup_cron() { log_info "Setting up update check cron job..." # Create cron job for daily check cat > /etc/cron.d/saw-update-check << 'CRONJOB' # SAW Update Check - Daily at 6:00 AM # Only downloads updates, does not apply them 0 6 * * * root /usr/bin/rpm-ostree update --check >> /var/log/saw-updates.log 2>&1 CRONJOB chmod 644 /etc/cron.d/saw-update-check log_info "Cron job created" log_info "Updates will be checked daily at 6:00 AM" } # Display help show_help() { cat << 'HELP' Update Approval System for SAW ============================== Usage: ./approve-update.sh [OPTIONS] Options: --check Check for available updates --approve Check and prompt for approval --apply Apply updates (requires approval first) --history Show update history --status Show update status --cron Set up daily update check --help Show this help message Examples: # Check for updates ./approve-update.sh --check # Check and prompt for approval ./approve-update.sh --approve # Apply updates ./approve-update.sh --apply Security Notes: - Updates require explicit approval - All approval decisions are logged - Updates can be reviewed before applying - Rollback is available if needed Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/atomic-updates/ HELP } # Main execution main() { echo "" echo "==========================================" echo " Update Approval System" echo " Fedora Kinoite SAW" echo "==========================================" echo "" check_root check_lock case "${1:-}" in --check) if check_updates; then display_updates log_info "Updates available for review" else log_info "No updates available" fi ;; --approve) check_updates display_updates get_approval ;; --apply) if [ ! -f /tmp/update-check.txt ]; then log_error "No update information available" log_info "Run with --check first" exit 1 fi display_updates get_approval apply_updates ;; --history) show_history ;; --status) show_status ;; --cron) setup_cron ;; --help|-h) show_help ;; *) show_help ;; esac } # Run main function main "$@"