#!/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