Content is user-generated and unverified.
#!/bin/bash # RHEL System Status Menu Script # Requires root privileges for some operations # Color codes for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' MAGENTA='\033[0;35m' NC='\033[0m' # Global variables UPDATES_AVAILABLE=0 LOG_OUTPUT=false LOG_FILE="" # Function to write output write_output() { local message="$1" echo -e "$message" if [ "$LOG_OUTPUT" = true ]; then echo -e "$message" | sed 's/\x1b\[[0-9;]*m//g' >> "$LOG_FILE" fi } # Function to start logging start_logging() { LOG_OUTPUT=true timestamp=$(date +%Y%m%d_%H%M%S) LOG_FILE="/tmp/rhel_security_audit_${timestamp}.txt" echo "==================================================" > "$LOG_FILE" echo "RHEL Security Audit Report" >> "$LOG_FILE" generated_date=$(date) echo "Generated: $generated_date" >> "$LOG_FILE" hostname_str=$(hostname) echo "Hostname: $hostname_str" >> "$LOG_FILE" echo "==================================================" >> "$LOG_FILE" echo "" >> "$LOG_FILE" } # Function to stop logging stop_logging() { if [ "$LOG_OUTPUT" = true ]; then echo "" >> "$LOG_FILE" echo "==================================================" >> "$LOG_FILE" echo "End of Report" >> "$LOG_FILE" echo "==================================================" >> "$LOG_FILE" LOG_OUTPUT=false echo "" echo -e "${GREEN}Report saved to: $LOG_FILE${NC}" echo "" fi } # Function to check if running as root check_root() { if [ $EUID -ne 0 ]; then echo -e "${RED}Warning: Some operations require root privileges${NC}" echo "Consider running with sudo for full functionality" echo "" fi } # HEALTH FUNCTIONS check_updates() { echo -e "${BLUE}=== DNF Update Status ===${NC}" if ! command -v dnf > /dev/null 2>&1; then echo -e "${RED}Error: dnf command not found${NC}" echo "DNF is not available on this system." echo "" if command -v yum > /dev/null 2>&1; then echo -e "${YELLOW}Found 'yum' instead. Would you like to use yum? (yes/no)${NC}" read use_yum if [ "$use_yum" = "yes" ]; then echo "Using yum instead of dnf..." check_updates_yum return fi elif command -v apt > /dev/null 2>&1; then echo -e "${YELLOW}This appears to be a Debian/Ubuntu system (apt found)${NC}" echo "This script is designed for RHEL systems." fi echo "" return fi echo "Refreshing package metadata..." # Clean DNF cache thoroughly dnf clean all > /dev/null 2>&1 # If cache directory exists and we have permissions, remove it completely if [ -d /var/cache/dnf ] && [ -w /var/cache/dnf ]; then rm -rf /var/cache/dnf/* > /dev/null 2>&1 fi dnf makecache 2>&1 | while IFS= read -r line; do if echo "$line" | grep -qvE "^[[:space:]]*$"; then echo -ne "\r\033[K$line" fi done echo "" if [ ${PIPESTATUS[0]} -ne 0 ]; then echo -e "${RED}Error: Failed to refresh metadata${NC}" echo "You may need root privileges or have repository issues" echo "" return fi echo "Checking for available updates..." temp_file=$(mktemp) # Run check-update silently, only save output to temp file dnf check-update > "$temp_file" 2>&1 check_exit=$? echo "" if [ $check_exit -eq 100 ]; then # Count only actual updates, excluding everything after "Obsoleting Packages" update_count=$(sed '/^Obsoleting Packages/,$d' "$temp_file" | \ grep -vE "^$|^Last metadata|^Security:|^Updating|^Installed|^Available|This system" | \ grep -E "^[a-zA-Z0-9]" | wc -l) if [ $update_count -gt 0 ]; then echo -e "${YELLOW}$update_count package(s) can be updated${NC}" echo "" echo -e "${CYAN}Available updates:${NC}" sed '/^Obsoleting Packages/,$d' "$temp_file" | \ grep -vE "^$|^Last metadata|^Security:|^Updating|^Installed|^Available|This system" | \ grep -E "^[a-zA-Z0-9]" UPDATES_AVAILABLE=1 echo "" echo -e "${CYAN}To apply these updates, select option 'U' from the Health menu${NC}" echo -e "${YELLOW}Note: Updates will be re-verified before installation${NC}" else echo -e "${GREEN}System is up to date. No packages need updating.${NC}" echo "" echo -e "${CYAN}Note: 'Obsoleting Packages' shown by dnf are informational only.${NC}" UPDATES_AVAILABLE=0 fi elif [ $check_exit -eq 0 ]; then echo -e "${GREEN}System is up to date. No packages need updating.${NC}" UPDATES_AVAILABLE=0 else echo -e "${RED}Error checking for updates (exit code: $check_exit)${NC}" echo "Error output:" cat "$temp_file" UPDATES_AVAILABLE=0 fi rm -f "$temp_file" echo "" } check_updates_yum() { echo -e "${BLUE}=== YUM Update Status ===${NC}" echo "Cleaning YUM cache..." yum clean all 2>&1 echo "Checking for available updates..." update_output=$(yum check-update 2>&1) check_exit=$? if [ $check_exit -eq 100 ]; then # Updates are available - stop processing at "Obsoleting Packages" update_count=$(sed '/^Obsoleting Packages/,$d' "$temp_check" | \ grep -vE "^$|^Last metadata|^Security:|^Updating|^Installed|^Available" | \ grep -E "^[a-zA-Z0-9]" | wc -l) if [ $update_count -gt 0 ]; then echo -e "${YELLOW}Found $update_count package(s) available for update:${NC}" echo "" sed '/^Obsoleting Packages/,$d' "$temp_check" | \ grep -vE "^$|^Last metadata|^Security:|^Updating|^Installed|^Available" | \ grep -E "^[a-zA-Z0-9]" | head -20 if [ $update_count -gt 20 ]; then echo "... and $((update_count - 20)) more" fi echo "" rm -f "$temp_check" echo -e "${YELLOW}This will update all packages on the system${NC}" echo -n "Are you sure you want to continue? (yes/no): " read confirm if [ "$confirm" = "yes" ]; then echo "" echo "Starting update process..." dnf update -y 2>&1 update_exit=$? if [ $update_exit -eq 0 ]; then echo "" echo -e "${GREEN}Update process completed successfully${NC}" UPDATES_AVAILABLE=0 else echo "" echo -e "${RED}Update process failed with exit code: $update_exit${NC}" echo "Please review the errors above" fi else echo "Update cancelled" fi else # No real updates, only obsoleting packages echo -e "${GREEN}System is up to date. No packages need updating.${NC}" echo "" echo -e "${CYAN}Note: 'Obsoleting Packages' are informational only and require no action.${NC}" UPDATES_AVAILABLE=0 rm -f "$temp_check" fi elif [ $check_exit -eq 0 ]; then echo -e "${GREEN}System is up to date. No packages need updating.${NC}" UPDATES_AVAILABLE=0 else echo -e "${RED}Error checking for updates (exit code: $check_exit)${NC}" echo "Error output:" echo "$update_output" UPDATES_AVAILABLE=0 fi echo "" } run_updates() { if ! command -v dnf > /dev/null 2>&1; then echo -e "${RED}Error: dnf command not found${NC}" echo "" if command -v yum > /dev/null 2>&1; then echo -e "${YELLOW}Found 'yum' instead. Would you like to use yum? (yes/no)${NC}" read use_yum if [ "$use_yum" = "yes" ]; then run_updates_yum return fi fi echo "" return fi echo -e "${BLUE}=== Running DNF Updates ===${NC}" echo "" # Clean cache thoroughly to ensure fresh metadata echo "Refreshing package metadata..." dnf clean all > /dev/null 2>&1 # If cache directory exists and we have permissions, remove it completely if [ -d /var/cache/dnf ] && [ -w /var/cache/dnf ]; then rm -rf /var/cache/dnf/* > /dev/null 2>&1 fi dnf makecache > /dev/null 2>&1 echo "" # Check what updates are actually available NOW echo "Verifying available updates..." temp_check=$(mktemp) dnf check-update > "$temp_check" 2>&1 check_exit=$? if [ $check_exit -eq 100 ]; then # Updates are available - stop processing at "Obsoleting Packages" update_count=$(sed '/^Obsoleting Packages/,$d' "$temp_check" | \ grep -vE "^$|^Last metadata|^Security:|^Updating|^Installed|^Available|This system" | \ grep -E "^[a-zA-Z0-9]" | wc -l) if [ $update_count -gt 0 ]; then echo -e "${YELLOW}Found $update_count package(s) available for update:${NC}" echo "" sed '/^Obsoleting Packages/,$d' "$temp_check" | \ grep -vE "^$|^Last metadata|^Security:|^Updating|^Installed|^Available|This system" | \ grep -E "^[a-zA-Z0-9]" | head -20 if [ $update_count -gt 20 ]; then echo "... and $((update_count - 20)) more" fi echo "" rm -f "$temp_check" echo -e "${YELLOW}This will update all packages on the system${NC}" echo -n "Are you sure you want to continue? (yes/no): " read confirm if [ "$confirm" = "yes" ]; then echo "" echo "Starting update process..." dnf update -y 2>&1 update_exit=$? if [ $update_exit -eq 0 ]; then echo "" echo -e "${GREEN}Update process completed successfully${NC}" UPDATES_AVAILABLE=0 else echo "" echo -e "${RED}Update process failed with exit code: $update_exit${NC}" echo "Please review the errors above" fi else echo "Update cancelled" fi else # No real updates, only obsoleting packages echo -e "${GREEN}System is up to date. No packages need updating.${NC}" echo "" echo -e "${CYAN}Note: 'Obsoleting Packages' are not updates and require no action.${NC}" UPDATES_AVAILABLE=0 rm -f "$temp_check" fi elif [ $check_exit -eq 0 ]; then # No updates available echo -e "${GREEN}System is up to date. No packages need updating.${NC}" echo "" echo -e "${CYAN}Tip: The system may have been updated since you checked.${NC}" UPDATES_AVAILABLE=0 rm -f "$temp_check" else # Error occurred echo -e "${RED}Error checking for updates (exit code: $check_exit)${NC}" echo "Error output:" cat "$temp_check" rm -f "$temp_check" fi echo "" } run_updates_yum() { echo -e "${BLUE}=== Running YUM Updates ===${NC}" echo -e "${YELLOW}This will update all packages on the system${NC}" echo -n "Are you sure you want to continue? (yes/no): " read confirm if [ "$confirm" = "yes" ]; then echo "" echo "Starting update process..." yum update -y 2>&1 update_exit=$? if [ $update_exit -eq 0 ]; then echo "" echo -e "${GREEN}Update process completed successfully${NC}" UPDATES_AVAILABLE=0 else echo "" echo -e "${RED}Update process failed with exit code: $update_exit${NC}" echo "Please review the errors above" fi else echo "Update cancelled" fi echo "" } check_services() { echo -e "${BLUE}=== Service Status Check ===${NC}" echo "" if ! command -v systemctl > /dev/null 2>&1; then echo -e "${RED}Error: systemctl command not found${NC}" echo "systemd is not available on this system." echo "" if command -v service > /dev/null 2>&1; then echo -e "${YELLOW}Trying 'service --status-all' instead:${NC}" service --status-all 2>&1 fi return fi echo -e "${YELLOW}=== Failed Services ===${NC}" failed_output=$(systemctl status --failed 2>&1) failed_exit=$? if [ $failed_exit -eq 0 ]; then echo "$failed_output" else echo -e "${RED}Error running systemctl status --failed (exit code: $failed_exit)${NC}" echo "$failed_output" fi echo "" failed_count=$(systemctl list-units --failed --no-legend 2>/dev/null | wc -l) if [ $failed_count -eq 0 ]; then echo -e "${GREEN}✓ No failed services${NC}" else echo -e "${RED}⚠ $failed_count failed service(s) detected${NC}" echo "" echo "Failed units:" systemctl list-units --failed --no-legend 2>&1 | while read line; do echo " $line" done fi echo "" echo -e "${YELLOW}=== Enabled Services ===${NC}" enabled_output=$(systemctl list-unit-files --state=enabled 2>&1) enabled_exit=$? if [ $enabled_exit -eq 0 ]; then enabled_count=$(echo "$enabled_output" | grep -c "enabled") echo "Total enabled services: $enabled_count" echo "" echo "Showing first 20 enabled services:" echo "$enabled_output" | head -20 else echo -e "${RED}Error running systemctl list-unit-files (exit code: $enabled_exit)${NC}" echo "$enabled_output" fi echo "" echo -e "${YELLOW}=== Currently Running Services ===${NC}" running_output=$(systemctl list-units --type=service --state=running --no-legend 2>&1) running_exit=$? if [ $running_exit -eq 0 ]; then running_count=$(echo "$running_output" | wc -l) echo "Total running services: $running_count" echo "" echo "Running services:" echo "$running_output" | while read line; do echo " $line" done else echo -e "${RED}Error listing running services (exit code: $running_exit)${NC}" echo "$running_output" fi echo "" echo -e "${CYAN}=== Summary ===${NC}" echo "• Failed services: $failed_count" echo "• Enabled services: $enabled_count" echo "• Running services: $running_count" echo "" } check_ldap_health() { echo -e "${BLUE}=== LDAP/AD Health Check ===${NC}" echo "" declare -a issues=() declare -a recommendations=() echo -e "${CYAN}Checking SSSD installation...${NC}" if ! command -v sssd > /dev/null 2>&1 && ! rpm -q sssd > /dev/null 2>&1; then echo -e "${RED}✗ SSSD is not installed${NC}" issues+=("SSSD not installed") recommendations+=("Install SSSD: sudo dnf install sssd sssd-tools") else echo -e "${GREEN}✓ SSSD is installed${NC}" fi echo "" echo -e "${CYAN}Checking SSSD service status...${NC}" if command -v systemctl > /dev/null 2>&1; then if systemctl is-active --quiet sssd; then echo -e "${GREEN}✓ SSSD service is running${NC}" else echo -e "${RED}✗ SSSD service is not running${NC}" issues+=("SSSD service not running") recommendations+=("Start SSSD: sudo systemctl start sssd") recommendations+=("Enable SSSD: sudo systemctl enable sssd") fi if systemctl is-enabled --quiet sssd; then echo -e "${GREEN}✓ SSSD service is enabled${NC}" else echo -e "${YELLOW}⚠ SSSD service is not enabled at boot${NC}" recommendations+=("Enable SSSD: sudo systemctl enable sssd") fi fi echo "" echo -e "${CYAN}Checking SSSD configuration...${NC}" sssd_conf="/etc/sssd/sssd.conf" if [ -f "$sssd_conf" ]; then echo -e "${GREEN}✓ SSSD config file exists${NC}" perms=$(stat -c "%a" "$sssd_conf" 2>/dev/null) if [ "$perms" = "600" ]; then echo -e "${GREEN}✓ SSSD config permissions are correct (600)${NC}" else echo -e "${RED}✗ SSSD config permissions are incorrect: $perms (should be 600)${NC}" issues+=("Incorrect SSSD config permissions") recommendations+=("Fix permissions: sudo chmod 600 $sssd_conf") fi if grep -q "^\[sssd\]" "$sssd_conf" 2>/dev/null; then echo -e "${GREEN}✓ [sssd] section found${NC}" else echo -e "${RED}✗ [sssd] section missing${NC}" issues+=("Missing [sssd] section in config") fi if grep -q "^\[domain/" "$sssd_conf" 2>/dev/null; then echo -e "${GREEN}✓ Domain configuration found${NC}" domain_name=$(grep "^\[domain/" "$sssd_conf" | head -1 | sed 's/\[domain\///' | sed 's/\]//') echo " Domain: $domain_name" else echo -e "${RED}✗ No domain configuration found${NC}" issues+=("No domain configuration in SSSD") recommendations+=("Configure domain in $sssd_conf") fi if grep -q "^ldap_uri" "$sssd_conf" 2>/dev/null; then ldap_uri=$(grep "^ldap_uri" "$sssd_conf" | head -1 | cut -d'=' -f2- | xargs) echo -e "${GREEN}✓ LDAP URI configured: $ldap_uri${NC}" else echo -e "${RED}✗ LDAP URI not configured${NC}" issues+=("LDAP URI missing") recommendations+=("Add ldap_uri to domain section") fi else echo -e "${RED}✗ SSSD config file not found${NC}" issues+=("SSSD not configured") recommendations+=("Run authconfig or realm join to configure SSSD") fi echo "" echo -e "${CYAN}Checking nsswitch.conf...${NC}" if [ -f /etc/nsswitch.conf ]; then if grep "^passwd:.*sss" /etc/nsswitch.conf > /dev/null 2>&1; then echo -e "${GREEN}✓ passwd configured for SSSD${NC}" else echo -e "${RED}✗ passwd not configured for SSSD${NC}" issues+=("nsswitch.conf passwd not using SSSD") recommendations+=("Add 'sss' to passwd line in /etc/nsswitch.conf") fi fi echo "" echo -e "${CYAN}=== Summary ===${NC}" echo "" if [ ${#issues[@]} -eq 0 ]; then echo -e "${GREEN}✓ No critical issues detected${NC}" else echo -e "${RED}Issues Found: ${#issues[@]}${NC}" for issue in "${issues[@]}"; do echo -e "${RED} • $issue${NC}" done fi echo "" if [ ${#recommendations[@]} -gt 0 ]; then echo -e "${YELLOW}Recommendations:${NC}" for rec in "${recommendations[@]}"; do echo -e "${YELLOW} → $rec${NC}" done echo "" fi if [ ${#issues[@]} -gt 0 ]; then echo -n "View recent SSSD logs? (yes/no): " read view_logs if [ "$view_logs" = "yes" ]; then echo "" if command -v journalctl > /dev/null 2>&1; then journalctl -u sssd -n 50 --no-pager 2>&1 else echo "journalctl not available" fi fi fi echo "" } # SECURITY FUNCTIONS check_active_users() { write_output "${BLUE}=== Most Active Users ===${NC}" write_output "Currently logged in users:" who | awk '{print $1}' | sort | uniq -c | sort -rn | while read line; do write_output "$line" done write_output "" write_output "Most active users (based on recent login history):" last -n 50 | grep -v "^$" | grep -v "wtmp begins" | grep -v "reboot" | \ awk '{print $1}' | sort | uniq -c | sort -rn | head -10 | while read line; do write_output "$line" done write_output "" if [ -f /var/log/secure ]; then write_output "Recent sudo activity:" grep "sudo" /var/log/secure 2>/dev/null | \ grep -oP "(?<=USER=)\w+" | sort | uniq -c | sort -rn | head -10 | while read line; do write_output "$line" done fi write_output "" } search_errors() { write_output "${BLUE}=== Error Search in System Logs ===${NC}" write_output "${YELLOW}=== Errors from journalctl since boot ===${NC}" if command -v journalctl > /dev/null 2>&1; then journalctl -p err -b --no-pager 2>&1 | tail -20 | while read line; do write_output "$line" done write_output "" boot_error_count=$(journalctl -p err -b --no-pager 2>/dev/null | wc -l) write_output "Total errors since boot: $boot_error_count" else write_output "${RED}Error: journalctl command not found${NC}" fi write_output "" write_output "${YELLOW}=== Warnings from journalctl ===${NC}" if command -v journalctl > /dev/null 2>&1; then journalctl -p warning -n 50 --no-pager 2>&1 | tail -15 | while read line; do write_output "$line" done else write_output "${RED}journalctl not available${NC}" fi write_output "" if [ -f /var/log/secure ]; then write_output "${YELLOW}=== Authentication issues from /var/log/secure ===${NC}" grep -iE "error|fail|denied|authentication failure|invalid user|connection closed" /var/log/secure 2>&1 | tail -15 | while read line; do write_output "$line" done write_output "" write_output "${YELLOW}Failed SSH login attempts:${NC}" failed_ssh=$(grep -i "failed password" /var/log/secure 2>/dev/null | wc -l) write_output "Total failed password attempts: $failed_ssh" grep -i "failed password" /var/log/secure 2>&1 | tail -10 | while read line; do write_output "$line" done write_output "" write_output "${YELLOW}Invalid user login attempts:${NC}" invalid_users=$(grep -i "invalid user" /var/log/secure 2>/dev/null | wc -l) write_output "Total invalid user attempts: $invalid_users" grep -i "invalid user" /var/log/secure 2>&1 | tail -10 | while read line; do write_output "$line" done write_output "" else write_output "${RED}Warning: /var/log/secure not found${NC}" write_output "" fi write_output "${YELLOW}=== Kernel alerts from dmesg ===${NC}" if command -v dmesg > /dev/null 2>&1; then dmesg 2>&1 | tail -20 | while read line; do write_output "$line" done write_output "" write_output "${YELLOW}Kernel errors and warnings:${NC}" dmesg 2>&1 | grep -iE "error|fail|warning|critical|alert" | tail -15 | while read line; do write_output "$line" done else write_output "${RED}Error: dmesg command not found${NC}" fi write_output "" if [ -f /var/log/messages ]; then write_output "${YELLOW}=== Recent errors in /var/log/messages ===${NC}" grep -iE "error|fail|critical" /var/log/messages 2>&1 | tail -10 | while read line; do write_output "$line" done write_output "" fi write_output "${YELLOW}=== Error summary from journalctl (last 24 hours) ===${NC}" if command -v journalctl > /dev/null 2>&1; then journalctl -p err --since "24 hours ago" --no-pager 2>&1 | \ grep -oP '^\w+ \d+ \S+ \S+' | awk '{print $4}' | sort | uniq -c | sort -rn | head -10 | while read line; do write_output "$line" done else write_output "${RED}journalctl not available${NC}" fi write_output "" } audit_permissions() { write_output "${BLUE}=== File Permission Audit ===${NC}" echo -n "Enter path to audit: " read audit_path if [ -z "$audit_path" ]; then write_output "${RED}No path provided${NC}" return fi if [ ! -d "$audit_path" ] && [ ! -f "$audit_path" ]; then write_output "${RED}Error: Path does not exist: $audit_path${NC}" return fi write_output "" write_output "${CYAN}Scanning: $audit_path${NC}" write_output "" write_output "${YELLOW}=== Files/Directories with 777 ===${NC}" full_access=$(find "$audit_path" -perm 0777 2>/dev/null) if [ -z "$full_access" ]; then write_output "${GREEN}✓ No 777 permissions found${NC}" else echo "$full_access" | while read item; do if [ -d "$item" ]; then item_type="DIR" else item_type="FILE" fi write_output "${RED} 777 [$item_type] $item${NC}" done fi write_output "" } analyze_app_logs() { write_output "${BLUE}=== Application Log Analysis ===${NC}" write_output "" declare -A app_logs write_output "${CYAN}Discovering application logs in /var/log...${NC}" write_output "" while IFS= read -r logfile; do basename_file=$(basename "$logfile") app_name=$(echo "$basename_file" | sed 's/\.log.*//' | sed 's/\.err.*//') if [ -z "${app_logs[$app_name]}" ]; then app_logs[$app_name]="$logfile" else app_logs[$app_name]="${app_logs[$app_name]}|$logfile" fi done < <(find /var/log -maxdepth 2 -type f -name "*.log" 2>/dev/null) [ -f /var/log/messages ] && app_logs["messages"]="/var/log/messages" [ -f /var/log/secure ] && app_logs["secure"]="/var/log/secure" [ -f /var/log/audit/audit.log ] && app_logs["audit"]="/var/log/audit/audit.log" write_output "${YELLOW}Available application logs:${NC}" write_output "" local counter=1 declare -A app_menu for app in "${!app_logs[@]}"; do app_menu[$counter]="$app" log_list="${app_logs[$app]}" log_count=$(echo "$log_list" | tr '|' '\n' | wc -l) write_output " $counter) $app ($log_count log file(s))" counter=$((counter + 1)) done write_output "" write_output " A) Analyze ALL logs" write_output " 0) Return to menu" write_output "" echo -n "Select application: " read selection if [ "$selection" = "0" ]; then return elif [ "$selection" = "A" ] || [ "$selection" = "a" ]; then write_output "" write_output "${CYAN}Analyzing ALL logs...${NC}" write_output "" for app in "${!app_logs[@]}"; do analyze_specific_logs "$app" "${app_logs[$app]}" done elif [ -n "${app_menu[$selection]}" ]; then selected_app="${app_menu[$selection]}" write_output "" write_output "${CYAN}Analyzing $selected_app logs...${NC}" write_output "" analyze_specific_logs "$selected_app" "${app_logs[$selected_app]}" else write_output "${RED}Invalid selection${NC}" fi } analyze_specific_logs() { local app_name=$1 local log_files=$2 write_output "${BLUE}=== Analysis for: $app_name ===${NC}" write_output "" IFS='|' read -ra LOGS <<< "$log_files" local total_errors=0 local total_denied=0 for logfile in "${LOGS[@]}"; do if [ ! -f "$logfile" ]; then continue fi write_output "${YELLOW}Log file: $logfile${NC}" error_count=$(grep -i "error" "$logfile" 2>/dev/null | wc -l) denied_count=$(grep -iE "denied|deny|forbidden|unauthorized" "$logfile" 2>/dev/null | wc -l) total_errors=$((total_errors + error_count)) total_denied=$((total_denied + denied_count)) write_output " Errors found: $error_count" write_output " Denied entries: $denied_count" if [ $error_count -gt 0 ]; then write_output "" write_output " ${RED}Recent errors (last 10):${NC}" grep -i "error" "$logfile" 2>/dev/null | tail -10 | while read line; do short_line=$(echo "$line" | cut -c1-120) write_output " $short_line..." done fi if [ $denied_count -gt 0 ]; then write_output "" write_output " ${RED}Recent denied entries (last 10):${NC}" grep -iE "denied|deny|forbidden|unauthorized" "$logfile" 2>/dev/null | tail -10 | while read line; do short_line=$(echo "$line" | cut -c1-120) write_output " $short_line..." done fi write_output "" done write_output "${CYAN}Summary for $app_name:${NC}" write_output " Total errors: $total_errors" write_output " Total denied: $total_denied" write_output "" } security_scan() { write_output "${BLUE}=== Security Scan ===${NC}" write_output "" write_output "${YELLOW}=== Login Events ===${NC}" if command -v ausearch > /dev/null 2>&1; then login_output=$(ausearch -m USER_LOGIN 2>&1) login_exit=$? if [ $login_exit -eq 0 ]; then login_count=$(echo "$login_output" | grep -c "type=USER_LOGIN") write_output "Total login events: $login_count" write_output "" echo "$login_output" | tail -40 | while read line; do write_output "$line" done else write_output "${RED}Error running ausearch (exit code: $login_exit)${NC}" write_output "Note: You may need root privileges" fi else write_output "${RED}ausearch not found${NC}" write_output "Install with: sudo dnf install audit" fi write_output "" write_output "${YELLOW}=== Open Ports ===${NC}" if command -v ss > /dev/null 2>&1; then port_output=$(ss -tuln 2>&1) port_exit=$? if [ $port_exit -eq 0 ]; then listening_count=$(echo "$port_output" | grep -c "LISTEN") write_output "Total listening ports: $listening_count" write_output "" echo "$port_output" | while read line; do write_output "$line" done else write_output "${RED}Error running ss${NC}" fi else write_output "${RED}ss not found${NC}" fi write_output "" write_output "${YELLOW}=== Firewall Configuration ===${NC}" if command -v firewall-cmd > /dev/null 2>&1; then fw_output=$(firewall-cmd --list-all 2>&1) fw_exit=$? if [ $fw_exit -eq 0 ]; then write_output "Firewall is active:" write_output "" echo "$fw_output" | while read line; do write_output "$line" done else write_output "${RED}Error running firewall-cmd${NC}" if echo "$fw_output" | grep -q "not running"; then write_output "FirewallD service is not running" fi fi else write_output "${RED}firewall-cmd not found${NC}" fi write_output "" } # Helper function to view previous STIG results view_stig_results() { local results_file=$1 local report_file=$2 echo "" echo -e "${CYAN}=== Viewing Previous STIG Scan Results ===${NC}" echo "" if [ ! -f "$results_file" ]; then echo -e "${RED}Results file not found${NC}" return fi # Parse results using same method as scan passed=$(grep -o '<result>pass</result>' "$results_file" 2>/dev/null | wc -l) failed=$(grep -o '<result>fail</result>' "$results_file" 2>/dev/null | wc -l) error=$(grep -o "<result>error</result>" "$results_file" 2>/dev/null | wc -l) unknown=$(grep -o '<result>unknown</result>' "$results_file" 2>/dev/null | wc -l) notapplicable=$(grep -o '<result>notapplicable</result>' "$results_file" 2>/dev/null | wc -l) passed=$(echo "$passed" | tr -d ' \n\r') failed=$(echo "$failed" | tr -d ' \n\r') error=$(echo "$error" | tr -d ' \n\r') unknown=$(echo "$unknown" | tr -d ' \n\r') notapplicable=$(echo "$notapplicable" | tr -d ' \n\r') passed=${passed:-0} failed=${failed:-0} error=${error:-0} unknown=${unknown:-0} notapplicable=${notapplicable:-0} total=$((passed + failed + error + unknown + notapplicable)) echo -e "${YELLOW}=== STIG Compliance Results ===${NC}" echo "" echo "Total rules evaluated: $total" echo -e "${GREEN}Passed: $passed${NC}" echo -e "${RED}Failed: $failed${NC}" echo -e "${YELLOW}Errors: $error${NC}" echo -e "${YELLOW}Unknown: $unknown${NC}" echo "Not Applicable: $notapplicable" echo "" if [ $total -gt 0 ] && [ $passed -gt 0 ]; then compliance_pct=$((passed * 100 / (passed + failed))) echo -e "Compliance Rate: ${CYAN}${compliance_pct}%${NC}" echo "" fi echo "Full results saved to:" echo " XML: $results_file" if [ -n "$report_file" ] && [ -f "$report_file" ]; then echo " HTML: $report_file" fi echo "" # Interactive menu for viewing specific results - loops until user exits while true; do echo -e "${CYAN}Would you like to view specific results?${NC}" echo "1) View Failed rules" echo "2) View Passed rules" echo "3) View Error rules" echo "4) View Unknown rules" echo "5) View Not Applicable rules" echo "A) View All rules" echo "0) Return to menu" echo "" echo -n "Select option: " read view_choice case $view_choice in 1) if [ $failed -gt 0 ]; then echo "" echo -e "${RED}=== Failed STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "fail" "$failed" else echo -e "${GREEN}No failed rules!${NC}" fi # Show remediation info for failed rules if [ $failed -gt 0 ]; then echo -e "${YELLOW}Remediation:${NC}" echo " 1. Review HTML report: $report_file" echo " 2. Generate fix: oscap xccdf generate fix --profile stig" echo " 3. Review and test fix script before execution" echo "" fi echo "" echo -n "Press Enter to continue..." read echo "" ;; 2) if [ $passed -gt 0 ]; then echo "" echo -e "${GREEN}=== Passed STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "pass" "$passed" else echo "No passed rules found." fi echo "" echo -n "Press Enter to continue..." read echo "" ;; 3) if [ $error -gt 0 ]; then echo "" echo -e "${YELLOW}=== Error STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "error" "$error" else echo "No error rules." fi echo "" echo -n "Press Enter to continue..." read echo "" ;; 4) if [ $unknown -gt 0 ]; then echo "" echo -e "${YELLOW}=== Unknown STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "unknown" "$unknown" else echo "No unknown rules." fi echo "" echo -n "Press Enter to continue..." read echo "" ;; 5) if [ $notapplicable -gt 0 ]; then echo "" echo -e "${CYAN}=== Not Applicable STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "notapplicable" "$notapplicable" else echo "No not applicable rules." fi echo "" echo -n "Press Enter to continue..." read echo "" ;; A|a) echo "" echo -e "${CYAN}=== All STIG Rules Results ===${NC}" echo "" if [ $failed -gt 0 ]; then echo -e "${RED}Failed ($failed):${NC}" display_stig_rules "$results_file" "fail" "$failed" 20 echo "" fi if [ $error -gt 0 ]; then echo -e "${YELLOW}Errors ($error):${NC}" display_stig_rules "$results_file" "error" "$error" 20 echo "" fi if [ $unknown -gt 0 ]; then echo -e "${YELLOW}Unknown ($unknown):${NC}" display_stig_rules "$results_file" "unknown" "$unknown" 20 echo "" fi if [ $passed -gt 0 ]; then echo -e "${GREEN}Passed ($passed) - showing first 20:${NC}" display_stig_rules "$results_file" "pass" "$passed" 20 echo "" fi echo -n "Press Enter to continue..." read echo "" ;; 0) break ;; *) echo -e "${RED}Invalid selection${NC}" echo "" ;; esac done } check_disa_stig() { echo -e "${BLUE}=== DISA STIG Compliance Check ===${NC}" echo "" # Check for recent scan results results_dir="/tmp/oscap_results" if [ -d "$results_dir" ]; then latest_results=$(ls -t "$results_dir"/stig_*.xml 2>/dev/null | head -1) latest_report=$(ls -t "$results_dir"/stig_*.html 2>/dev/null | head -1) if [ -n "$latest_results" ] && [ -f "$latest_results" ]; then result_age=$(stat -c %Y "$latest_results" 2>/dev/null || echo 0) current_time=$(date +%s) age_hours=$(( (current_time - result_age) / 3600 )) echo -e "${CYAN}Found previous scan results (${age_hours} hours old):${NC}" echo " Results: $(basename "$latest_results")" if [ -n "$latest_report" ]; then echo " Report: $(basename "$latest_report")" fi echo "" echo "Would you like to:" echo "1) View previous scan results" echo "2) Run a new scan" echo "0) Cancel" echo "" echo -n "Select option: " read scan_choice case $scan_choice in 1) view_stig_results "$latest_results" "$latest_report" return ;; 2) echo "" echo "Proceeding with new scan..." echo "" ;; 0) echo "Cancelled" return ;; *) echo -e "${YELLOW}Invalid choice, proceeding with new scan...${NC}" echo "" ;; esac fi fi # Check OpenSCAP local oscap_found=false local scap_security_guide_found=false echo -e "${CYAN}Checking for OpenSCAP tools...${NC}" if command -v oscap > /dev/null 2>&1; then echo -e "${GREEN}✓ OpenSCAP (oscap) is installed${NC}" oscap_version=$(oscap --version 2>&1 | head -1) echo " Version: $oscap_version" oscap_found=true else echo -e "${RED}✗ OpenSCAP (oscap) is NOT installed${NC}" fi # Check SCAP Security Guide if rpm -q scap-security-guide > /dev/null 2>&1; then echo -e "${GREEN}✓ SCAP Security Guide is installed${NC}" ssg_version=$(rpm -q scap-security-guide) echo " Package: $ssg_version" scap_security_guide_found=true else echo -e "${RED}✗ SCAP Security Guide is NOT installed${NC}" fi echo "" # If tools not installed, provide installation instructions if [ "$oscap_found" = false ] || [ "$scap_security_guide_found" = false ]; then echo -e "${YELLOW}Required packages are missing${NC}" echo "" echo "To install OpenSCAP and SCAP Security Guide, run:" echo -e "${CYAN}sudo dnf install openscap-scanner scap-security-guide${NC}" echo "" return fi # Ask if user wants to run the scan echo -e "${YELLOW}OpenSCAP DISA STIG scan can take several minutes to complete.${NC}" echo -n "Do you want to run the STIG compliance scan now? (yes/no): " read run_scan if [ "$run_scan" != "yes" ]; then echo "Scan cancelled" echo "" return fi echo "" # Detect RHEL version if [ -f /etc/redhat-release ]; then rhel_version=$(grep -oP 'release \K[0-9]+' /etc/redhat-release | head -1) echo "RHEL Version: $rhel_version" else rhel_version="9" fi # Find datastream ssg_dir="/usr/share/xml/scap/ssg/content" datastream_file="$ssg_dir/ssg-rhel${rhel_version}-ds.xml" if [ ! -f "$datastream_file" ]; then datastream_file=$(find "$ssg_dir" -name "*rhel*-ds.xml" 2>/dev/null | head -1) fi if [ -z "$datastream_file" ] || [ ! -f "$datastream_file" ]; then echo -e "${RED}No datastream file found${NC}" return fi echo "Datastream: $datastream_file" echo "" # Find STIG profile stig_profile="xccdf_org.ssgproject.content_profile_stig" echo "Running STIG scan..." echo "" if [ $EUID -ne 0 ]; then echo -e "${YELLOW}Warning: Running without root - some checks may be skipped${NC}" echo "" fi results_dir="/tmp/oscap_results" mkdir -p "$results_dir" timestamp=$(date +%Y%m%d_%H%M%S) results_file="$results_dir/stig_${timestamp}.xml" report_file="$results_dir/stig_${timestamp}.html" oscap xccdf eval \ --profile "$stig_profile" \ --results "$results_file" \ --report "$report_file" \ "$datastream_file" 2>&1 echo "" if [ -f "$results_file" ]; then # Count results - OSCAP uses abbreviated result tags # Using simple pattern that works regardless of whitespace passed=$(grep -o '<result>pass</result>' "$results_file" 2>/dev/null | wc -l) failed=$(grep -o '<result>fail</result>' "$results_file" 2>/dev/null | wc -l) error=$(grep -o "<result>error</result>" "$results_file" 2>/dev/null | wc -l) unknown=$(grep -o "<result>unknown</result>" "$results_file" 2>/dev/null | wc -l) notapplicable=$(grep -o "<result>notapplicable</result>" "$results_file" 2>/dev/null | wc -l) # Clean up whitespace passed=$(echo "$passed" | tr -d ' \n\r') failed=$(echo "$failed" | tr -d ' \n\r') error=$(echo "$error" | tr -d ' \n\r') unknown=$(echo "$unknown" | tr -d ' \n\r') notapplicable=$(echo "$notapplicable" | tr -d ' \n\r') # Ensure valid numbers passed=${passed:-0} failed=${failed:-0} error=${error:-0} unknown=${unknown:-0} notapplicable=${notapplicable:-0} total=$((passed + failed + error + unknown + notapplicable)) echo -e "${YELLOW}=== STIG Compliance Results ===${NC}" echo "" echo "Total rules evaluated: $total" echo -e "${GREEN}Passed: $passed${NC}" echo -e "${RED}Failed: $failed${NC}" echo -e "${YELLOW}Errors: $error${NC}" echo -e "${YELLOW}Unknown: $unknown${NC}" echo "Not Applicable: $notapplicable" echo "" if [ $total -gt 0 ] && [ $passed -gt 0 ]; then compliance_pct=$((passed * 100 / (passed + failed))) echo -e "Compliance Rate: ${CYAN}${compliance_pct}%${NC}" echo "" fi echo "Full results saved to:" echo " HTML: $report_file" echo " XML: $results_file" echo "" # Ask user what they want to see echo -e "${CYAN}Would you like to view specific results? (optional)${NC}" echo "1) View Failed rules" echo "2) View Passed rules" echo "3) View Error rules" echo "4) View Unknown rules" echo "5) View Not Applicable rules" echo "A) View All rules" echo "0) Skip (continue)" echo "" echo -n "Select option: " read view_choice case $view_choice in 1) if [ $failed -gt 0 ]; then echo "" echo -e "${RED}=== Failed STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "fail" "$failed" else echo -e "${GREEN}No failed rules!${NC}" fi ;; 2) if [ $passed -gt 0 ]; then echo "" echo -e "${GREEN}=== Passed STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "pass" "$passed" else echo "No passed rules found." fi ;; 3) if [ $error -gt 0 ]; then echo "" echo -e "${YELLOW}=== Error STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "error" "$error" else echo "No error rules." fi ;; 4) if [ $unknown -gt 0 ]; then echo "" echo -e "${YELLOW}=== Unknown STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "unknown" "$unknown" else echo "No unknown rules." fi ;; 5) if [ $notapplicable -gt 0 ]; then echo "" echo -e "${CYAN}=== Not Applicable STIG Rules ===${NC}" echo "" display_stig_rules "$results_file" "notapplicable" "$notapplicable" else echo "No not applicable rules." fi ;; A|a) echo "" echo -e "${CYAN}=== All STIG Rules Results ===${NC}" echo "" if [ $failed -gt 0 ]; then echo -e "${RED}Failed ($failed):${NC}" display_stig_rules "$results_file" "fail" "$failed" 20 echo "" fi if [ $error -gt 0 ]; then echo -e "${YELLOW}Errors ($error):${NC}" display_stig_rules "$results_file" "error" "$error" 20 echo "" fi if [ $unknown -gt 0 ]; then echo -e "${YELLOW}Unknown ($unknown):${NC}" display_stig_rules "$results_file" "unknown" "$unknown" 20 echo "" fi if [ $passed -gt 0 ]; then echo -e "${GREEN}Passed ($passed) - showing first 20:${NC}" display_stig_rules "$results_file" "pass" "$passed" 20 echo "" fi ;; 0|*) echo "Continuing..." ;; esac echo "" if [ $failed -gt 0 ]; then echo -e "${YELLOW}Remediation:${NC}" echo " 1. Review HTML report: $report_file" echo " 2. Generate fix: oscap xccdf generate fix --profile $stig_profile --output /tmp/fix.sh $datastream_file" echo " 3. Review and execute fix script" else echo -e "${GREEN}✓ Fully compliant with DISA STIG!${NC}" fi else echo -e "${RED}Results file not generated - scan may have failed${NC}" fi echo "" } # Helper function to display STIG rules by result type display_stig_rules() { local results_file=$1 local result_type=$2 local total_count=$3 local max_display=${4:-100} # Handle <r>type</r> format using pattern matching instead of exact match awk -v type="$result_type" -v max="$max_display" ' /<rule-result/ { in_rule = 1 if (match($0, /idref="[^"]+"/)) { rule_id = substr($0, RSTART+7, RLENGTH-8) } else { rule_id = "" } v_number = "" cci = "" cce = "" has_type = 0 next } in_rule { # Use pattern matching - check if line contains <r>type</r> pattern = "<result>" type "</result>" if (index($0, pattern) > 0) { has_type = 1 } # Extract V-number if (/V-[0-9]/ && v_number == "") { if (match($0, /V-[0-9]+/)) { v_number = substr($0, RSTART, RLENGTH) } } # Extract CCI if (/CCI-[0-9]/ && cci == "") { if (match($0, /CCI-[0-9]+/)) { cci = substr($0, RSTART, RLENGTH) } } # Extract CCE if (/CCE-[0-9]/ && cce == "") { if (match($0, /CCE-[0-9]+-[0-9]+/)) { cce = substr($0, RSTART, RLENGTH) } } } in_rule && /<\/rule-result>/ { if (has_type && rule_id != "") { count++ if (count <= max) { print " • " rule_id if (v_number != "") print " STIG ID: " v_number if (cci != "") print " CCI: " cci if (cce != "") print " CCE: " cce print "" } } in_rule = 0 next } END { if (count > max) { print " ... and " (count - max) " more (see full report)" print "" } } ' "$results_file" } # TOOLBOX FUNCTIONS check_ssh_config() { echo -e "${BLUE}=== SSH Configuration Audit ===${NC}" echo "" SSHD_CONFIG="/etc/ssh/sshd_config" SSH_CONFIG="/etc/ssh/ssh_config" TEMPLATE_DIR="/etc/ssh/templates" SSHD_TEMPLATE="$TEMPLATE_DIR/sshd_config.template" SSH_TEMPLATE="$TEMPLATE_DIR/ssh_config.template" if [ ! -d "$TEMPLATE_DIR" ]; then echo -e "${YELLOW}Template directory not found: $TEMPLATE_DIR${NC}" echo "Creating template directory..." mkdir -p "$TEMPLATE_DIR" 2>/dev/null if [ $? -eq 0 ]; then echo -e "${GREEN}Created: $TEMPLATE_DIR${NC}" echo "" echo "Please place your template files in this directory:" echo " - sshd_config.template (for server config)" echo " - ssh_config.template (for client config)" else echo -e "${RED}Failed to create directory (need root)${NC}" fi echo "" return fi echo -e "${CYAN}Checking SSHD Server Config...${NC}" if [ -f "$SSHD_TEMPLATE" ]; then diff_output=$(diff <(grep -v "^#" "$SSHD_TEMPLATE" | grep -v "^$" | sort) <(grep -v "^#" "$SSHD_CONFIG" | grep -v "^$" | sort) 2>/dev/null) if [ -z "$diff_output" ]; then echo -e "${GREEN}✓ Config matches template${NC}" else echo -e "${YELLOW}⚠ Differences found${NC}" echo "" echo "$diff_output" | grep "^[-+]" | grep -v "^[-+][-+][-+]" echo "" added=$(echo "$diff_output" | grep "^+" | grep -v "^+++" | wc -l) removed=$(echo "$diff_output" | grep "^-" | grep -v "^---" | wc -l) echo "Summary:" echo -e " ${GREEN}+ $added line(s) in current config not in template${NC}" echo -e " ${RED}- $removed line(s) in template not in current config${NC}" fi else echo -e "${YELLOW}Template not found: $SSHD_TEMPLATE${NC}" fi echo "" echo -e "${CYAN}Checking SSH Client Config...${NC}" if [ -f "$SSH_TEMPLATE" ]; then diff_output=$(diff <(grep -v "^#" "$SSH_TEMPLATE" | grep -v "^$" | sort) <(grep -v "^#" "$SSH_CONFIG" | grep -v "^$" | sort) 2>/dev/null) if [ -z "$diff_output" ]; then echo -e "${GREEN}✓ Config matches template${NC}" else echo -e "${YELLOW}⚠ Differences found${NC}" echo "" echo "$diff_output" | grep "^[-+]" | grep -v "^[-+][-+][-+]" fi else echo -e "${YELLOW}Template not found: $SSH_TEMPLATE${NC}" fi echo "" } check_certificates() { echo -e "${BLUE}=== Certificate Expiration Check ===${NC}" echo "" declare -a cert_locations=( "/etc/pki/tls/certs" "/etc/ssl/certs" "/etc/pki/ca-trust/source/anchors" "/etc/httpd/conf/ssl.crt" "/etc/nginx/ssl" "/etc/letsencrypt/live" "/etc/pki/tls/private" ) echo -e "${CYAN}Searching for certificates in common locations...${NC}" echo "" declare -A cert_info cert_count=0 for location in "${cert_locations[@]}"; do if [ -d "$location" ]; then while IFS= read -r cert_file; do if [ -f "$cert_file" ]; then if openssl x509 -in "$cert_file" -noout -text > /dev/null 2>&1; then cert_count=$((cert_count + 1)) cert_info[$cert_count]="$cert_file" fi fi done < <(find "$location" -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cert" \) 2>/dev/null) fi done if [ $cert_count -eq 0 ]; then echo -e "${YELLOW}No certificates found in common locations${NC}" echo "" return fi echo -e "${GREEN}Found $cert_count certificate(s)${NC}" echo "" echo "==========================================" declare -a expiring_soon declare -a expired_certs current_timestamp=$(date +%s) warning_days=30 warning_timestamp=$((current_timestamp + (warning_days * 86400))) for i in "${!cert_info[@]}"; do cert_file="${cert_info[$i]}" echo "" echo -e "${CYAN}Certificate $i: $cert_file${NC}" subject=$(openssl x509 -in "$cert_file" -noout -subject 2>/dev/null | sed 's/subject=//') issuer=$(openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null | sed 's/issuer=//') start_date=$(openssl x509 -in "$cert_file" -noout -startdate 2>/dev/null | sed 's/notBefore=//') end_date=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | sed 's/notAfter=//') end_timestamp=$(date -d "$end_date" +%s 2>/dev/null) if [ -n "$end_timestamp" ]; then days_until_expiry=$(( (end_timestamp - current_timestamp) / 86400 )) echo " Subject: $subject" echo " Issuer: $issuer" echo " Valid From: $start_date" echo " Valid Until: $end_date" if [ $end_timestamp -lt $current_timestamp ]; then echo -e " Status: ${RED}EXPIRED${NC}" echo -e " Expired $((days_until_expiry * -1)) days ago" expired_certs+=("$i:$cert_file") elif [ $end_timestamp -lt $warning_timestamp ]; then echo -e " Status: ${YELLOW}EXPIRING SOON${NC}" echo -e " Days remaining: ${YELLOW}$days_until_expiry${NC}" expiring_soon+=("$i:$cert_file:$days_until_expiry") else echo -e " Status: ${GREEN}VALID${NC}" echo " Days remaining: $days_until_expiry" fi fi echo "==========================================" done echo "" echo -e "${CYAN}=== Summary ===${NC}" echo "Total certificates found: $cert_count" echo "Expired certificates: ${#expired_certs[@]}" echo "Expiring within $warning_days days: ${#expiring_soon[@]}" echo "" if [ ${#expired_certs[@]} -gt 0 ]; then echo -e "${RED}⚠ WARNING: Expired certificates detected!${NC}" for cert in "${expired_certs[@]}"; do cert_file=$(echo "$cert" | cut -d':' -f2-) echo " - $cert_file" done echo "" fi if [ ${#expiring_soon[@]} -gt 0 ]; then echo -e "${YELLOW}⚠ NOTICE: Certificates expiring soon:${NC}" for cert in "${expiring_soon[@]}"; do cert_file=$(echo "$cert" | cut -d':' -f2) days=$(echo "$cert" | cut -d':' -f3) echo " - $cert_file ($days days remaining)" done echo "" fi } # ======================================================================== # FIXED SOFTWARE COMPARISON FUNCTIONS # ======================================================================== compare_software() { echo -e "${BLUE}=== Software Comparison Tool ===${NC}" echo "" echo -e "${CYAN}Generating list of installed software on this server...${NC}" local_pkg_file="/tmp/local_packages_$(date +%Y%m%d_%H%M%S).txt" local_pkg_names="/tmp/local_pkg_names_$(date +%Y%m%d_%H%M%S).txt" if command -v rpm > /dev/null 2>&1; then # Full package info rpm -qa --qf "%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n" | sort > "$local_pkg_file" # Package names only (for comparison) rpm -qa --qf "%{NAME}\n" | sort -u > "$local_pkg_names" local_count=$(wc -l < "$local_pkg_file") echo -e "${GREEN}Found $local_count packages installed locally${NC}" else echo -e "${RED}Error: rpm command not found${NC}" return fi echo "" echo "Select comparison method:" echo "1) Compare with file (manual method)" echo "2) Compare via SSH (automatic method)" echo "0) Cancel" echo "" echo -n "Select option: " read compare_method case $compare_method in 1) compare_with_file "$local_pkg_file" "$local_pkg_names" ;; 2) compare_via_ssh "$local_pkg_file" "$local_pkg_names" ;; 0) echo "Cancelled" rm -f "$local_pkg_file" "$local_pkg_names" return ;; *) echo -e "${RED}Invalid option${NC}" rm -f "$local_pkg_file" "$local_pkg_names" return ;; esac rm -f "$local_pkg_file" "$local_pkg_names" } compare_with_file() { local local_pkg_file=$1 local local_pkg_names=$2 echo "" echo -e "${CYAN}=== Manual Comparison Method ===${NC}" echo "" echo "To compare software with another server, follow these steps:" echo "" echo -e "${YELLOW}STEP 1: On the REMOTE server, run these commands:${NC}" echo "" echo "rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\\n' | sort > /tmp/remote_packages.txt" echo "rpm -qa --qf '%{NAME}\\n' | sort -u > /tmp/remote_pkg_names.txt" echo "" echo -e "${YELLOW}STEP 2: Verify the files were created correctly:${NC}" echo "" echo "wc -l /tmp/remote_packages.txt /tmp/remote_pkg_names.txt" echo "(Should show 1000+ lines for both files)" echo "" echo -e "${YELLOW}STEP 3: Copy files from remote server to this server:${NC}" echo "" echo "scp remote_server:/tmp/remote_packages.txt /tmp/remote_packages.txt" echo "scp remote_server:/tmp/remote_pkg_names.txt /tmp/remote_pkg_names.txt" echo "" echo -n "Press Enter when the remote files are ready, or 'q' to cancel: " read ready if [ "$ready" = "q" ] || [ "$ready" = "Q" ]; then echo "Cancelled" return fi remote_pkg_file="/tmp/remote_packages.txt" remote_pkg_names="/tmp/remote_pkg_names.txt" if [ ! -f "$remote_pkg_file" ]; then echo -e "${RED}Error: Remote package file not found at $remote_pkg_file${NC}" return fi if [ ! -f "$remote_pkg_names" ]; then echo -e "${RED}Error: Remote package names file not found at $remote_pkg_names${NC}" return fi # Validate remote files have reasonable content local remote_count=$(wc -l < "$remote_pkg_file") local remote_names_count=$(wc -l < "$remote_pkg_names") if [ $remote_count -lt 100 ]; then echo -e "${RED}Warning: Remote package file only has $remote_count lines${NC}" echo -e "${RED}Expected 1000+ lines. The file may be incomplete.${NC}" echo "" echo "First few lines of remote file:" head -5 "$remote_pkg_file" echo "" echo -n "Continue anyway? (yes/no): " read continue_anyway if [ "$continue_anyway" != "yes" ]; then echo "Cancelled" return fi fi perform_comparison "$local_pkg_file" "$remote_pkg_file" "$local_pkg_names" "$remote_pkg_names" } compare_via_ssh() { local local_pkg_file=$1 local local_pkg_names=$2 echo "" echo -e "${CYAN}=== SSH Comparison Method ===${NC}" echo "" if ! command -v ssh > /dev/null 2>&1; then echo -e "${RED}Error: ssh command not found${NC}" return fi echo -n "Enter remote server hostname or IP: " read remote_host if [ -z "$remote_host" ]; then echo -e "${RED}No hostname provided${NC}" return fi echo -n "Enter username (default: current user): " read remote_user if [ -z "$remote_user" ]; then remote_user=$(whoami) fi echo "" echo -e "${YELLOW}Note: You will be prompted for SSH password/key${NC}" echo -e "${YELLOW}Credentials are NOT stored by this script${NC}" echo "" echo "Connecting to $remote_user@$remote_host..." echo "" # Test connection first if ! ssh -o ConnectTimeout=10 -o BatchMode=no "$remote_user@$remote_host" "echo 'Connection successful'" > /dev/null 2>&1; then echo -e "${RED}Error: Could not connect to remote server${NC}" return fi remote_pkg_file="/tmp/remote_packages_$(date +%Y%m%d_%H%M%S).txt" remote_pkg_names="/tmp/remote_pkg_names_$(date +%Y%m%d_%H%M%S).txt" echo "Retrieving package list from remote server..." # Get full package list ssh_output=$(ssh -o ConnectTimeout=10 -o BatchMode=no "$remote_user@$remote_host" \ "rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort" 2>&1) ssh_exit=$? if [ $ssh_exit -ne 0 ]; then echo -e "${RED}Error retrieving package list from remote server${NC}" echo "Error: $ssh_output" return fi echo "$ssh_output" > "$remote_pkg_file" # Get package names only ssh_names=$(ssh -o ConnectTimeout=10 -o BatchMode=no "$remote_user@$remote_host" \ "rpm -qa --qf '%{NAME}\n' | sort -u" 2>&1) if [ $? -ne 0 ]; then echo -e "${RED}Error retrieving package names from remote server${NC}" return fi echo "$ssh_names" > "$remote_pkg_names" remote_count=$(wc -l < "$remote_pkg_file") echo -e "${GREEN}Found $remote_count packages on remote server${NC}" echo "" perform_comparison "$local_pkg_file" "$remote_pkg_file" "$local_pkg_names" "$remote_pkg_names" rm -f "$remote_pkg_file" "$remote_pkg_names" } perform_comparison() { local local_file=$1 local remote_file=$2 local local_names=$3 local remote_names=$4 echo -e "${CYAN}=== Analyzing Differences ===${NC}" echo "" # Create temp files local_only_names="/tmp/local_only_names_$(date +%Y%m%d_%H%M%S).txt" remote_only_names="/tmp/remote_only_names_$(date +%Y%m%d_%H%M%S).txt" common_names="/tmp/common_names_$(date +%Y%m%d_%H%M%S).txt" version_diff="/tmp/version_diff_$(date +%Y%m%d_%H%M%S).txt" # Find packages that exist in both (by name) comm -12 "$local_names" "$remote_names" > "$common_names" common_count=$(wc -l < "$common_names") # Find packages only in local comm -23 "$local_names" "$remote_names" > "$local_only_names" local_only_count=$(wc -l < "$local_only_names") # Find packages only in remote comm -13 "$local_names" "$remote_names" > "$remote_only_names" remote_only_count=$(wc -l < "$remote_only_names") # Check version differences for common packages echo "" > "$version_diff" version_diff_count=0 while IFS= read -r pkg_name; do local_version=$(grep "^${pkg_name}-" "$local_file" | head -1) remote_version=$(grep "^${pkg_name}-" "$remote_file" | head -1) if [ "$local_version" != "$remote_version" ]; then echo "LOCAL: $local_version" >> "$version_diff" echo "REMOTE: $remote_version" >> "$version_diff" echo "" >> "$version_diff" version_diff_count=$((version_diff_count + 1)) fi done < "$common_names" # Display summary echo -e "${YELLOW}=== Summary ===${NC}" local_total=$(wc -l < "$local_file") remote_total=$(wc -l < "$remote_file") echo "Local server packages: $local_total" echo "Remote server packages: $remote_total" echo "" echo -e "${GREEN}Common packages (same name): $common_count${NC}" echo -e "${YELLOW}Version differences: $version_diff_count${NC}" echo -e "${CYAN}Packages only on LOCAL: $local_only_count${NC}" echo -e "${MAGENTA}Packages only on REMOTE: $remote_only_count${NC}" echo "" # Show packages only on local if [ $local_only_count -gt 0 ]; then echo -e "${CYAN}=== Packages ONLY on LOCAL server ===${NC}" while IFS= read -r pkg_name; do grep "^${pkg_name}-" "$local_file" done < "$local_only_names" | head -20 if [ $local_only_count -gt 20 ]; then echo "... and $((local_only_count - 20)) more" fi echo "" fi # Show packages only on remote if [ $remote_only_count -gt 0 ]; then echo -e "${MAGENTA}=== Packages ONLY on REMOTE server ===${NC}" while IFS= read -r pkg_name; do grep "^${pkg_name}-" "$remote_file" done < "$remote_only_names" | head -20 if [ $remote_only_count -gt 20 ]; then echo "... and $((remote_only_count - 20)) more" fi echo "" fi # Show version differences if [ $version_diff_count -gt 0 ]; then echo -e "${YELLOW}=== Version Differences (first 10) ===${NC}" head -30 "$version_diff" if [ $version_diff_count -gt 10 ]; then echo "... and $((version_diff_count - 10)) more version differences" fi echo "" fi # Offer to save report echo -n "Save detailed comparison report? (yes/no): " read save_report if [ "$save_report" = "yes" ]; then report_file="/tmp/software_comparison_$(date +%Y%m%d_%H%M%S).txt" { echo "=========================================" echo "Software Comparison Report" echo "Generated: $(date)" echo "=========================================" echo "" echo "SUMMARY" echo "-------" echo "Local server packages: $local_total" echo "Remote server packages: $remote_total" echo "Common packages: $common_count" echo "Version differences: $version_diff_count" echo "Packages only on local: $local_only_count" echo "Packages only on remote: $remote_only_count" echo "" if [ $local_only_count -gt 0 ]; then echo "PACKAGES ONLY ON LOCAL SERVER" echo "==============================" while IFS= read -r pkg_name; do grep "^${pkg_name}-" "$local_file" done < "$local_only_names" echo "" fi if [ $remote_only_count -gt 0 ]; then echo "PACKAGES ONLY ON REMOTE SERVER" echo "===============================" while IFS= read -r pkg_name; do grep "^${pkg_name}-" "$remote_file" done < "$remote_only_names" echo "" fi if [ $version_diff_count -gt 0 ]; then echo "VERSION DIFFERENCES" echo "===================" cat "$version_diff" echo "" fi } > "$report_file" echo -e "${GREEN}Report saved to: $report_file${NC}" fi # Clean up temp files rm -f "$local_only_names" "$remote_only_names" "$common_names" "$version_diff" } validate_daemon_configs() { echo -e "${BLUE}=== Daemon Configuration Validator ===${NC}" echo "" echo -e "${CYAN}Scanning for installed daemons...${NC}" echo "" # Arrays to store found daemons declare -a found_daemons=() declare -A daemon_commands=() declare -A daemon_names=() declare -A daemon_restart_impacts=() daemon_count=0 # OpenSSH if command -v sshd > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("sshd") daemon_commands["sshd"]="sshd -t" daemon_names["sshd"]="OpenSSH Server" daemon_restart_impacts["sshd"]="⚠ CRITICAL: All active SSH sessions will be TERMINATED. Remote access will be briefly interrupted. Plan maintenance window." echo -e "${GREEN}✓ Found: OpenSSH Server (sshd)${NC}" fi # NGINX if command -v nginx > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("nginx") daemon_commands["nginx"]="nginx -t" daemon_names["nginx"]="NGINX Web Server" daemon_restart_impacts["nginx"]="⚠ MODERATE: Web traffic will be interrupted for 1-2 seconds. Active connections may be dropped. Use 'nginx -s reload' for zero-downtime config reload." echo -e "${GREEN}✓ Found: NGINX Web Server${NC}" fi # Apache HTTPD if command -v httpd > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("httpd") daemon_commands["httpd"]="httpd -t" daemon_names["httpd"]="Apache HTTPD" daemon_restart_impacts["httpd"]="⚠ MODERATE: Web traffic will be interrupted briefly. Active HTTP requests will fail. Use 'apachectl graceful' for graceful restart." echo -e "${GREEN}✓ Found: Apache HTTPD${NC}" fi # PHP-FPM if command -v php-fpm > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("php-fpm") daemon_commands["php-fpm"]="php-fpm -t" daemon_names["php-fpm"]="PHP-FPM" daemon_restart_impacts["php-fpm"]="⚠ MODERATE: PHP processing will halt. Active PHP requests will fail or timeout. Web applications will be unavailable during restart." echo -e "${GREEN}✓ Found: PHP-FPM${NC}" fi # BIND (named) if command -v named > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("named") daemon_commands["named"]="named-checkconf /etc/named.conf" daemon_names["named"]="BIND DNS Server" daemon_restart_impacts["named"]="⚠ HIGH: DNS resolution will fail during restart. Can impact entire network/organization. Use 'rndc reload' for config reload without restart." echo -e "${GREEN}✓ Found: BIND DNS Server (named)${NC}" fi # rsyslog if command -v rsyslogd > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("rsyslog") daemon_commands["rsyslog"]="rsyslogd -N1" daemon_names["rsyslog"]="Rsyslog" daemon_restart_impacts["rsyslog"]="✓ LOW: Log messages may be lost during restart (typically <1 second). System logging will resume automatically." echo -e "${GREEN}✓ Found: Rsyslog${NC}" fi # Postfix if command -v postfix > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("postfix") daemon_commands["postfix"]="postfix check" daemon_names["postfix"]="Postfix Mail Server" daemon_restart_impacts["postfix"]="⚠ MODERATE: Mail delivery will pause. Incoming mail will be queued by sender. Outgoing mail in queue will be delayed." echo -e "${GREEN}✓ Found: Postfix Mail Server${NC}" fi # MySQL/MariaDB if command -v mysqld > /dev/null 2>&1 || command -v mariadbd > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("mysql") if [ -f /etc/my.cnf ]; then daemon_commands["mysql"]="mysqld --validate-config" else daemon_commands["mysql"]="echo 'Config file /etc/my.cnf not found'" fi daemon_names["mysql"]="MySQL/MariaDB Server" daemon_restart_impacts["mysql"]="⚠ CRITICAL: All database connections will be dropped. Applications will lose DB access. Queries in progress will fail. Plan maintenance window." echo -e "${GREEN}✓ Found: MySQL/MariaDB Server${NC}" fi # PostgreSQL if command -v postgres > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("postgresql") daemon_commands["postgresql"]="sudo -u postgres postgres -C config_file" daemon_names["postgresql"]="PostgreSQL Server" daemon_restart_impacts["postgresql"]="⚠ CRITICAL: All database connections will be terminated. Applications will experience DB outage. Use 'pg_ctl reload' for config-only changes." echo -e "${GREEN}✓ Found: PostgreSQL Server${NC}" fi # Chrony (NTP) if command -v chronyd > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("chrony") if [ -f /etc/chrony.conf ]; then daemon_commands["chrony"]="chronyd -p" else daemon_commands["chrony"]="echo 'Config file not found'" fi daemon_names["chrony"]="Chrony NTP" daemon_restart_impacts["chrony"]="✓ LOW: Time synchronization will pause briefly. System time may drift slightly during restart." echo -e "${GREEN}✓ Found: Chrony NTP${NC}" fi # HAProxy if command -v haproxy > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("haproxy") if [ -f /etc/haproxy/haproxy.cfg ]; then daemon_commands["haproxy"]="haproxy -c -f /etc/haproxy/haproxy.cfg" else daemon_commands["haproxy"]="haproxy -c -f /etc/haproxy/haproxy.cfg" fi daemon_names["haproxy"]="HAProxy Load Balancer" daemon_restart_impacts["haproxy"]="⚠ HIGH: Load balancing will stop. All backend connections will be interrupted. Can cause full service outage. Use reload instead of restart." echo -e "${GREEN}✓ Found: HAProxy Load Balancer${NC}" fi # Firewalld if command -v firewalld > /dev/null 2>&1; then daemon_count=$((daemon_count + 1)) found_daemons+=("firewalld") daemon_commands["firewalld"]="firewall-cmd --check-config" daemon_names["firewalld"]="Firewalld" daemon_restart_impacts["firewalld"]="⚠ HIGH: Firewall rules will be reloaded. Brief moment where traffic may not be filtered. Use 'firewall-cmd --reload' instead." echo -e "${GREEN}✓ Found: Firewalld${NC}" fi echo "" echo -e "${GREEN}Found $daemon_count daemon(s)${NC}" echo "" if [ $daemon_count -eq 0 ]; then echo -e "${YELLOW}No supported daemons found${NC}" return fi # Display menu echo -e "${CYAN}Select daemon to validate:${NC}" echo "" local counter=1 declare -A menu_map for daemon in "${found_daemons[@]}"; do menu_map[$counter]="$daemon" echo " $counter) ${daemon_names[$daemon]}" counter=$((counter + 1)) done echo "" echo " A) Test ALL daemon configurations" echo " I) Show restart impact information" echo " 0) Return to menu" echo "" echo -n "Select option: " read selection case $selection in 0) return ;; I|i) show_restart_impacts echo "" echo -n "Press Enter to continue..." read ;; A|a) echo "" echo -e "${CYAN}=== Testing ALL Daemon Configurations ===${NC}" echo "" for daemon in "${found_daemons[@]}"; do test_daemon_config "$daemon" echo "" done echo -e "${GREEN}All daemon configuration tests complete${NC}" ;; *) if [ -n "${menu_map[$selection]}" ]; then selected_daemon="${menu_map[$selection]}" echo "" test_daemon_config "$selected_daemon" else echo -e "${RED}Invalid selection${NC}" fi ;; esac } # Helper function to test individual daemon config test_daemon_config() { local daemon=$1 local cmd="${daemon_commands[$daemon]}" local name="${daemon_names[$daemon]}" echo -e "${YELLOW}=== Testing: $name ===${NC}" echo "Command: $cmd" echo "" # Execute the test command output=$(eval $cmd 2>&1) exit_code=$? echo "$output" echo "" if [ $exit_code -eq 0 ]; then echo -e "${GREEN}✓ Configuration is valid${NC}" # Show restart impact warning echo "" echo -e "${CYAN}Restart Impact:${NC}" echo "${daemon_restart_impacts[$daemon]}" # Check if service is running if command -v systemctl > /dev/null 2>&1; then service_name=$(get_service_name "$daemon") if systemctl is-active --quiet "$service_name" 2>/dev/null; then echo "" echo -e "${GREEN}Service Status: RUNNING${NC}" echo "" echo -n "Would you like to reload/restart this service? (yes/no): " read restart_confirm if [ "$restart_confirm" = "yes" ]; then echo "" echo "Select action:" echo "1) Reload (recommended - graceful, no downtime if supported)" echo "2) Restart (may cause interruption)" echo "0) Cancel" echo -n "Select: " read action_choice case $action_choice in 1) echo "" echo "Reloading $name..." systemctl reload "$service_name" 2>&1 if [ $? -eq 0 ]; then echo -e "${GREEN}✓ Service reloaded successfully${NC}" else echo -e "${YELLOW}Reload not supported, trying restart...${NC}" systemctl restart "$service_name" 2>&1 if [ $? -eq 0 ]; then echo -e "${GREEN}✓ Service restarted successfully${NC}" else echo -e "${RED}✗ Service restart failed${NC}" fi fi ;; 2) echo "" echo -e "${YELLOW}⚠ WARNING: This will cause service interruption!${NC}" echo -n "Type 'RESTART' to confirm: " read final_confirm if [ "$final_confirm" = "RESTART" ]; then echo "" echo "Restarting $name..." systemctl restart "$service_name" 2>&1 if [ $? -eq 0 ]; then echo -e "${GREEN}✓ Service restarted successfully${NC}" else echo -e "${RED}✗ Service restart failed${NC}" fi else echo "Restart cancelled" fi ;; 0) echo "Cancelled" ;; esac fi else echo "" echo -e "${YELLOW}Service Status: NOT RUNNING${NC}" fi fi else echo -e "${RED}✗ Configuration has errors${NC}" echo "" echo -e "${YELLOW}DO NOT restart this service until errors are fixed!${NC}" fi } # Helper function to get systemd service name from daemon name get_service_name() { local daemon=$1 case $daemon in sshd) echo "sshd" ;; nginx) echo "nginx" ;; httpd) echo "httpd" ;; php-fpm) echo "php-fpm" ;; named) echo "named" ;; rsyslog) echo "rsyslog" ;; postfix) echo "postfix" ;; mysql) echo "mysqld" ;; postgresql) echo "postgresql" ;; chrony) echo "chronyd" ;; haproxy) echo "haproxy" ;; firewalld) echo "firewalld" ;; *) echo "$daemon" ;; esac } # Helper function to show restart impacts show_restart_impacts() { echo "" echo -e "${CYAN}=== Service Restart Impact Guide ===${NC}" echo "" echo -e "${RED}CRITICAL IMPACT SERVICES:${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" for daemon in "${found_daemons[@]}"; do impact="${daemon_restart_impacts[$daemon]}" if echo "$impact" | grep -q "CRITICAL"; then echo "" echo -e "${daemon_names[$daemon]}:" echo "$impact" fi done echo "" echo -e "${YELLOW}MODERATE IMPACT SERVICES:${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" for daemon in "${found_daemons[@]}"; do impact="${daemon_restart_impacts[$daemon]}" if echo "$impact" | grep -q "MODERATE"; then echo "" echo -e "${daemon_names[$daemon]}:" echo "$impact" fi done echo "" echo -e "${GREEN}LOW IMPACT SERVICES:${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" for daemon in "${found_daemons[@]}"; do impact="${daemon_restart_impacts[$daemon]}" if echo "$impact" | grep -q "LOW"; then echo "" echo -e "${daemon_names[$daemon]}:" echo "$impact" fi done echo "" echo -e "${CYAN}BEST PRACTICES:${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "• Always test configuration before restart" echo "• Use 'reload' instead of 'restart' when possible" echo "• Schedule restarts during maintenance windows for critical services" echo "• Have rollback plan ready" echo "• Monitor service status after restart" echo "• For SSH: Keep existing session open, test new connection in separate window" echo "• For databases: Notify applications, drain connections if possible" echo "• For web servers: Use graceful restart options when available" echo "" } # MENU FUNCTIONS health_menu() { while true; do clear echo -e "${GREEN}╔════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ HEALTH MENU ║${NC}" echo -e "${GREEN}╚════════════════════════════════════════╝${NC}" echo "" echo "1) Check DNF Update Status" if [ $UPDATES_AVAILABLE -eq 1 ]; then echo -e "U) ${YELLOW}Run DNF Updates (Updates Available!)${NC}" else echo "U) Run DNF Updates" fi echo "2) Check Service Status" echo "3) Check LDAP/AD Health" echo "B) Back to Main Menu" echo "" echo -n "Select an option: " read choice case $choice in 1) clear check_updates echo -n "Press Enter to continue..." read ;; U|u) clear run_updates echo -n "Press Enter to continue..." read ;; 2) clear check_services echo -n "Press Enter to continue..." read ;; 3) clear check_ldap_health echo -n "Press Enter to continue..." read ;; B|b) break ;; *) echo -e "${RED}Invalid option${NC}" sleep 1 ;; esac done } security_menu() { while true; do clear echo -e "${MAGENTA}╔════════════════════════════════════════╗${NC}" echo -e "${MAGENTA}║ SECURITY MENU ║${NC}" echo -e "${MAGENTA}╚════════════════════════════════════════╝${NC}" echo "" if [ "$LOG_OUTPUT" = true ]; then echo -e "${GREEN}[LOGGING ENABLED]${NC}" echo "" fi echo "1) Show Most Active Users" echo "2) Search for Errors in System Logs" echo "3) Audit File Permissions" echo "4) Analyze Application Logs" echo "5) Security Scan (Logins/Ports/Firewall)" echo "6) DISA STIG Compliance Check" echo "A) Run All Security Checks" echo "" LOG_STATUS="OFF" if [ "$LOG_OUTPUT" = true ]; then LOG_STATUS="ON" fi echo -e "${CYAN}L) Toggle Logging (Currently: $LOG_STATUS)${NC}" if [ "$LOG_OUTPUT" = true ]; then echo -e "${CYAN}S) Save and View Report${NC}" fi echo "B) Back to Main Menu" echo "" echo -n "Select an option: " read choice case $choice in 1) clear if [ "$LOG_OUTPUT" = false ]; then start_logging fi check_active_users if [ "$LOG_OUTPUT" = false ]; then stop_logging fi echo -n "Press Enter..." read ;; 2) clear if [ "$LOG_OUTPUT" = false ]; then start_logging fi search_errors if [ "$LOG_OUTPUT" = false ]; then stop_logging fi echo -n "Press Enter..." read ;; 3) clear if [ "$LOG_OUTPUT" = false ]; then start_logging fi audit_permissions if [ "$LOG_OUTPUT" = false ]; then stop_logging fi echo -n "Press Enter..." read ;; 4) clear if [ "$LOG_OUTPUT" = false ]; then start_logging fi analyze_app_logs if [ "$LOG_OUTPUT" = false ]; then stop_logging fi echo -n "Press Enter..." read ;; 5) clear if [ "$LOG_OUTPUT" = false ]; then start_logging fi security_scan if [ "$LOG_OUTPUT" = false ]; then stop_logging fi echo -n "Press Enter..." read ;; 6) clear check_disa_stig echo -n "Press Enter..." read ;; A|a) clear if [ "$LOG_OUTPUT" = false ]; then start_logging fi check_active_users write_output "" search_errors write_output "" security_scan write_output "" if [ "$LOG_OUTPUT" = false ]; then stop_logging fi echo -n "Press Enter..." read ;; L|l) if [ "$LOG_OUTPUT" = true ]; then LOG_OUTPUT=false echo -e "${YELLOW}Logging disabled${NC}" else timestamp=$(date +%Y%m%d_%H%M%S) LOG_FILE="/tmp/rhel_security_audit_${timestamp}.txt" start_logging echo -e "${GREEN}Logging enabled: $LOG_FILE${NC}" fi sleep 2 ;; S|s) if [ "$LOG_OUTPUT" = true ]; then clear stop_logging echo "Report:" echo "----------------------------------------" cat "$LOG_FILE" echo "----------------------------------------" echo "" echo -n "Press Enter..." read else echo -e "${RED}Logging is not enabled${NC}" sleep 2 fi ;; B|b) if [ "$LOG_OUTPUT" = true ]; then stop_logging fi break ;; *) echo -e "${RED}Invalid option${NC}" sleep 1 ;; esac done } toolbox_menu() { while true; do clear echo -e "${CYAN}╔════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ TOOLBOX MENU ║${NC}" echo -e "${CYAN}╚════════════════════════════════════════╝${NC}" echo "" echo "1) SSH Configuration Audit" echo "2) Certificate Expiration Check" echo "3) Compare Installed Software" echo "4) Validate Daemon Configurations" echo "B) Back to Main Menu" echo "" echo -n "Select an option: " read choice case $choice in 1) clear check_ssh_config echo -n "Press Enter..." read ;; 2) clear check_certificates echo -n "Press Enter..." read ;; 3) clear compare_software echo -n "Press Enter..." read ;; 4) clear validate_daemon_configs echo -n "Press Enter..." read ;; B|b) break ;; *) echo -e "${RED}Invalid option${NC}" sleep 1 ;; esac done } show_menu() { clear echo -e "${GREEN}╔════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ RHEL System Management Script ║${NC}" echo -e "${GREEN}╚════════════════════════════════════════╝${NC}" echo "" echo -e "${GREEN}1) Health${NC} - System updates and health checks" echo -e "${MAGENTA}2) Security${NC} - User activity and error logs" echo -e "${CYAN}3) Toolbox${NC} - Configuration auditing tools" echo "" echo "Q) Quit" echo "" echo -n "Select a category: " } main() { check_root while true; do show_menu read choice case $choice in 1) health_menu ;; 2) security_menu ;; 3) toolbox_menu ;; Q|q) echo "Exiting..." exit 0 ;; *) echo -e "${RED}Invalid option${NC}" sleep 1 ;; esac done } main
Content is user-generated and unverified.
    RHEL System Toolbox: Complete Admin & Security Audit Script | Claude