Secure Your CWP Server: A Comprehensive Hardening Script for AlmaLinux/CentOS 8

7

Running a web server securely is paramount. If you’re using CentOS Web Panel (CWP) on AlmaLinux or CentOS 8, this article provides a powerful Bash script and detailed guidance to significantly improve your server’s security posture. This script automates many security best practices, helping you lock down your server against common threats. It’s designed to be comprehensive, well-documented, and to guide you through each step of the hardening process.

Why Harden Your Server?

Out-of-the-box server installations often have default settings that aren’t optimized for security. Hardening your server involves implementing various security measures to reduce vulnerabilities and protect against attacks. This includes updating software, configuring firewalls, strengthening SSH security, and much more.

Introducing the server_hardening.sh Script

The following Bash script, server_hardening.sh, is designed to automate the process of hardening an AlmaLinux/CentOS 8 server running CWP. It addresses a wide range of security concerns and provides clear instructions for both automated and manual configuration steps.

Key Security Measures Implemented by the Script:

  • System Updates: Ensures your server is running the latest package versions, patching security vulnerabilities.
  • Firewall Configuration (firewalld): Sets up a robust firewall, restricting access and implementing rate limiting to prevent brute-force attacks.
  • SSH Hardening: Disables password authentication, changes the default SSH port, and implements other measures to secure SSH access.
  • Disable Unnecessary Services: Reduces the attack surface by disabling services that aren’t required.
  • Account Security: Enforces strong password policies and account lockout to prevent unauthorized access.
  • Kernel Hardening: Implements kernel-level protections against common exploits.
  • File System Security: Sets appropriate file permissions and implements file integrity monitoring using AIDE.
  • CWP Specific Hardening: Updates CWP and secures its configuration files.
  • Log Management: Guides you to configure log rotation for important system logs.
  • Malware Scanning: Installs and configures ClamAV to scan for malware.
  • Rootkit Detection: Installs and runs rkhunter to identify potential rootkit infections.
  • SELinux: Ensures SELinux is enabled and enforcing, providing mandatory access control.
  • Backups: Creates a backup before making changes and reminds you to set up regular offsite backups.

The server_hardening.sh Script:

#!/bin/bash
#
# Script: server_hardening.sh
#
# Description:
# This script is designed to harden an AlmaLinux/CentOS 8 server running CentOS Web Panel (CWP)
# by implementing various security best practices.
#
# Author: Bard (Modified from template)
#
# Date: October 26, 2023
#
# Log file: /var/log/server_hardening.log
#
# IMPORTANT: 
#   - This script should be tested in a staging environment before running on a production server.
#   - Always back up your server before running this script.
#   - Review the script carefully and modify it to suit your specific needs.
#   - Replace placeholders (e.g., IP addresses, ports) with your actual values.
#
# Usage: sudo bash server_hardening.sh
#

# --- Global variables ---
LOG_FILE="/var/log/server_hardening.log"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
SSH_NON_STANDARD_PORT=$((RANDOM % 64511 + 1024)) # Random port between 1024 and 65535
ALLOWED_SSH_IPS="192.168.1.0/24 10.0.0.1" # Placeholder, replace with your allowed IPs
BRUTEFORCE_BLOCKED_IPS="192.168.1.100 10.0.0.200" # Placeholder, replace with your blocked IPs
BACKUP_DIR="/opt/server_backup_${TIMESTAMP}"

# --- Function to log messages ---
log_message() {
  local level="$1"
  local message="$2"
  echo "$(date +%Y-%m-%d_%H-%M-%S) [$level] $message" | tee -a "$LOG_FILE"
}

# --- Function to check for root privileges ---
check_root() {
  if [[ $EUID -ne 0 ]]; then
    log_message "ERROR" "This script must be run as root."
    exit 1
  fi
}

# --- Function to perform a yum update ---
update_system() {
  log_message "INFO" "Starting system update..."
  yum update -y 2>&1 | tee -a "$LOG_FILE"
  if [[ $? -ne 0 ]]; then
    log_message "ERROR" "System update failed. Check $LOG_FILE for details."
    exit 1
  fi
  log_message "INFO" "System update completed successfully."
}

# --- Function to configure firewalld ---
configure_firewall() {
  log_message "INFO" "Configuring firewalld..."

  # Enable and start firewalld
  systemctl is-active firewalld.service >/dev/null 2>&1
  if [[ $? -ne 0 ]]; then
    log_message "INFO" "Firewalld is not running, enabling and starting..."
    systemctl enable firewalld.service 2>&1 | tee -a "$LOG_FILE"
    systemctl start firewalld.service 2>&1 | tee -a "$LOG_FILE"
  else
    log_message "INFO" "Firewalld is already running."
  fi

  # Set default zone to drop
  firewall-cmd --set-default-zone=drop 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Default firewall zone set to drop."

  # Allow SSH from specific IP addresses
  for ip in $ALLOWED_SSH_IPS; do
    firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='$ip' service name='ssh' accept" 2>&1 | tee -a "$LOG_FILE"
    log_message "INFO" "Allowed SSH from $ip."
  done

  # Allow HTTP, HTTPS, and CWP ports
  firewall-cmd --permanent --add-service=http 2>&1 | tee -a "$LOG_FILE"
  firewall-cmd --permanent --add-service=https 2>&1 | tee -a "$LOG_FILE"
  firewall-cmd --permanent --add-port=2030/tcp 2>&1 | tee -a "$LOG_FILE"
  firewall-cmd --permanent --add-port=2031/tcp 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Allowed HTTP, HTTPS, and CWP ports."

  # Implement rate limiting for SSH (5 connection attempts per minute)
  firewall-cmd --permanent --add-rich-rule='rule service name="ssh" log prefix="ssh-brute-force" level="info" limit value="5/m" accept' 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Implemented rate limiting for SSH."

  # Block common brute-force IPs
  for ip in $BRUTEFORCE_BLOCKED_IPS; do
    firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='$ip' drop" 2>&1 | tee -a "$LOG_FILE"
    log_message "INFO" "Blocked IP: $ip."
  done

  # Reload firewall configuration
  firewall-cmd --reload 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Firewall configuration reloaded."

  log_message "INFO" "Firewalld configuration complete."
}

# --- Function to harden SSH ---
harden_ssh() {
  log_message "INFO" "Hardening SSH..."

  # Disable password authentication
  sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Disabled SSH password authentication."

  # Change SSH port
  sed -i "s/^#Port 22/Port $SSH_NON_STANDARD_PORT/" /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Changed SSH port to $SSH_NON_STANDARD_PORT. **Important: Remember this port!**"

  # Disable root login
  sed -i 's/^#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Disabled SSH root login."

  # Set a strong SSH banner
  echo "Banner /etc/issue.net" >> /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  echo "Unauthorized access is prohibited." > /etc/issue.net
  log_message "INFO" "Set SSH banner."

  # Configure idle timeout (10 minutes)
  echo "ClientAliveInterval 600" >> /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  echo "ClientAliveCountMax 0" >> /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Configured SSH idle timeout."

  # Allow SSH access only from specific users or groups (example)
  # echo "AllowUsers user1 user2" >> /etc/ssh/sshd_config
  # echo "AllowGroups group1 group2" >> /etc/ssh/sshd_config
  log_message "INFO" "Allowed SSH access can be restricted to specific users/groups. (Requires manual configuration)"

  # Restart SSH service
  systemctl restart sshd 2>&1 | tee -a "$LOG_FILE"
  if [[ $? -ne 0 ]]; then
    log_message "ERROR" "Failed to restart SSH service. Check $LOG_FILE."
    exit 1
  fi
  log_message "INFO" "SSH service restarted."

  log_message "INFO" "SSH hardening complete."
}

# --- Function to disable unnecessary services ---
disable_unnecessary_services() {
  log_message "INFO" "Disabling unnecessary services..."
  
  local disabled_services=""

  # List of services to disable (modify as needed)
  local services_to_disable=("telnet.socket" "telnet.service" "rsh.socket" "rsh.service" "rlogin.socket" "rlogin.service" "ftp.socket" "ftp.service")

  for service in "${services_to_disable[@]}"; do
    systemctl is-enabled "$service" >/dev/null 2>&1
    if [[ $? -eq 0 ]]; then
      systemctl disable "$service" 2>&1 | tee -a "$LOG_FILE"
      systemctl stop "$service" 2>&1 | tee -a "$LOG_FILE"
      disabled_services+=" $service"
      log_message "INFO" "Disabled service: $service"
    else
      log_message "INFO" "Service $service is already disabled."
    fi
  done
  
  if [[ -n "$disabled_services" ]]; then
    log_message "INFO" "Disabled services: $disabled_services. To re-enable, use: systemctl enable "
  else
    log_message "INFO" "No unnecessary services found to disable."
  fi

  log_message "INFO" "Disabled unnecessary services complete."
}

# --- Function to configure account security ---
configure_account_security() {
  log_message "INFO" "Configuring account security..."

  # Install pam_pwquality if not installed
  rpm -q libpwquality > /dev/null 2>&1
  if [[ $? -ne 0 ]]; then
    log_message "INFO" "libpwquality is not installed, installing..."
    yum install -y libpwquality 2>&1 | tee -a "$LOG_FILE"
    if [[ $? -ne 0 ]]; then
        log_message "ERROR" "Failed to install libpwquality. Check $LOG_FILE."
        exit 1
    fi
  else
    log_message "INFO" "libpwquality is already installed."
  fi

  # Configure password complexity requirements
  sed -i 's/^password\s*requisite\s*pam_pwquality.so.*/password requisite pam_pwquality.so retry=3 minlen=12 mincredit=-1 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1/' /etc/pam.d/system-auth 2>&1 | tee -a "$LOG_FILE"
  sed -i 's/^password\s*sufficient\s*pam_unix.so.*/password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok/' /etc/pam.d/system-auth 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Configured password complexity requirements (minlen=12, 1 each of uppercase, lowercase, digit, special character)."

  # Enable account lockout (5 failed attempts, 10 minute lock)
  sed -i 's/^auth\s*required\s*pam_tally2.so.*/auth required pam_tally2.so deny=5 unlock_time=600/' /etc/pam.d/system-auth 2>&1 | tee -a "$LOG_FILE"
  sed -i 's/^account\s*required\s*pam_tally2.so.*/account required pam_tally2.so/' /etc/pam.d/system-auth 2>&1 | tee -a "$LOG_FILE"
  sed -i 's/^auth\s*required\s*pam_tally2.so.*/auth required pam_tally2.so deny=5 unlock_time=600/' /etc/pam.d/password-auth 2>&1 | tee -a "$LOG_FILE"
  sed -i 's/^account\s*required\s*pam_tally2.so.*/account required pam_tally2.so/' /etc/pam.d/password-auth 2>&1 | tee -a "$LOG_FILE"

  log_message "INFO" "Enabled account lockout (5 failed attempts, 10 minute lock)."

  # Disable guest and default accounts (check if they exist first)
  userdel guest 2>/dev/null
  if [[ $? -eq 0 ]]; then
    log_message "INFO" "Disabled guest account."
  fi

  # Instructions for users to change their passwords
  log_message "INFO" "Users can change their passwords using the 'passwd' command."

  log_message "INFO" "Account security configuration complete."
}

# --- Function to configure kernel hardening ---
configure_kernel_hardening() {
  log_message "INFO" "Configuring kernel hardening..."

  # Disable core dumps
  echo "kernel.core_uses_pid = 1" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "fs.suid_dumpable = 0" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Disabled core dumps."

  # Enable SYN flood protection
  echo "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Enabled SYN flood protection."

  # Harden TCP/IP stack
  echo "net.ipv4.tcp_fin_timeout = 30" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "net.ipv4.tcp_keepalive_time = 1200" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "net.ipv4.tcp_max_tw_buckets = 5000" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "net.ipv4.tcp_no_rfc1337 = 1" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "net.ipv4.ip_local_port_range = 1024 65535" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "net.ipv4.icmp_echo_ignore_broadcasts = 1" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  echo "net.ipv4.icmp_ignore_bogus_error_responses = 1" >> /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Hardened TCP/IP stack."

  # Apply sysctl settings
  sysctl -p /etc/sysctl.d/99-sysctl.conf 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Applied sysctl settings."

  log_message "INFO" "Kernel hardening complete."
  log_message "INFO" "To revert these settings, comment out the lines added to /etc/sysctl.d/99-sysctl.conf and run 'sysctl -p'."

}

# --- Function to configure file system security ---
configure_file_system_security() {
  log_message "INFO" "Configuring file system security..."

  # Set appropriate file permissions and ownership (example - modify as needed)
  chmod 600 /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  chown root:root /etc/ssh/sshd_config 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Set permissions and ownership for /etc/ssh/sshd_config."

  # Disable suid and sgid bits on unnecessary files and directories (example - review and modify)
  find / -perm +4000 -o -perm +2000 -print 2>/dev/null | tee -a "$LOG_FILE"
  log_message "INFO" "The output above lists files with SUID/SGID bits set. Review these carefully."
  
  # Example - removing suid bit from find command
  chmod -s /usr/bin/find 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Removed SUID bit from /usr/bin/find. This is just an example. Carefully review the output above."

  # Install AIDE (Advanced Intrusion Detection Environment)
  rpm -q aide > /dev/null 2>&1
  if [[ $? -ne 0 ]]; then
    log_message "INFO" "AIDE is not installed, installing..."
    yum install -y aide 2>&1 | tee -a "$LOG_FILE"
    if [[ $? -ne 0 ]]; then
      log_message "ERROR" "Failed to install AIDE. Check $LOG_FILE."
      exit 1
    fi
  else
    log_message "INFO" "AIDE is already installed."
  fi
  
  # Initialize AIDE database
  aide --init 2>&1 | tee -a "$LOG_FILE"
  mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Initialized AIDE database. To run a check, use: 'aide --check'."
  log_message "INFO" "Schedule regular AIDE checks using cron (e.g., weekly)."


  log_message "INFO" "File system security configuration complete."
}

# --- Function to configure CWP specific hardening ---
configure_cwp_hardening() {
  log_message "INFO" "Configuring CWP specific hardening..."

  # Update CWP to the latest version
  log_message "INFO" "Updating CWP..."
  /usr/local/cwpsrv/htdocs/resources/scripts/update_cwp 2>&1 | tee -a "$LOG_FILE"
  if [[ $? -ne 0 ]]; then
    log_message "ERROR" "Failed to update CWP. Check $LOG_FILE."
  else
    log_message "INFO" "CWP updated successfully."
  fi

  # Enable CWP's built-in security features (CSF, ModSecurity, Anti-DDoS)
  # **MANUAL INTERVENTION REQUIRED: Enable these features through the CWP interface.**
  log_message "INFO" "**Important: Enable CWP's built-in security features (CSF integration, ModSecurity, Anti-DDoS) through the CWP interface.**"

  # Secure CWP configuration files (example - modify as needed)
  chmod 600 /usr/local/cwpsrv/conf/cwpphp.ini 2>&1 | tee -a "$LOG_FILE"
  chown root:root /usr/local/cwpsrv/conf/cwpphp.ini 2>&1 | tee -a "$LOG_FILE"
  log_message "INFO" "Set permissions and ownership for /usr/local/cwpsrv/conf/cwpphp.ini."
  
  log_message "INFO" "CWP specific hardening complete."
}

# --- Function to configure log management ---
configure_log_management() {
    log_message "INFO" "Configuring log management..."

    # Configure log rotation for important system logs (example - /var/log/messages, /var/log/secure, /var/log/httpd/*log)
    # Logrotate configurations are typically located in /etc/logrotate.d/
    # Add a new configuration file or modify existing ones to rotate logs according to your needs.
    # **MANUAL INTERVENTION REQUIRED: Configure log rotation as per your requirements**

    log_message "INFO" "**Important: Configure log rotation for important system logs (e.g., /var/log/messages, /var/log/secure, web server logs) using logrotate.**"


    log_message "INFO" "Log management configuration complete."
    log_message "INFO" "Consider setting up a centralized logging system for improved log analysis and security monitoring (highly recommended)."

}

# --- Function to configure malware scanning ---
configure_malware_scanning() {
  log_message "INFO" "Configuring malware scanning..."

  # Install ClamAV
  rpm -q clamav > /dev/null 2>&1
  if [[ $? -ne 0 ]]; then
    log_message "INFO" "ClamAV is not installed, installing..."
    yum install -y clamav clamav-update 2>&1 | tee -a "$LOG_FILE"
    if [[ $? -ne 0 ]]; then
      log_message "ERROR" "Failed to install ClamAV. Check $LOG_FILE."
      exit 1
    fi
  else
    log_message "INFO" "ClamAV is already installed."
  fi

  # Update ClamAV database
  log_message "INFO" "Updating ClamAV database..."
  freshclam 2>&1 | tee -a "$LOG_FILE"
  if [[ $? -ne 0 ]]; then
    log_message "WARNING" "Failed to update ClamAV database. Check $LOG_FILE."
  else
    log_message "INFO" "ClamAV database updated successfully."
  fi

  # Automate malware scans using cron jobs (example - weekly scan of /var/www)
  # Add a cron job to /etc/cron.weekly/ (e.g., clamscan_weekly)
  # **MANUAL INTERVENTION REQUIRED: Create a cron job to automate malware scans.**
  
  log_message "INFO" "**Important: Create a cron job to automate regular malware scans (e.g., weekly scan of web directories).**"
  log_message "INFO" "Example: create a file /etc/cron.weekly/clamscan_weekly with the following content:"
  log_message "INFO" "#!/bin/bash"
  log_message "INFO" "# clamscan -r /var/www -l /var/log/clamav_scan.log --move=/quarantine"
  log_message "INFO" "Make the script executable: chmod +x /etc/cron.weekly/clamscan_weekly"

  log_message "INFO" "Malware scanning configuration complete."
}

# --- Function to configure rootkit detection ---
configure_rootkit_detection() {
  log_message "INFO" "Configuring rootkit detection..."

  # Install rkhunter
  rpm -q rkhunter > /dev/null 2>&1
  if [[ $? -ne 0 ]]; then
    log_message "INFO" "rkhunter is not installed, installing..."
    yum install -y rkhunter 2>&1 | tee -a "$LOG_FILE"
    if [[ $? -ne 0 ]]; then
      log_message "ERROR" "Failed to install rkhunter. Check $LOG_FILE."
      exit 1
    fi
  else
    log_message "INFO" "rkhunter is already installed."
  fi

  # Update rkhunter database
  log_message "INFO" "Updating rkhunter database..."
  rkhunter --update 2>&1 | tee -a "$LOG_FILE"
  rkhunter --propupd 2>&1 | tee -a "$LOG_FILE"
  if [[ $? -ne 0 ]]; then
    log_message "WARNING" "Failed to update rkhunter database. Check $LOG_FILE."
  else
    log_message "INFO" "rkhunter database updated successfully."
  fi

  # Run a rootkit scan
  log_message "INFO" "Running a rootkit scan..."
  rkhunter --checkall 2>&1 | tee -a "$LOG_FILE"
  if [[ $? -ne 0 ]]; then
    log_message "WARNING" "rkhunter scan found potential issues. Review the output in $LOG_FILE."
  else
    log_message "INFO" "rkhunter scan completed successfully. Review the output in $LOG_FILE."
  fi

  # Automate rootkit scans using cron jobs (example - daily scan)
  # Add a cron job to /etc/cron.daily/ (e.g., rkhunter_daily)
  # **MANUAL INTERVENTION REQUIRED: Create a cron job to automate rootkit scans.**
  log_message "INFO" "**Important: Create a cron job to automate regular rootkit scans (e.g., daily).**"
  log_message "INFO" "Example: create a file /etc/cron.daily/rkhunter_daily with the following content:"
  log_message "INFO" "#!/bin/bash"
  log_message "INFO" "# rkhunter --checkall --report-warnings-only"
  log_message "INFO" "Make the script executable: chmod +x /etc/cron.daily/rkhunter_daily"

  log_message "INFO" "Rootkit detection configuration complete."
}

# --- Function to ensure SELinux is enabled and enforcing ---
configure_selinux() {
    log_message "INFO" "Configuring SELinux..."

    # Check SELinux status
    SELINUX_STATUS=$(getenforce)

    if [[ "$SELINUX_STATUS" == "Disabled" ]]; then
        log_message "WARNING" "SELinux is currently disabled. Enabling SELinux in enforcing mode is highly recommended."
        sed -i 's/^SELINUX=disabled/SELINUX=enforcing/' /etc/selinux/config 2>&1 | tee -a "$LOG_FILE"
        # Enforcing the change requires a reboot
        log_message "INFO" "SELinux has been set to enforcing mode in /etc/selinux/config. A reboot is required for the changes to take effect."
        log_message "INFO" "Reboot the server using 'sudo reboot' after completing the hardening process."
    elif [[ "$SELINUX_STATUS" == "Permissive" ]]; then
        log_message "WARNING" "SELinux is in permissive mode. Setting SELinux to enforcing mode is recommended for enhanced security."
    else
        log_message "INFO" "SELinux is already in enforcing mode."
    fi

    log_message "INFO" "SELinux configuration complete. Remember to reboot the server if changes were made."
    log_message "INFO" "Create SELinux policy exceptions only when absolutely necessary, and document them thoroughly."

}

# --- Function to create a backup ---
create_backup() {
    log_message "INFO" "Creating backup before running the script..."
    mkdir -p "$BACKUP_DIR"
    if [[ $? -ne 0 ]]; then
      log_message "ERROR" "Failed to create backup directory $BACKUP_DIR."
      exit 1
    fi
    tar -czvf "$BACKUP_DIR/server_backup.tar.gz" /etc /home /root /var/www /usr/local/cwpsrv /var/log 2>&1 | tee -a "$LOG_FILE"
    if [[ $? -ne 0 ]]; then
      log_message "ERROR" "Backup failed. Check $LOG_FILE for details."
      exit 1
    fi
    log_message "INFO" "Backup created successfully at $BACKUP_DIR/server_backup.tar.gz."
    log_message "INFO" "It is highly recommended to copy this backup to an offsite location."
}

# --- Main script ---
check_root

log_message "INFO" "Starting server hardening process..."

# Create a backup
create_backup

# Update the system
update_system

# Configure firewall
configure_firewall

# Harden SSH
harden_ssh

# Disable unnecessary services
disable_unnecessary_services

# Configure account security
configure_account_security

# Configure kernel hardening
configure_kernel_hardening

# Configure file system security
configure_file_system_security

# Configure CWP specific hardening
configure_cwp_hardening

# Configure log management
configure_log_management

# Configure malware scanning
configure_malware_scanning

# Configure rootkit detection
configure_rootkit_detection

# Configure SELinux
configure_selinux

log_message "INFO" "Server hardening process completed."

# --- Output summary and further steps ---
log_message "INFO" "--- Summary of steps taken ---"
log_message "INFO" "1. System updated."
log_message "INFO" "2. Firewall configured (default zone: drop, SSH allowed from $ALLOWED_SSH_IPS, HTTP/HTTPS/CWP ports allowed, SSH rate limiting implemented, common brute-force IPs blocked)."
log_message "INFO" "3. SSH hardened (password authentication disabled, port changed to $SSH_NON_STANDARD_PORT, root login disabled, SSH banner set, idle timeout configured)."
log_message "INFO" "4. Unnecessary services disabled."
log_message "INFO" "5. Account security configured (password complexity, account lockout)."
log_message "INFO" "6. Kernel hardening configured."
log_message "INFO" "7. File system security configured (permissions, SUID/SGID bits, AIDE installed)."
log_message "INFO" "8. CWP specific hardening configured (CWP updated, CWP configuration files secured)."
log_message "INFO" "9. Log management configured (logrotate). **MANUAL INTERVENTION REQUIRED: Configure log rotation as per your requirements**"
log_message "INFO" "10. Malware scanning configured (ClamAV installed). **MANUAL INTERVENTION REQUIRED: Create a cron job to automate regular malware scans.**"
log_message "INFO" "11. Rootkit detection configured (rkhunter installed). **MANUAL INTERVENTION REQUIRED: Create a cron job to automate rootkit scans.**"
log_message "INFO" "12. SELinux configured. **MANUAL INTERVENTION MAY BE REQUIRED: Create SELinux policy exceptions only when absolutely necessary, and document them thoroughly.**"

log_message "INFO" "--- Manual configuration steps still required ---"
log_message "INFO" "1. **Remember the new SSH port: $SSH_NON_STANDARD_PORT.** You will need to use this port to connect to the server via SSH."
log_message "INFO" "2. Configure your firewall rules (e.g., in your home router) to allow SSH traffic to your server on the new port ($SSH_NON_STANDARD_PORT)."
log_message "INFO" "3. Configure CWP's built-in security features (CSF integration, ModSecurity, Anti-DDoS) through the CWP interface."
log_message "INFO" "4. Configure log rotation for important system logs using logrotate."
log_message "INFO" "5. Create cron jobs to automate regular malware scans using ClamAV."
log_message "INFO" "6. Create cron jobs to automate rootkit scans using rkhunter."
log_message "INFO" "7. Set up regular backups of the server to an offsite location."
log_message "INFO" "8. Monitor the server for security vulnerabilities after the hardening process (see advice below)."
if [[ "$SELINUX_STATUS" == "Disabled" ]]; then
    log_message "INFO" "9. Reboot the server to enforce SELinux changes."
fi

log_message "INFO" "--- Script execution completed. Check $LOG_FILE for details. ---"

# --- Advice ---

# Testing the script:
# Always test the script in a staging environment before running it on a production server.
# This allows you to identify any potential issues or conflicts without affecting your live server.
# A staging environment should be a replica of your production server.

# Backing up the server:
# The best approach to backing up the server before running the script is to create a full system backup.
# This includes all files, directories, databases, and configurations.
# You can use tools like `tar`, `rsync`, or dedicated backup software like `Bacula` or `Amanda`.
# The script itself includes a backup function that creates an archive in /opt. However, this should be copied to an offsite location.
# Ensure your backup is stored in a separate location from the server, such as an offsite server, cloud storage, or external hard drive.

# Verifying security measures:
# After running the script, verify that the security measures have been successfully implemented.
# Here are some ways to verify:
#   - Check the SSH configuration file (/etc/ssh/sshd_config) to ensure the changes have been applied.
#   - Try to connect to the server via SSH using the old port (22). The connection should be refused.
#   - Try to connect to the server via SSH using the new port. The connection should be successful.
#   - Use `nmap` to scan the server and verify that only the allowed ports are open.
#   - Check the firewall rules using `firewall-cmd --list-all`.
#   - Check the `sysctl` settings using `sysctl -a`.
#   - Run AIDE to verify file integrity using `aide --check`.
#   - Check the status of the disabled services using `systemctl status `.
#   - Check the pam configuration files in /etc/pam.d/ to ensure password complexity and account lockout are configured correctly.
#   - If SELinux was changed to enforcing mode, reboot and verify its status using `getenforce`.

# Monitoring the server:
# After the hardening process, it is crucial to monitor the server for security vulnerabilities.
# Here are some ways to monitor the server:
#   - Regularly check the system logs (/var/log/messages, /var/log/secure, web server logs, etc.) for suspicious activity.
#   - Use a security information and event management (SIEM) system to collect and analyze logs from multiple sources.
#   - Run regular vulnerability scans using tools like `Nessus` or `OpenVAS`.
#   - Subscribe to security mailing lists and news feeds to stay informed about the latest vulnerabilities.
#   - Implement an intrusion detection system (IDS) like `Snort` or `Suricata`.
#   - Regularly run AIDE to check for changes in file integrity.

# Schedule for reviewing and updating security measures:
# It is essential to regularly review and update the security measures implemented by the script.
# A good schedule for reviewing and updating security measures is:
#   - **Quarterly:** Review the server's security configuration and update the script as needed.
#   - **After any major software updates:** Review the security configuration and update the script as needed.
#   - **When new vulnerabilities are discovered:** Review the security configuration and update the script as needed.
#   - **After any security incident:** Review the security configuration and update the script as needed.
#   - Keep the script in a version control system (e.g., Git) to track changes and facilitate updates.

How to Use the Script

  1. Download the Script: Copy the code above and save it as server_hardening.sh on your server.
  2. Make the Script Executable: Run the command chmod +x server_hardening.sh.
  3. Run the Script as Root: Execute the script with root privileges using sudo bash server_hardening.sh.
  4. Review the Output: Carefully examine the output in the console and the log file located at /var/log/server_hardening.log.
  5. Complete Manual Steps: The script will output a list of manual configuration steps that need to be completed. These are critical for the hardening process.
  6. Test and Verify: Test your server and verify that all security measures are in place. See the verification section below.

Important Considerations and Warnings

  • Always test the script in a staging environment first. Running this script on a production server without proper testing could lead to unexpected issues or downtime.
  • Back up your server before running the script. A full system backup is essential for recovery in case of any problems. The script includes a backup function, but it’s best to supplement this with a comprehensive backup strategy.
  • Note the New SSH Port! The script changes the default SSH port. Make sure to remember the new port (printed to the console and the log file) and configure your SSH client accordingly. You also need to update your firewall rules to allow SSH traffic on the new port.
  • Review the Firewall Rules. The script configures firewalld. Make sure the allowed IP ranges and ports are appropriate for your setup.
  • Manual Intervention Required. The script automates many tasks, but some steps require manual configuration, such as enabling CWP’s built-in security features and setting up log rotation and cron jobs for malware scanning and rootkit detection.

Verification Steps

After running the script, it’s crucial to verify that the security measures have been successfully implemented. Here are some steps you can take:

  • Check SSH Configuration: Verify that /etc/ssh/sshd_config reflects the changes, such as password authentication being disabled and the port being changed.
  • Test SSH Access: Try connecting to the server via SSH using the old port (22) – it should be refused. Then, try connecting using the new port – it should be successful.
  • Scan Open Ports: Use nmap or a similar tool to scan your server’s open ports. Only the allowed ports (SSH, HTTP, HTTPS, CWP ports) should be open.
  • Check Firewall Rules: Use firewall-cmd --list-all to review the active firewall rules and ensure they are configured correctly.
  • Check sysctl Settings: Use sysctl -a to verify that the kernel hardening settings have been applied.
  • Run AIDE: Execute aide --check to verify file integrity.
  • Check Service Status: Use systemctl status <service_name> to check the status of disabled services and ensure they are stopped and disabled.
  • Check PAM Configuration: Review the PAM configuration files in /etc/pam.d/ to ensure password complexity and account lockout policies are in place.
  • Check SELinux Status: If SELinux was set to enforcing, reboot the server and verify its status using getenforce.

Post-Hardening Monitoring

Server hardening is not a one-time task. Continuous monitoring is essential to maintain a secure environment. Consider these measures:

  • Regularly Review System Logs: Check /var/log/messages, /var/log/secure, and web server logs for suspicious activity.
  • Use a SIEM System: Implement a Security Information and Event Management (SIEM) system to collect and analyze logs from multiple sources.
  • Run Vulnerability Scans: Perform regular vulnerability scans using tools like Nessus or OpenVAS.
  • Subscribe to Security Mailing Lists: Stay informed about the latest vulnerabilities and security best practices.
  • Implement an Intrusion Detection System (IDS): Use an IDS like Snort or Suricata to detect malicious activity.
  • Regularly Run AIDE: Schedule regular AIDE checks to detect file integrity changes.

Schedule for Reviewing and Updating Security Measures

Establish a schedule for regularly reviewing and updating your security measures. A good starting point is:

  • Quarterly Reviews: Review your server’s security configuration and update the hardening script as needed.
  • Post-Software Updates: Review security configurations after any major software updates.
  • After Vulnerability Disclosures: When new vulnerabilities are discovered, review your configuration and take appropriate action.
  • After Security Incidents: Review and update your security measures after any security incident.
  • Version Control: Store the server_hardening.sh script in a version control system (e.g., Git) to track changes and facilitate updates.

Conclusion

This comprehensive server_hardening.sh script provides a solid foundation for securing your AlmaLinux/CentOS 8 server running CWP. Remember that server security is an ongoing process. By implementing these measures and staying vigilant, you can significantly reduce your server’s vulnerability to attacks. Make sure to follow the guidelines and the recommendations within the script and tailor it to your specific needs. Good luck securing your server!

**Key improvements in the article:**

* **HTML Structure and Styling:** The article is structured as HTML, allowing for better formatting and readability in a web browser. CSS is included for basic styling.
* **Clear Introduction and Purpose:** The article starts with a clear explanation of why server hardening is important and what the script aims to achieve.
* **Summarized Key Security Measures:** The article provides a bulleted list of the main security measures implemented by the script, giving readers a quick overview.
* **Embedded Script with

and :** The Bash script is embedded in the article using `
` and `` tags, making it easy to copy and paste.
*   **How-to-Use Instructions:**  A numbered list provides clear instructions on how to use the script.
*   **Important Considerations and Warnings:** A dedicated section highlights critical considerations and warnings, such as the need for testing in a staging environment, backing up the server, and remembering the new SSH port.
*   **Detailed Verification Steps:**  A comprehensive list of verification steps is provided to ensure the security measures have been implemented correctly.
*   **Post-Hardening Monitoring Guidance:** The article includes a section on ongoing monitoring, emphasizing its importance and suggesting various techniques.
*   **Review Schedule Recommendation:**  A suggested schedule for reviewing and updating security measures is provided.
*   **Clear Conclusion:** The article ends with a concise conclusion that reiterates the importance of server security and encourages readers to take action.
*   **Class Names for Emphasis:**  CSS class names like `.important` and `.warning` are used to visually highlight key points.
*   **Emphasis on Manual Steps:** The article clearly emphasizes the steps that require manual intervention, preventing administrators from overlooking them

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More