So… you’ve just setup a shiny new server and you want to take measures to keep the bad guys out? Well, here I will give you a few tips on how to do just that.
This guide was written with CentOS 7.1 in mind but other up-to-date variants such as Fedora and RHEL should be pretty similar if not the same.
Hardening SSH (Secure Shell)
Most of you will be using this protocol as a means to remotely administrate your Linux server and your right to. Using SSH is by far the best method to administer your server due to its use of encrypted communications unlike it’s older cousins rlogin and telnet which provide no secure methods of communication.
Create a standard user
Use the ‘useradd’ command to add a username of your choice.
useradd YOURUSER
Set a password for your newly created user.
passwd YOURUSER
Add your user to the WHEEL group to enable that user to use the sudo command.
usermod -aG wheel YOURUSER
Create an authentication key
This method of authenticating with your server is much more secure that using a standard password, part of this process will require you to create the key on your local machine which you will be connecting from.
You will be asked if you would like to protect the key with a password, I advise you to do this but it’s not mandatory.
Creating the key on Mac/Linux
ssh-keygen -b 4096
Press Enter to use the default names id_rsa and id_rsa.pub in /home/your_username/.ssh before entering your passphrase.
Upload your public key to your server
For Linux
ssh-copy-id YOURUSER@YOURSERVER
For Mac
On your server do.
sudo mkdir -p ~/.ssh && sudo chmod -R 700 ~/.ssh
From your Mac do the following making sure to substitute ‘youruser’ and ‘yourserver’.
scp ~/.ssh/id_rsa.pub YOURUSER@YOURSERVER.0:~/.ssh/authorized_keys
Now on to the configuration changes.
Open up the SSH config file for editing
In this section we will be performing the following actions
- Disallowing root logins
- Setting allowed users
- Changing the default port
- Disabling password authentication
- Force protocol 2
You can replace nano with your favourite text editor such as vi.
sudo nano /etc/ssh/sshd_config
Disallow root logins.
Find the line that says
#PermitRootLogin yes
and change it to
PermitRootLogin no
Setting your user as an allowed user.
Add the following line to the bottom of your sshd_config file substituting ‘YOURUSER’ with your newly created account.
AllowUsers YOURUSER
Change the default service port.
Find the line that says
Port 22
Change to something other than 22 such as 22000
Port 22000
Disabling password authentication
We can disable password authentication because we will now be using our newly created key pair to authenticate to the server.
Look for the line that has
#PasswordAuthentication yes
and replace it with the below line.
PasswordAuthentication no
Only use SSH protocol 2
SSH Protocol 1 is generally considered obsolete as it’s vulnerable and old so lets go ahead and only use SSH Protocol 2. Protocol 2 should be enforced by default but it’s worth checking.
Look for the line that says.
#Protocol 2
Uncomment the line so it looks like this.
Protocol 2
There are many more options that we could set but this should be suffice in securing your SSH Service.
Save the file and restart the SSH service by doing the following.
sudo systemctl reload sshd.service
You should now be able to login on your chosen port with your authorised keys by connecting like this.
ssh YOURUSER@YOURSERVER -p 22000
Fail2ban
Fail2ban is a handy tool/service that monitors system log files to detect potential intrusion attempts and places bans using a variety of methods.
To install on CentOS we need to enable the EPEL repository by doing the following.
sudo yum install epel-release
Once the installation has completed we need to then go ahead and install Fail2ban
sudo yum install fail2ban fail2ban-systemd
Fail to ban comes with a wealth of options that would deserve a post all to itself so in this instance we will create a basic configuration file that will help secure your server, especially the SSH service.
Using SELinux? Then you will want to update your policy by doing the following.
yum update -y selinux-policy*
Configuration
We will be configuring fail2ban for use with Firewalld as it is implemented by default in CentOS 7.
Create a sshd.local file ready for editing.
sudo nano /etc/fail2ban/jail.d/sshd.local
Add the following lines.
[sshd]
enabled = true
port = 22000
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400
Save the file and go ahead and start fail2ban.
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
You should now have a working fail2ban installation which will automatically ban IP addresses after 3 failed attempts at logging in to your system via SSH.
Apache Hardening
The default Apache configuration just works but there’s a few tweaks we can do here and there that makes the bad guys job a little harder. One of the things we can do is try and prevent information leakage.
By default Apache gives out server version information on error pages. We can prevent this by adding a couple of lines to our httpd.conf file.
Version banner
Open up the httpd config file ready for editing.
sudo nano /etc/httpd/conf/httpd.conf
add the following lines to the bottom of the file
ServerTokens Prod
ServerSignature Off
Cookies
Trace Requests
To protect yourself from Cross Site Tracing attacks append the following line to the end of your configuration file.
TraceEnable off
Set the HttpOnly and Secure flag
To mitigate against most of the common Cross Site Scripting (XSS) attacks you can set the following directive, again add the following line at the end of your configuration file.
Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure
X-Frame Options
Adding this option to your configuration file will indicate whether or not a browser should be allowed to open a webpage in a frame or iframe. This will prevent site content embedded into other sites. See – https://www.owasp.org/index.php/Clickjacking.
Append the following line in your configuration file.
Header always append X-Frame-Options SAMEORIGIN
XSS Protection Again
To ensure and enforce web browser Cross Site Scripting protection append the following to your configuration file.
Header set X-XSS-Protection “1; mode=block”
Now with those options set we need to restart the Apache daemon.
sudo systemctl reload httpd.service
Logwatch
Now to keep tabs on all of those logs, Logwatch is a great tool to monitor your servers logs and email the administrator a digest on a daily basis.
Installation
sudo yum install logwatch sendmail
Now start sendmail.
sudo systemctl start sendmail
Configuration
The default configuration file for Logwatch is located at the below path.
/usr/share/logwatch/default.conf/logwatch.conf.
This file contains information on which directories for Logwatch to track, how the digest is sent and where the digest is sent to.
By default Logwatch keeps track of everything in /var/log but if you have other log files that you wish to add you can do this by adding the below to your logwatch.conf under the heading ‘Default Log Directory’.
LogDir = /some/path/to/your/logs
Email your daily digest
let’s go ahead and edit the logwatch.conf file.
sudo nano /usr/share/logwatch/default.conf/logwatch.conf
We need to change add your email into the configuration file so that the digest gets delivered to your inbox.
Look for the following section.
# Default person to mail reports to. Can be a local account or a
# complete email address. Variable Output should be set to mail, or
# --output mail should be passed on command line to enable mail feature.
MailTo = root
change ‘root’ to your own personal email address or wherever you want the digest sending to.
Adding Logwatch to Cron
Open up the crontab.
crontab -e
Now add the following line to the end of the file. This line will make logwatch run at midnight each day.
00 00 * * * /usr/sbin/logwatch
This guide was a little quick and dirty so you have any additions to this guide I would love to hear them, also if you think something is wrong or could have been done more efficiently please get in contact.
Ther is an error in your fail2ban ssh setup. You are telling it to block the standard ssh port when there are to many failures, but you are not running ssh on the standard ssh port.
Hey Frank, thanks for that will change accordingly. Good spot!
Hi Thanks for reaching out. It looks as if when choosing the service SSH it will work the same as putting the port in due to settings in sshd_config. I tested this, even though my ssh service was on a non default port.
Really good tutorial, I’m using this now.
Hi John, Thanks for the feedback. Great to see it’s coming in handy for yourself.
Where I come to change the Port number, there’s a note saying:
# If you want to change the port on a SELinux system, you have to tell
# SELinux about this change.
# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER
If this is necessary, maybe it’s worth popping it in the tutorial?
Strange, This must be an SELinux change that has happened. I will double check and add it into the guide. Thanks for pointing that out.
One more thing, making the port number adjustment results in Job for sshd.service invalid. when trying to restart the service. Any ideas?
Have you made the change to the config file before issuing the SELinux command?
Ended up asking my server support to do it – no idea what they did.
Still loving the tutorial.
Header set X-XSS-Protection “1; mode=block”
Uses curly quotes! Please change them to “”s.
Also, there’s a CIS Linux server benchmark for people interested in taking their security to the next level.
Hey John, not noticed that before. As you can see from your reply it changes normal quotes to curly ones. Must be a wordpress theme thing. I will check it out.
Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure
Breaks Drupal logins on http (presumably WordPress logins too, btw.
I’d suggest adding the following Apache headers too since they became a standard nowadays:
X-Content-Type-Options
Content-Security-Policy
Strict-Transport-Security
Also, if you change the default SSH port, you need to tell SELinux about it (I take it that you have it set to the enforcing mode since you are hardening the server) as it won’t work otherwise.
James – I have additional hardening steps for sshd (both as client and server). The steps limit the available protocol negotiations to only those that are NIST/HIPAA approved. In the real world, I’ve found this to foil current scan bots, which attempt to negotiate with a weaker set of protocols. I will pass these things and onto you via email, if you want.
Hey Lew, That’d be great thanks!
Hey Lew, That’d be great thanks!