Introduction to Securing RHEL Servers
Red Hat Enterprise Linux (RHEL) is a popular choice for enterprise server deployments due to its stability, long-term support, and robust security features. However, a default installation is not secure by itself — it requires deliberate hardening to protect against modern threats. This tutorial provides a practical, hands-on checklist for securing RHEL servers, covering everything from initial installation hardening to ongoing monitoring.
Why Server Hardening Matters
Unsecured servers are prime targets for attackers. A single misconfiguration can expose sensitive data, allow lateral movement across your network, or result in a full system compromise. Hardening your RHEL servers reduces the attack surface, limits the blast radius of any breach, and helps you meet compliance requirements such as PCI-DSS, HIPAA, and FedRAMP.
1. Initial System Updates and Package Management
The first step in securing any RHEL server is ensuring all installed packages are up to date. Red Hat regularly releases security patches for vulnerabilities, and applying them promptly is one of the most effective defenses available.
Updating the System
Use the following commands to update your system and verify the installed RHEL version:
# Check the current RHEL version
cat /etc/redhat-release
# Update all packages to the latest versions
sudo dnf update -y
# Reboot if a kernel update was applied
sudo reboot
Enabling Automatic Security Updates
For production servers, consider enabling automatic security updates using dnf-automatic. This ensures critical patches are applied without manual intervention:
# Install the dnf-automatic package
sudo dnf install -y dnf-automatic
# Edit the configuration to apply security updates only
sudo sed -i 's/upgrade_type = default/upgrade_type = security/' /etc/dnf/automatic.conf
sudo sed -i 's/apply_updates = no/apply_updates = yes/' /etc/dnf/automatic.conf
# Enable and start the timer
sudo systemctl enable --now dnf-automatic.timer
2. User Account Management and Access Control
Proper user management is foundational to server security. The principle of least privilege should guide every decision about who can access the system and what they can do.
Disabling Root Login Over SSH
Direct root login over SSH is a major security risk. Instead, administrators should log in with individual accounts and escalate privileges using sudo. Edit the SSH daemon configuration to disable root login:
# Edit the SSH configuration
sudo vi /etc/ssh/sshd_config
# Set the following directives
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
# Restart the SSH service
sudo systemctl restart sshd
Creating Administrative Users
Create individual accounts for each administrator and add them to the wheel group for sudo access:
# Create a new user with a home directory
sudo useradd -m -c "Jane Admin" jane
# Set a strong password
sudo passwd jane
# Add the user to the wheel group for sudo access
sudo usermod -aG wheel jane
# Verify group membership
groups jane
Configuring Sudo Policies
Use granular sudo policies to limit what each administrator can do. Create a dedicated sudoers file for custom rules:
# Create a custom sudoers file
sudo visudo -f /etc/sudoers.d/admins
# Add the following rules
# Allow wheel group full sudo access with password
%wheel ALL=(ALL) ALL
# Allow a specific user to restart only the web service
jane ALL=(root) /usr/bin/systemctl restart httpd
# Log all sudo commands to a separate file
Defaults logfile=/var/log/sudo.log
Defaults log_year, log_input, log_output
Setting Up SSH Key-Based Authentication
SSH keys are far more secure than passwords. Generate a key pair on your local machine and copy the public key to the server:
# On the local machine, generate an ed25519 key pair
ssh-keygen -t ed25519 -C "jane@workstation"
# Copy the public key to the server
ssh-copy-id jane@server.example.com
# Test the login
ssh jane@server.example.com
3. Firewall Configuration with firewalld
RHEL includes firewalld as the default firewall management tool. It provides a zone-based approach to network security, allowing you to define different trust levels for different network interfaces.
Basic Firewall Setup
Start by enabling firewalld and reviewing the current configuration:
# Enable and start firewalld
sudo systemctl enable --now firewalld
# Check the firewall state
sudo firewall-cmd --state
# List all active zones
sudo firewall-cmd --get-active-zones
# List all services in the public zone
sudo firewall-cmd --zone=public --list-all
Allowing Specific Services
Only open ports that are absolutely necessary. For example, if the server is a web server, allow HTTP and HTTPS traffic:
# Allow HTTP and HTTPS permanently
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Allow SSH (if not already allowed)
sudo firewall-cmd --permanent --add-service=ssh
# Reload the firewall to apply changes
sudo firewall-cmd --reload
# Verify the configuration
sudo firewall-cmd --zone=public --list-all
Restricting SSH to Specific IP Addresses
For additional security, restrict SSH access to specific IP addresses or ranges using rich rules:
# Remove the default SSH service
sudo firewall-cmd --permanent --remove-service=ssh
# Allow SSH only from a specific IP address
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="ssh" accept'
# Allow SSH from a specific subnet
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" service name="ssh" accept'
# Reload the firewall
sudo firewall-cmd --reload
4. SELinux Configuration
Security-Enhanced Linux (SELinux) is a mandatory access control system built into RHEL. It provides fine-grained control over what processes can access, significantly reducing the impact of security vulnerabilities.
Checking SELinux Status
Always verify that SELinux is enabled and running in enforcing mode:
# Check the current SELinux mode
getenforce
# Get detailed SELinux status
sestatus
# Check SELinux booleans
sudo semanage boolean -l
Setting SELinux to Enforcing Mode
If SELinux is disabled or in permissive mode, enable enforcing mode. Note that changing from disabled to enforcing requires a relabel and reboot:
# Edit the SELinux configuration
sudo vi /etc/selinux/config
# Set SELINUX to enforcing
SELINUX=enforcing
# If transitioning from disabled, relabel the filesystem
sudo touch /.autorelabel
sudo reboot
Troubleshooting SELinux Denials
When SELinux blocks an action, it logs an AVC denial. Use the following tools to identify and resolve issues:
# Install troubleshooting tools
sudo dnf install -y setroubleshoot-server setools-console
# View recent SELinux denials
sudo ausearch -m AVC,USER_AVC -ts recent
# Get human-readable explanations of denials
sudo sealert -a /var/log/audit/audit.log
# List all SELinux denials with details
sudo audit2why -a
Managing SELinux Contexts for Custom Paths
If you move web content to a non-standard directory, you must update the SELinux context:
# Change the context of a custom web directory
sudo semanage fcontext -a -t httpd_sys_content_t '/custom/web(/.*)?'
# Apply the new context
sudo restorecon -Rv /custom/web
# Verify the context
ls -Z /custom/web
5. Securing Network Services
Beyond the firewall, individual services must be configured securely. This section covers hardening common services found on RHEL servers.
Hardening the SSH Daemon
SSH is one of the most targeted services. Apply these additional hardening measures:
# Edit the SSH daemon configuration
sudo vi /etc/ssh/sshd_config
# Apply the following hardening settings
Port 2222 # Change the default port
Protocol 2 # Use only SSH protocol 2
MaxAuthTries 3 # Limit authentication attempts
LoginGraceTime 30 # Reduce login grace period
AllowUsers jane bob # Allow only specific users
X11Forwarding no # Disable X11 forwarding
ClientAliveInterval 300 # Set idle timeout
ClientAliveCountMax 0 # Disconnect after timeout
AllowTcpForwarding no # Disable TCP forwarding
PermitTunnel no # Disable tunneling
# Restart SSH to apply changes
sudo systemctl restart sshd
Securing the Web Server (Apache/httpd)
If running Apache, apply security headers and disable unnecessary modules:
# Install the headers module
sudo dnf install -y mod_ssl
# Edit the Apache configuration
sudo vi /etc/httpd/conf/httpd.conf
# Add security headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Hide Apache version information
ServerTokens Prod
ServerSignature Off
# Test the configuration and restart
sudo httpd -t
sudo systemctl restart httpd
Disabling Unused Services
Reduce the attack surface by disabling services that are not needed:
# List all enabled services
sudo systemctl list-unit-files --type=service --state=enabled
# Disable unnecessary services
sudo systemctl disable --now avahi-daemon
sudo systemctl disable --now cups
sudo systemctl disable --now bluetooth
# Mask services to prevent them from being started
sudo systemctl mask avahi-daemon
6. File System and Kernel Hardening
Kernel parameters and file system mount options provide additional layers of security. The sysctl interface allows you to tune kernel behavior at runtime.
Applying Kernel Security Parameters
Create a dedicated sysctl configuration file for security settings:
# Create a custom sysctl configuration
sudo vi /etc/sysctl.d/99-security.conf
# Add the following security parameters
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable IP forwarding (unless this is a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Ignore ICMP redirect messages
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Log suspicious packets (martians)
net.ipv4.conf.all.log_martians = 1
# Enable TCP SYN cookies
net.ipv4.tcp_syncookies = 1
# Disable core dumps for SUID programs
fs.suid_dumpable = 0
# Apply the changes
sudo sysctl --system
Securing /tmp with Mount Options
The /tmp directory is world-writable and a common target for attacks. Mount it with security-enhancing options:
# Add a tmpfs entry for /tmp in fstab
sudo vi /etc/fstab
# Add the following line
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec,mode=1777 0 0
# Remount /tmp
sudo mount -o remount /tmp
# Verify the mount options
mount | grep /tmp
Setting Secure Permissions on Key Files
Ensure critical system files have appropriate permissions:
# Restrict permissions on cron directories
sudo chmod 700 /etc/cron.d
sudo chmod 700 /etc/cron.daily
sudo chmod 700 /etc/cron.hourly
sudo chmod 700 /etc/cron.weekly
sudo chmod 700 /etc/cron.monthly
# Restrict permissions on SSH configuration
sudo chmod 600 /etc/ssh/sshd_config
sudo chmod 700 /etc/ssh
# Restrict access to the hosts file
sudo chmod 644 /etc/hosts
7. Auditing and Logging
Comprehensive logging and auditing are essential for detecting security incidents and conducting forensic investigations. RHEL includes the Linux Audit daemon (auditd) for system auditing.
Configuring the Audit Daemon
Install and configure auditd to monitor critical system events:
# Install auditd
sudo dnf install -y audit
# Enable and start the audit daemon
sudo systemctl enable --now auditd
# Edit the audit configuration
sudo vi /etc/audit/auditd.conf
# Set the following parameters
log_file = /var/log/audit/audit.log
max_log_file = 100
max_log_file_action = ROTATE
space_left = 75
space_left_action = EMAIL
action_mail_acct = root
admin_space_left = 50
admin_space_left_action = HALT
disk_full_action = HALT
# Restart auditd to apply changes
sudo service auditd restart
Adding Audit Rules
Create custom audit rules to monitor important files and activities:
# Create a custom rules file
sudo vi /etc/audit/rules.d/custom.rules
# Monitor changes to the passwd file
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/gshadow -p wa -k identity
# Monitor sudoers files
-w /etc/sudoers -p wa -k scope
-w /etc/sudoers.d/ -p wa -k scope
# Monitor SSH configuration changes
-w /etc/ssh/sshd_config -p wa -k ssh_config
# Monitor login and logout events
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
# Monitor system time changes
-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change
# Load the new rules
sudo augenrules --load
# Verify the rules are loaded
sudo auditctl -l
Reviewing Audit Logs
Use the ausearch and aureport tools to analyze audit logs:
# Search for identity-related events
sudo ausearch -k identity
# Generate a summary report of all audit events
sudo aureport --summary
# Generate a report of failed login attempts
sudo aureport --auth --failed
# Search for events within a specific time range
sudo ausearch --start today --end now -k ssh_config
Configuring Log Rotation and Remote Logging
For production environments, forward logs to a central logging server to prevent tampering:
# Install rsyslog (usually pre-installed)
sudo dnf install -y rsyslog
# Configure remote logging
sudo vi /etc/rsyslog.conf
# Add the following to forward all logs to a remote server
*.* @@logserver.example.com:6514
# Ensure TLS is used for secure transmission
# (requires certificate configuration - see rsyslog documentation)
# Restart rsyslog
sudo systemctl restart rsyslog
8. Intrusion Detection with AIDE
Advanced Intrusion Detection Environment (AIDE) is a file integrity checker that creates a database of file attributes and alerts you when changes occur. It is essential for detecting unauthorized modifications.
Installing and Initializing AIDE
# Install AIDE
sudo dnf install -y aide
# Initialize the AIDE database (this may take several minutes)
sudo aide --init
# Move the initialized database to the production location
sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
# Verify the database was created
ls -la /var/lib/aide/
Running Integrity Checks
Schedule regular integrity checks using cron:
# Create a cron job for daily AIDE checks
sudo crontab -e
# Add the following line to run a check every day at 2 AM
0 2 * * * /usr/sbin/aide --check | mail -s "AIDE Daily Report" root@localhost
# Run a manual check
sudo aide --check
Updating the AIDE Database After Legitimate Changes
After making legitimate system changes (such as package updates), update the AIDE database:
# Run a check and update the database
sudo aide --update
# Replace the old database with the updated one
sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
9. Using OpenSCAP for Compliance Scanning
OpenSCAP is a framework for compliance scanning and vulnerability assessment. RHEL includes SCAP Security Guide (SSG) profiles that map to various security standards.
Installing and Running a Scan
# Install OpenSCAP and the SCAP Security Guide
sudo dnf install -y openscap-scanner scap-security-guide
# List available profiles
oscap info /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# Run a scan against the PCI-DSS profile
sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_pci-dss \
--results scan-results.xml \
--report scan-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# View the HTML report
sudo cp scan-report.html /var/www/html/
Generating a Remediation Script
OpenSCAP can generate a bash script that automatically remediates failed checks:
# Generate a remediation script
sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_pci-dss \
--results scan-results.xml \
--remediate \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
Review the remediation script carefully before running it, as some changes may affect application functionality.
10. Best Practices and Ongoing Maintenance
Security is not a one-time task but an ongoing process. The following best practices will help you maintain a secure RHEL environment over time.
Regular Security Tasks
- Review and apply security updates at least weekly, or immediately for critical vulnerabilities
- Audit user accounts monthly and remove inactive accounts
- Review sudo logs and firewall rules quarterly
- Run AIDE integrity checks daily and review the reports
- Conduct OpenSCAP compliance scans quarterly
- Review SSH access logs for suspicious activity weekly
- Test backup and restore procedures regularly
Implementing Fail2Ban for Brute Force Protection
While not included by default, Fail2Ban provides additional protection against brute force attacks:
# Enable the EPEL repository
sudo dnf install -y epel-release
# Install Fail2Ban
sudo dnf install -y fail2ban
# Create a local configuration file
sudo vi /etc/fail2ban/jail.local
# Add the following configuration
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
banaction = firewallcmd-rich-rules[actiontype=]
[sshd]
enabled = true
port = 2222
logpath = %(sshd_log)s
# Enable and start Fail2Ban
sudo systemctl enable --now fail2ban
# Check the status
sudo fail2ban-client status
sudo fail2ban-client status sshd
Documenting Your Configuration
Maintain detailed documentation of all security configurations, including:
- Firewall rules and their business justification
- User accounts and their access levels
- SELinux custom policies and boolean changes
- Audit rules and log retention policies
- Change history for all security-related modifications
- Incident response procedures and contact information
Automating with Ansible
For managing multiple servers, use Ansible to automate security configurations. Here is a simple playbook example:
# secure-rhel.yml
---
- name: Secure RHEL Server
hosts: all
become: yes
tasks:
- name: Update all packages
dnf:
name: "*"
state: latest
- name: Ensure firewalld is enabled and running
systemd:
name: firewalld
enabled: yes
state: started
- name: Allow only SSH and HTTP/HTTPS through the firewall
ansible.posix.firewalld:
service: "{{ item }}"
permanent: yes
state: enabled
loop:
- ssh
- http
- https
notify: reload firewalld
- name: Disable root login over SSH
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
notify: restart sshd
- name: Ensure SELinux is in enforcing mode
selinux:
policy: targeted
state: enforcing
handlers:
- name: reload firewalld
systemd:
name: firewalld
state: reloaded
- name: restart sshd
systemd:
name: sshd
state: restarted
Run the playbook with:
ansible-playbook -i inventory secure-rhel.yml
Conclusion
Securing a RHEL server requires a multi-layered approach that addresses user access, network exposure, service configuration, kernel parameters, file integrity, and continuous monitoring. By following this practical checklist, you establish a strong security baseline that significantly reduces your server's attack surface. Remember that security is an ongoing process — regularly review your configurations, apply updates promptly, monitor audit logs, and adapt your defenses to emerging threats. Combining the built-in RHEL security tools like SELinux, firewalld, and auditd with additional measures such as AIDE, OpenSCAP, and Fail2Ban creates a defense-in-depth strategy that protects your infrastructure against both opportunistic and targeted attacks. Always test changes in a staging environment before applying them to production, and maintain thorough documentation to support both operational efficiency and compliance audits.