How to harden CentOS 7, Red Hat Enterprise Linux 7 & Amazon Linux for better security

A few years ago I wrote a quite popular post for security hardening on Ubuntu 14.04, and now here’s a new version for CentOS 7 and RHEL 7. Much of it should apply to CentOS/RHEL versions 6 and 8, with some tweaks required here and there. It should also largely work with Amazon Linux and Amazon Linux 2, although again some tweaks will be required for those.

Assume that all these operations need to be performed as root, which you can do by logging in as root with

sudo -i

(or you can issue an endless series of sudo commands if you prefer).

Update: I have now automated this process in the form of Ansible playbooks which you can use. The playbooks are in a repository on my GitHub, and I’ve written a new blog post with more details about them and how they work.

Harden SSH

I generally regard it as a sensible idea to disable root login over SSH, so in /etc/ssh/sshd_config you should change PermitRootLogin to no.

If SSH on your servers is open to the world then I also advise running SSH on a non-standard port in order to avoid incoming SSH hacking attempts. To do that, in /etc/ssh/sshd_config change Port from 22 to another port of your choice, e.g. 1022. Note that you’ll need to update your firewall or EC2 security rules accordingly.

After making changes to SSH, reload the OpenSSH server:

systemctl reload sshd

Improve IP security

Create the file /etc/sysctl.d/10-network-security.conf and add the following lines to improve IP security:

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Block SYN attacks
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5

# Log Martians
net.ipv4.conf.all.log_martians = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0 
net.ipv6.conf.default.accept_redirects = 0

# Ignore Directed pings
net.ipv4.icmp_echo_ignore_all = 1

Load the new rules:

sysctl --system

PHP hardening

If you’re using PHP, these are changes worth making in /etc/php.ini in order to improve the security of PHP:

  1. Add exec, system, shell_exec, and passthru to disable_functions.
  2. Change expose_php to Off.
  3. Ensure that display_errors, track_errors and html_errors are set to Off.

Apache hardening

If you’re using Apache web server, it’s worth making sure you have the following parameters set in the config (/etc/httpd/conf/httpd.conf by default) to make sure Apache is suitably hardened:

ServerTokens Prod
ServerSignature Off
TraceEnable Off
Header unset ETag
FileETag None

Then restart Apache:

systemctl restart httpd

Install and configure ModSecurity

If you’re using Apache, the web application firewall ModSecurity is a great way to harden your web server so that it’s much less vulnerable to probes and attacks. Firstly, install the necessary packages:

yum install mod_security

Install zip/unzip if you don’t already have them:

yum install zip unzip

Next, install the Open Web Application Security Project ModSecurity Core Rule Set:

cd /tmp
curl -L -O https://github.com/coreruleset/coreruleset/archive/refs/heads/v3.3/master.zip
unzip master.zip
mv coreruleset-3.3-master /etc/httpd/owasp_crs
mv /etc/httpd/owasp_crs/crs-setup.conf.example /etc/httpd/owasp_crs/crs-setup.conf

To add the rules to Apache, edit /etc/httpd/conf.d/mod_security.conf and add the following lines near the end, just before </IfModule>:

Include owasp_crs/crs-setup.conf
Include owasp_crs/rules/*.conf

Restart Apache to activate the new security rules:

systemctl restart httpd

Install and configure mod_evasive

If you’re using Apache then it’s a good idea to install mod_evasive to help protect against denial of service attacks. Firstly install the EPEL repository:

yum install epel-release

Then install mod_evasive:

yum install mod_evasive

Restart Apache to activate it:

systemctl restart httpd

Install and configure rootkit checkers

It’s highly desirable to get alerted if any rootkits are found on your server, so let’s install a rootkit checker:

yum install rkhunter

Let’s run rkhunter weekly instead of daily, because daily is too annoying:

mv /etc/cron.daily/rkhunter /etc/cron.weekly

Install Logwatch

Logwatch is a great tool which provides regular reports nicely summarising what’s been going on in the server logs. Install it like this:

yum install logwatch

Make it run weekly instead of daily, otherwise it gets too annoying:

mv /etc/cron.daily/0logwatch /etc/cron.weekly

Make it show output from the last week by editing the script /etc/cron.weekly/0logwatch and changing the line

$LOGWATCH_SCRIPT $OPTIONS

to the following:

$LOGWATCH_SCRIPT $OPTIONS --range 'between -7 days and -1 days'

Enable process accounting

Linux process accounting keeps track of all sorts of details about which commands have been run on the server, who ran them, when, etc. It’s a very sensible thing to enable on a server where security is a priority, so let’s install and activate it:

yum install psacct
systemctl enable psacct
systemctl start psacct

To show users’ connect times, run ac. To show information about commands previously run by users, run sa. To see the last commands run, run lastcomm. Those are a few commands to give you an idea of what’s possible; just read the manpages to get more details if you need to.

I threw together a quick Bash script to send a weekly email with a summary of user activity, login information and commands run. To get the same report yourself, create a file called /etc/cron.weekly/pacct-report containing the following (don’t forget to make this file executable) (you can grab this from GitHub if you prefer):

#!/bin/bash

users=$(cat /etc/passwd | awk -F ':' '{print $1}' | sort)

echo "USERS' CONNECT TIMES"

for user in $users ; do
  ac=$(ac -d $user)
  [ -n "$ac" ] && echo -e "\n${user}:\n\n${ac}"
done

echo ""
echo "COMMANDS BY USER"
echo ""

for user in $users ; do
  comm=$(lastcomm --user $user | awk '{print $1}' | sort | uniq -c | sort -nr)
  if [ "$comm" ] ; then
    echo "$user:"
    echo "$comm"
  fi
done

echo ""
echo "COMMANDS BY FREQUENCY OF EXECUTION"
echo ""

sa | awk '{print $1, $6}' | sort -n | head -n -1 | sort -nr

Things I haven’t covered

There are some additional issues you might want to consider which I haven’t covered here for various reasons:

  1. This guide assumes your server is on a network behind a firewall of some kind, whether that’s a hardware firewall of your own, EC2 security rules on Amazon Web Services, or whatever; and that the firewall is properly configured to only allow through the necessary traffic. However, if that’s not the case then you’ll need to install and configure a firewall on the server itself. The recommended software for this would probably be firewalld.
  2. Given the rise in hacking, nowadays I would advise that placing your SSH server on a non-standard port (as described above) may not be sufficient on its own. Seriously consider the use of firewall rules or Security Groups (on AWS) to lock down your SSH access to a minimal number of authorised IP addresses. If that’s not an option, look into the use of fail2ban as a serious hacking prevention tool. Always monitor your security logs on a regular basis in order to identify incoming hacking attempts.
  3. Once you’ve hardened your server, you’re advised to run some vulnerability scans and penetration tests against it in order to check that it’s actually as invincible as you’re now hoping it is. This is a topic which requires a post all of its own so I won’t be covering it in any detail here, but a good starting point if you’re not already familiar with it is the excellent Nmap security scanner.

If you need help with server security or any other infrastructure issues, check out my SysAdmin and DevOps services and feel free to get in touch.