Introduction to Securing CentOS Servers
CentOS (Community Enterprise Operating System) has long been a favorite among system administrators for building production servers. Whether you are running CentOS 7, CentOS 8, or one of its successors like AlmaLinux and Rocky Linux, the underlying RHEL-based architecture means the security principles remain largely the same. However, a fresh CentOS installation is not secure by default — it is configured for usability, not for production hardening. This tutorial walks you through a practical, actionable checklist to lock down a CentOS server from the moment you gain SSH access.
What Is Server Hardening?
Server hardening is the process of reducing a system's attack surface by removing unnecessary software, disabling unused services, enforcing strict access controls, applying patches, and configuring defensive tools such as firewalls and intrusion detection systems. The goal is not to make the server impenetrable — no system is — but to make it significantly harder and less attractive for attackers to compromise.
Why It Matters
Every minute, thousands of automated bots scan the internet for vulnerable servers. Default SSH ports, weak passwords, outdated packages, and open ports are low-hanging fruit that attackers exploit within hours of a server going online. A single compromised server can become part of a botnet, leak sensitive customer data, or serve as a pivot point into your broader infrastructure. Hardening your CentOS server is therefore not optional — it is the baseline of responsible operations.
1. Initial System Updates and Package Management
The very first action after provisioning a CentOS server should be updating all installed packages to their latest stable versions. This patches known CVEs and ensures you are running software with the latest security fixes.
# Update all packages
sudo yum update -y
# On CentOS 8 / RHEL 8+ based systems
sudo dnf update -y
# Enable automatic security updates (CentOS 8+)
sudo dnf install -y dnf-automatic
sudo systemctl enable --now dnf-automatic.timer
After updating, remove unnecessary packages that may introduce vulnerabilities:
# Remove unused packages and clean cache
sudo yum autoremove -y
sudo yum clean all
# List installed packages for audit
rpm -qa | sort > installed_packages.txt
Best Practices for Package Management
- Always test updates on a staging server before applying to production.
- Enable automatic security-only updates for critical infrastructure.
- Avoid installing compilers (gcc, make) on production servers unless absolutely required.
- Use signed repositories only and verify GPG keys.
2. Securing SSH Access
SSH is the primary entry point to your server, and it is also the most attacked service. Hardening SSH is one of the highest-impact changes you can make.
2.1 Disable Root Login and Password Authentication
Edit the SSH daemon configuration file at /etc/ssh/sshd_config:
# Disable direct root login
PermitRootLogin no
# Disable password authentication
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Limit authentication attempts
MaxAuthTries 3
# Disable X11 forwarding if not needed
X11Forwarding no
# Set a login grace period
LoginGraceTime 30
# Allow only specific users
AllowUsers deployuser
Before restarting SSH, ensure you have set up SSH key-based authentication:
# On your local machine, generate a key pair
ssh-keygen -t ed25519 -C "admin@yourdomain.com"
# Copy the public key to the server
ssh-copy-id deployuser@your-server-ip
# Restart SSH after verifying key login works
sudo systemctl restart sshd
2.2 Change the Default SSH Port
Changing the SSH port from 22 to a non-standard port reduces automated brute-force noise significantly. Edit /etc/ssh/sshd_config:
Port 2222
Update the firewall before restarting SSH:
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo systemctl restart sshd
2.3 Use Fail2Ban to Block Brute-Force Attacks
Fail2Ban monitors log files and bans IPs that show malicious signs. Install it via EPEL:
sudo yum install -y epel-release
sudo yum install -y fail2ban
# Create a local configuration
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo vi /etc/fail2ban/jail.local
Add a jail for SSH:
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/secure
maxretry = 3
bantime = 3600
findtime = 600
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
3. User Management and Privilege Escalation
Never run services or perform daily tasks as the root user. Create dedicated accounts with limited privileges and grant sudo access only where necessary.
# Create a new user with sudo privileges
sudo adduser deployuser
sudo passwd deployuser
# Add to the wheel group (sudoers)
sudo usermod -aG wheel deployuser
# Lock the root password (optional but recommended)
sudo passwd -l root
3.1 Restrict Sudo Access
Edit the sudoers file safely using visudo:
sudo visudo
Configure granular permissions:
# Require password for sudo
Defaults timestamp_timeout=5
# Allow deployuser to restart nginx only
deployuser ALL=(ALL) /bin/systemctl restart nginx
# Log all sudo commands
Defaults logfile="/var/log/sudo.log"
Defaults log_input, log_output
3.2 Enforce Strong Password Policies
Configure password quality with PAM by editing /etc/security/pwquality.conf:
minlen = 12
minclass = 4
maxrepeat = 3
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
Set password aging policies in /etc/login.defs:
PASS_MAX_DAYS 90
PASS_MIN_DAYS 7
PASS_MIN_LEN 12
PASS_WARN_AGE 7
4. Configuring the Firewall
CentOS uses firewalld as the default firewall management tool. A properly configured firewall is your first line of network defense.
# Verify firewalld is running
sudo systemctl status firewalld
# Enable and start firewalld
sudo systemctl enable --now firewalld
# List current rules
sudo firewall-cmd --list-all
4.1 Define Zones and Services
# Set default zone to public
sudo firewall-cmd --set-default-zone=public
# Allow only essential services
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Remove unwanted services
sudo firewall-cmd --permanent --remove-service=dhcpv6-client
# Reload to apply changes
sudo firewall-cmd --reload
4.2 Rate-Limit SSH Connections
Protect against brute-force attacks by limiting connection rates:
sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" limit value="3/m" accept'
sudo firewall-cmd --reload
Best Practices for Firewall Configuration
- Follow the principle of least privilege: only open ports that are actively needed.
- Use separate zones for internal and external interfaces.
- Regularly audit open ports with
ss -tulpn. - Document every firewall rule change in your infrastructure documentation.
5. SELinux Configuration
SELinux (Security-Enhanced Linux) is a mandatory access control system built into CentOS. Many administrators disable it because it can be difficult to troubleshoot, but disabling SELinux removes a critical layer of protection.
# Check SELinux status
sudo sestatus
# Check current mode
getenforce
# Set to enforcing mode permanently
sudo vi /etc/selinux/config
Set the configuration:
SELINUX=enforcing
SELINUXTYPE=targeted
# Apply changes (requires reboot)
sudo reboot
# Temporarily set mode without reboot
sudo setenforce 1
5.1 Troubleshooting SELinux Denials
# Install troubleshooting tools
sudo yum install -y setroubleshoot setools
# View SELinux denials
sudo sealert -a /var/log/audit/audit.log
# List SELinux contexts of files
ls -Z /var/www/html
# Restore default contexts
sudo restorecon -Rv /var/www/html
If a service requires a specific policy, use audit2allow to generate a custom module rather than disabling SELinux entirely:
sudo grep httpd /var/log/audit/audit.log | audit2allow -M myhttpdpolicy
sudo semodule -i myhttpdpolicy.pp
6. Securing Network Parameters with Sysctl
The Linux kernel exposes many network parameters that can be tuned to mitigate common attacks such as SYN floods, IP spoofing, and man-in-the-middle attacks.
Create a custom sysctl configuration file:
sudo vi /etc/sysctl.d/99-security.conf
Add the following hardening parameters:
# Ignore ICMP echo requests (ping)
net.ipv4.icmp_echo_ignore_all = 1
# Enable SYN cookies
net.ipv4.tcp_syncookies = 1
# Disable IP forwarding
net.ipv4.ip_forward = 0
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable reverse path filtering
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Log martian packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Disable IPv6 if not used
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# Apply the new settings
sudo sysctl --system
# Verify changes
sudo sysctl -a | grep syncookies
7. Disabling Unused Services
Every running service is a potential attack vector. Audit your system and disable anything that is not required for your workload.
# List all enabled services
sudo systemctl list-unit-files --type=service --state=enabled
# Disable common unnecessary services
sudo systemctl disable --now avahi-daemon
sudo systemctl disable --now cups
sudo systemctl disable --now bluetooth
sudo systemctl disable --now nfs-server
sudo systemctl disable --now rpcbind
# List all listening ports
sudo ss -tulpn
Review the output of ss -tulpn carefully. If you see a service listening on a port you do not recognize, investigate it immediately and disable it if it is not needed.
8. File System and Kernel Hardening
8.1 Secure /tmp and /var/tmp
The /tmp directory is world-writable and a common target for attackers to store scripts. Mount it with noexec, nosuid, and nodev options:
# Create a tmpfs mount for /tmp
sudo vi /etc/fstab
Add the following line:
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
tmpfs /var/tmp tmpfs defaults,noexec,nosuid,nodev 0 0
sudo mount -o remount /tmp
sudo mount -o remount /var/tmp
8.2 Set Secure Permissions on Critical Files
# Restrict access to 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 SSH configuration
sudo chmod 600 /etc/ssh/sshd_config
# Restrict access to host files
sudo chattr +i /etc/passwd
sudo chattr +i /etc/shadow
sudo chattr +i /etc/group
sudo chattr +i /etc/gshadow
Note: Setting the immutable flag with chattr +i prevents even root from modifying these files without first removing the flag. This can break user management tools, so test carefully in your environment. To remove the flag, use chattr -i.
9. Intrusion Detection with AIDE
AIDE (Advanced Intrusion Detection Environment) creates a database of file checksums and permissions, then alerts you when files change unexpectedly. This helps detect unauthorized modifications.
sudo yum install -y aide
# Initialize the AIDE database
sudo aide --init
# Move the database to the production location
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
# Run a manual check
sudo aide --check
Schedule regular AIDE checks via cron:
sudo crontab -e
0 3 * * * /usr/sbin/aide --check | mail -s "AIDE Report for $(hostname)" admin@yourdomain.com
10. Logging and Auditing
Centralized and tamper-resistant logging is essential for incident response. CentOS uses journald and rsyslog for logging, and auditd for security auditing.
10.1 Configure Auditd
sudo yum install -y audit
sudo systemctl enable --now auditd
Edit /etc/audit/auditd.conf to configure log retention and behavior:
max_log_file = 100
max_log_file_action = ROTATE
space_left = 50
space_left_action = EMAIL
admin_space_left = 20
admin_space_left_action = HALT
Add audit rules to monitor critical files:
sudo vi /etc/audit/rules.d/audit.rules
# Monitor changes to /etc/passwd
-w /etc/passwd -p wa -k identity
# Monitor changes to /etc/shadow
-w /etc/shadow -p wa -k identity
# Monitor sudoers changes
-w /etc/sudoers -p wa -k scope
# Monitor SSH configuration
-w /etc/ssh/sshd_config -p wa -k ssh_config
# Monitor login events
-w /var/log/lastlog -p wa -k logins
sudo systemctl restart auditd
sudo auditctl -l
10.2 Forward Logs to a Remote Server
Configure rsyslog to forward logs to a central logging server:
sudo vi /etc/rsyslog.conf
*.* @@logserver.yourdomain.com:514
sudo systemctl restart rsyslog
11. Time Synchronization
Accurate system time is critical for log correlation, certificate validation, and cron job execution. Configure chrony for time synchronization:
sudo yum install -y chrony
sudo systemctl enable --now chronyd
# Verify synchronization
chronyc sources
chronyc tracking
12. Additional Hardening Measures
12.1 Install and Configure ClamAV
For servers that handle file uploads or email, an antivirus scanner adds another layer of defense:
sudo yum install -y clamav clamav-update
# Update virus database
sudo freshclam
# Run a scan
sudo clamscan -r /home
# Schedule daily scans
echo "0 2 * * * /usr/bin/clamscan -r /home --quiet -l /var/log/clamav/daily.log" | sudo crontab -
12.2 Enable Process Accounting
sudo yum install -y psacct
sudo systemctl enable --now psacct
# View command history of users
sudo lastcomm
12.3 Secure the Bootloader
Set a GRUB password to prevent unauthorized boot parameter changes:
sudo grub2-setpassword
13. Creating a Hardening Script
To make this checklist repeatable across multiple servers, encapsulate the key steps into a bash script. Below is a starting point that you can extend:
#!/bin/bash
# centos-hardening.sh - Basic CentOS server hardening script
set -e
echo "=== Updating system packages ==="
yum update -y
yum autoremove -y
echo "=== Configuring SSH ==="
sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#MaxAuthTries.*/MaxAuthTries 3/' /etc/ssh/sshd_config
systemctl restart sshd
echo "=== Configuring firewall ==="
systemctl enable --now firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
echo "=== Applying sysctl hardening ==="
cat > /etc/sysctl.d/99-security.conf << 'EOF'
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.icmp_echo_ignore_all = 1
EOF
sysctl --system
echo "=== Enabling SELinux ==="
sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
echo "=== Installing and enabling fail2ban ==="
yum install -y epel-release
yum install -y fail2ban
systemctl enable --now fail2ban
echo "=== Hardening complete. Reboot required for SELinux changes. ==="
Make the script executable and run it:
chmod +x centos-hardening.sh
sudo ./centos-hardening.sh
Best Practices Summary
- Principle of least privilege: Grant only the minimum access required for each user and service.
- Defense in depth: Layer multiple controls — firewall, SELinux, fail2ban, AIDE — so that a single failure does not compromise the entire system.
- Keep everything updated: Apply security patches promptly and enable automatic updates where feasible.
- Monitor continuously: Configure auditd, log forwarding, and AIDE checks so you detect incidents early.
- Document everything: Maintain records of all configuration changes, firewall rules, and user access grants.
- Test before deploying: Always validate hardening changes on a staging server to avoid breaking production services.
- Regularly review access: Audit user accounts, SSH keys, and sudo permissions on a monthly basis.
- Back up configurations: Keep version-controlled backups of critical configuration files so you can recover quickly.
Conclusion
Securing a CentOS server is not a one-time task but an ongoing discipline. The checklist above covers the foundational hardening steps — from system updates and SSH lockdown to firewall configuration, SELinux enforcement, kernel tuning, intrusion detection, and auditing. By systematically applying each control and continuously monitoring your systems, you dramatically reduce the risk of compromise and build a server environment that can withstand the relentless automated attacks that define today's internet. Remember that security is a process, not a destination: revisit this checklist regularly, stay informed about new vulnerabilities, and adapt your defenses as your infrastructure evolves.