Introduction
NGINX is the world’s most popular open source web server and load balancer for high-traffic sites, powering over 140 million properties, so it is important to protect your website and users from brute-force attacks. Fail2ban is an intrusion prevention software framework that protects computer servers from brute-force attacks which is written in Python programming language. It is able to run on POSIX systems that have an interface to a packet-control system or firewall installed locally, for example, iptables or TCP Wrapper.
Fail2ban scans log files (e.g. /var/log/apache/error_log) and bans IPs that show the malicious signs — too many password failures, seeking for exploits, etc. Generally Fail2Ban is then used to update firewall rules to reject the IP addresses for a specified amount of time, although any arbitrary other action (e.g. sending an email) could also be configured. Fail2Ban is able to reduce the rate of incorrect authentications attempts however; it cannot eliminate the risk that weak authentication presents. Configure services to use only two factor or public/private authentication mechanisms if you really want to protect services.Fail2ban blocks the source address using the regular linux firewall like iptables. You can configure fail2ban to send alert emails to you on each block. You can specify the amount of time you want the malicious source address to be kept blocked. You can also specify a list of source addresses in fail2ban configuration to be ignored.
Features
- Monitoring of log files.
- Intrusion prevention.
- Preventing dictionary attack.
- Protection from DoS Attacks.
- Update Netfilter/iptables or PF firewall rules.
- Protect against a distributed brute-force attack.
In this tutorial, we will learn how to install Fail2ban and configure it to secure your Nginx server from DDoS attacks on CentOS-7
Requirements
- A server running CentOS-7.
- A static IP Address for your server.
- A non-root user account with sudo privilege set up on your server.
Getting Started
Let’s start by making sure that your CentOS server is fully up to date.
You can update your server by running the following command:
sudo yum update -y
Install Nginx
By default Nginx is not available in the Centos-7 repository. You will need to install EPEL repository in order to install Nginx.
You can install the EPEL repository by running the following command:
sudo yum install epel-release
Now that the Nginx repository is installed on your server, you can install Nginx using the following yum command:
sudo yum install nginx
Nginx does not start on its own. To get Nginx running, type:
sudo systemctl start nginx
If you are running a firewall, run the following commands to allow HTTP and HTTPS traffic:
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
Before continuing, you will probably want to enable Nginx to start when your system boots. To do so, enter the following command:
sudo systemctl enable nginx
Now check on your machine that nginx web server is running or not by typing the URL http://localhost or http://server-ip-address in your browser.
You will see following image:
HP_NO_IMG/data/uploads/users/fd35bf73-10f3-43bf-b753-4edc26228307/617237432.png” alt=”” />
The above page is used to test the proper operation of the nginx HTTP server after it has been installed. If you can read this page, it means that the web server installed at this site is working properly.
#Configure Nginx Password Authentication
To start out, you need to create the file that will hold your username and password combinations. You can do this by using the OpenSSL utilities that may already be available on your server. If you have OpenSSL installed on your server, you can create a password file with no additional packages. We will create a hidden file called .htpasswd
in the /etc/nginx configuration directory to store our username and password combinations.
You can add a username to the file using this command. We are using hitesh as our username, but you can use whatever name you’d like:
sudo sh -c "echo -n 'hitesh:' > /etc/nginx/.htpasswd"
Next, add an encrypted password entry for the username by typing:
sudo sh -c "openssl passwd -apr1 >> /etc/nginx/.htpasswd"
You can repeat this process for additional usernames. You can see how the usernames and encrypted passwords are stored within the file by typing:
cat /etc/nginx/.htpasswd
You should see the following output:
hitesh:$apr1$WE2SfA57$O6kstuLHgfZ8SJNBd0YxE0
Now that you have a file with your users and passwords in a format that Nginx can read, you need to configure Nginx to check this file before serving our protected content. Begin by opening up the server block configuration file that you wish to add a restriction to.
sudo nano /etc/nginx/nginx.conf
Inside, with the comments stripped, the file should look similar to this:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
To set up authentication, you will need to decide on the context to restrict. Among other choices, Nginx allows you to set restrictions on the server level or inside a specific location. In our example, we’ll restrict the entire document root with a location block, but you can modify this listing to only target a specific directory within the web space. Within this location block, use the auth_basic
directive to turn on authentication and to choose a realm name to be displayed to the user when prompting for credentials. We will use the auth_basic_user_file
directive to point Nginx to the password file we created:
sudo nano /etc/nginx/nginx.conf
Add / Edit the following lines:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
auth_basic "Restricted Content";
auth_basic_user_file /etc/nginx/.htpasswd;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
Save and close the file when you are finished. Restart Nginx to implement your password policy:
sudo systemctl restart nginx
The directory you specified should now be password protected.
To confirm that your content is protected, try to access your restricted content in a web browser by typing the URL http://server-ip-address. You should be presented with a username and password prompt that looks like this:
HP_NO_IMG/data/uploads/users/fd35bf73-10f3-43bf-b753-4edc26228307/2114392051.png” alt=”” />
If you enter the correct credentials, you will be allowed to access the content. If you enter the wrong credentials or hit “Cancel”, you will see the “Authorization Required” error page:
HP_NO_IMG/data/uploads/users/fd35bf73-10f3-43bf-b753-4edc26228307/1552205808.png” alt=”” />
You should now have everything you need to set up basic authentication for your site. Keep in mind that password protection should be combined with SSL encryption so that your credentials are not sent to the server in plain text.
Install Fail2ban
Once your Nginx server is running and password authentication is enabled, you can go ahead and install Fail2ban. Fail2ban is an intrusion prevention framework, which works together with a packet-control system or firewall installed on your server. It is commonly used to block connection attempts after a number of failed tries. It operates by monitoring log files for certain type of entries and runs predetermined actions based on its findings. Install the program using the command below.
sudo yum install fail2ban fail2ban-systemd
By default, fail2ban is configured to only ban failed SSH login attempts. You will need to enable some rules that will configure it to check our Nginx logs for patterns that indicate malicious activity. Once installed, copy the default jail.conf file to make a local configuration with this command
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Then open the new local configuration file for edit with your favourite text editor, for example
sudo nano /etc/fail2ban/jail.local
Add / Edit the file as shown below:
[DEFAULT]
# MISCELLANEOUS OPTIONS
# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not
# ban a host which matches an address in this list. Several addresses can be
# defined using space separator.
ignoreip = 127.0.0.1/8 server-ip-address
# External command that will take an tagged arguments to ignore, e.g. ,
# and return true if the IP is to be ignored. False otherwise.
# ignorecommand = /path/to/command
ignorecommand =
# "bantime" is the number of seconds that a host is banned.
bantime = 600
# A host is banned if it has generated "maxretry" during the last "findtime" seconds.
findtime = 600
# "maxretry" is the number of failures before a host get banned.
maxretry = 5
Save and close the file.
- Ignoreip is used to set the list of IPs which will not be banned. The list of IP addresses should be given with a space separator. This parameter is used to set your personal IP address (if you access the server from a fixed IP).
- The Bantime parameter is used to set the duration of seconds for which a host needs to be banned.
- Findtime is the parameter which is used to check if a host must be banned or not. When the host generates maxrety in its last findtime, it is banned.
- Maxretry is the parameter used to set the limit for the number of retry’s by a host, upon exceeding this limit, the host is banned.
Execute the following lines of command to run the protective Fail2Ban software on the server.
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Configuring Fail2Ban to Monitor Nginx Logs
Now that you have some of the general Fail2ban settings in place, you can concentrate on adding some Nginx-specific jails that will monitor your web server logs for specific behavior patterns. Each jail within the configuration file is marked by a header containing the jail name in square brackets (every section but the [DEFAULT] section indicates a specific jail’s configuration).
sudo nano /etc/fail2ban/jail.local
Add the following content:
#To enable log monitoring for Nginx login attempts.
[nginx-auth]
enabled = true
filter = nginx-auth
action = iptables-multiport[name=NoAuthFailures, port="http,https"]
logpath = /var/log/nginx*/*error*.log
bantime = 600 # 10 minutes
maxretry = 6
[nginx-login]
enabled = true
filter = nginx-login
action = iptables-multiport[name=NoLoginFailures, port="http,https"]
logpath = /var/log/nginx*/*access*.log
bantime = 600 # 10 minutes
maxretry = 6
#This is the only Nginx-specific jail included with CentOS 7 fail2ban package. However, you can create our own jails to add additional functionality.
#You can create an [nginx-noscript] jail to ban clients that are searching for scripts on the website to execute and exploit. If you do not use PHP #or any other language in conjunction with your web server, you can add this jail to ban those who request these types of resources:
#to ban clients that are searching for scripts on the website to execute and exploit.
[nginx-noscript]
enabled = true
action = iptables-multiport[name=NoScript, port="http,https"]
filter = nginx-noscript
logpath = /var/log/nginx*/*access*.log
maxretry = 6
bantime = 86400 # 1 day
#You can add a section called [nginx-badbots] to stop some known malicious bot request patterns:
# to stop some known malicious bot request patterns
[nginx-badbots]
enabled = true
filter = apache-badbots
action = iptables-multiport[name=BadBots, port="http,https"]
logpath = /var/log/nginx*/*access*.log
bantime = 86400 # 1 day
maxretry = 1
#If you do not use Nginx to provide access to web content within users' home directories, you can ban users who request these resources by adding an #[nginx-nohome] jail:
#to provide access to web content within users' home directories
[nginx-nohome]
enabled = true
port = http,https
filter = nginx-nohome
logpath = /var/log/nginx/access.log
maxretry = 2
#We should ban clients attempting to use our Nginx server as an open proxy. We can add an [nginx-noproxy] jail to match these requests:
#ban clients attempting to use our Nginx server as an open proxy
[nginx-proxy]
enabled = true
action = iptables-multiport[name=NoProxy, port="http,https"]
filter = nginx-proxy
logpath = /var/log/nginx*/*access*.log
maxretry = 0
bantime = 86400 # 1 day
When you are finished making the modifications you need, save and close the file. You now have to add the filters for the jails that we have created.
Adding the Filters for Additional Nginx Jails
You will need to updated the /etc/fail2ban/jail.local file with some additional jail specifications to match and ban a larger range of bad behavior. You need to create the filter files for the jails we’ve created. These filter files will specify the patterns to look for within the Nginx logs.Begin by changing to the filters directory:
cd /etc/fail2ban/filter.d
We actually want to start by adjusting the pre-supplied Nginx authentication filter to match an additional failed login log pattern. You can do this by creating nginx-auth.conf
file:
sudo nano nginx-auth.conf
Add the following content:
# Auth filter /etc/fail2ban/filter.d/nginx-auth.conf:
# Blocks IPs that fail to authenticate using basic authentication
[Definition]
failregex = no user/password was provided for basic authentication.*client:
user .* was not found in.*client:
user .* password mismatch.*client:
ignoreregex =
Save and close the file when you are finished.
Next, create another file:
sudo nano nginx-proxy.conf
Add the following content:
# Proxy filter /etc/fail2ban/filter.d/nginx-proxy.conf:
# Block IPs trying to use server as proxy.
# Matches e.g.
# 192.168.1.1 - - "GET http://www.something.com/
#
[Definition]
failregex = ^ -.*GET http.*
ignoreregex =
Save and close the file when finished.
Next, create the filter for the [nginx-noscript] jail:
sudo nano nginx-noscript.conf
Add the following content:
# Noscript filter /etc/fail2ban/filter.d/nginx-noscript.conf:
# Block IPs trying to execute scripts such as .php, .pl, .exe and other funny scripts.
# Matches e.g.
# 192.168.1.1 - - "GET /something.php
[Definition]
failregex = ^ -.*GET.*(.php|.asp|.exe|.pl|.cgi|scgi)
ignoreregex =
Save and close the file when you are finished.
Next, create the filter for the [nginx-noscript] jail:
sudo nano nginx-login.conf
Add the following content:
# Login filter /etc/fail2ban/filter.d/nginx-login.conf:
# Blocks IPs that fail to authenticate using web application's log in page
# Scan access log for HTTP 200 + POST /sessions => failed log in
[Definition]
failregex = ^ -.*POST /sessions HTTP/1.." 200
ignoreregex =
Save and close the file when you are finished.
To implement your configuration changes, you’ll need to restart the fail2ban service. You can do that by typing:
sudo service fail2ban restart
The service should restart, implementing the different banning policies you’ve configured.
You can see all of your enabled jails by using the fail2ban-client
command:
sudo fail2ban-client status
You should see a list of all of the jails you enabled:
Status
|- Number of jail: 5
`- Jail list: nginx-badbots, nginx-login, nginx-proxy, nginx-auth, nginx-noscript, nginx-nohome
You should test your fail2ban rules with the fail2ban-regex
command:
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-auth.conf
You should see the following output:
Running tests
=============
Use regex file : /etc/fail2ban/filter.d/nginx-auth.conf
Use log file : /var/log/nginx/access.log
Results
=======
Failregex: 0 total
Ignoreregex: 0 total
Summary
=======
Sorry, no match
Look at the above section 'Running tests' which could contain important
information.
You can look at iptables to see that fail2ban has modified your firewall rules to create a framework for banning clients. Even with no previous firewall rules, you would now have a framework enabled that allows fail2ban to selectively ban clients by adding them to purpose-built chains:
sudo iptables –S
You should see the following output:
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N FORWARD_IN_ZONES
-N FORWARD_IN_ZONES_SOURCE
-N FORWARD_OUT_ZONES
-N FORWARD_OUT_ZONES_SOURCE
-N FORWARD_direct
-N FWDI_public
-N FWDI_public_allow
-N FWDI_public_deny
-N FWDI_public_log
-N FWDO_public
-N FWDO_public_allow
-N FWDO_public_deny
-N FWDO_public_log
-N INPUT_ZONES
-N INPUT_ZONES_SOURCE
-N INPUT_direct
-N IN_public
-N IN_public_allow
-N IN_public_deny
-N IN_public_log
-N OUTPUT_direct
-N fail2ban-BadBots
-N fail2ban-NoAuthFailures
-N fail2ban-NoLoginFailures
-N fail2ban-NoProxy
-N fail2ban-NoScript
-A INPUT -p tcp -m multiport --dports 80,443 -j fail2ban-NoProxy
-A INPUT -p tcp -m multiport --dports 80,443 -j fail2ban-NoScript
-A INPUT -p tcp -m multiport --dports 80,443 -j fail2ban-BadBots
-A INPUT -p tcp -m multiport --dports 80,443 -j fail2ban-NoLoginFailures
-A INPUT -p tcp -m multiport --dports 80,443 -j fail2ban-NoAuthFailures
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j INPUT_direct
-A INPUT -j INPUT_ZONES_SOURCE
-A INPUT -j INPUT_ZONES
-A INPUT -p icmp -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i lo -j ACCEPT
-A FORWARD -j FORWARD_direct
-A FORWARD -j FORWARD_IN_ZONES_SOURCE
-A FORWARD -j FORWARD_IN_ZONES
-A FORWARD -j FORWARD_OUT_ZONES_SOURCE
-A FORWARD -j FORWARD_OUT_ZONES
-A FORWARD -p icmp -j ACCEPT
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
-A OUTPUT -j OUTPUT_direct
-A FORWARD_IN_ZONES -i eth0 -g FWDI_public
-A FORWARD_IN_ZONES -g FWDI_public
-A FORWARD_OUT_ZONES -o eth0 -g FWDO_public
-A FORWARD_OUT_ZONES -g FWDO_public
-A FWDI_public -j FWDI_public_log
-A FWDI_public -j FWDI_public_deny
-A FWDI_public -j FWDI_public_allow
-A FWDO_public -j FWDO_public_log
-A FWDO_public -j FWDO_public_deny
-A FWDO_public -j FWDO_public_allow
-A INPUT_ZONES -i eth0 -g IN_public
-A INPUT_ZONES -g IN_public
-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public_allow -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
-A IN_public_allow -p tcp -m tcp --dport 8080 -m conntrack --ctstate NEW -j ACCEPT
-A fail2ban-BadBots -j RETURN
-A fail2ban-NoAuthFailures -j RETURN
-A fail2ban-NoLoginFailures -j RETURN
-A fail2ban-NoProxy -j RETURN
-A fail2ban-NoScript -j RETURN
Testing Fail2Ban Policies
It is important to test your Fail2ban policies to ensure they block traffic as expected. For instance, from the client machine open your web browser and type the URL http://server-ip-address, whe Nginx authentication prompt, give incorrect credentials a number of times. After you have surpassed the limit, you should be banned and unable to access the site.
On server machine, If you look at the status with the fail2ban-client command, you will see your IP address being banned from the site:
sudo fail2ban-client status nginx-auth
Output:
Status for the jail: nginx-auth
|- filter
| |- File list: /var/log/nginx/error.log
| |- Currently failed: 5
| `- Total failed: 0
`- action
|- Currently banned: 1
| `- IP list:
`- Total banned: 1
`- Banned IP list: 192.168.0.13
When you are satisfied that your rules are working, you can manually un-ban your IP address with the fail2ban-client
command:
sudo fail2ban-client set nginx-auth unbanip 192.168.0.13
You should now be able to attempt authentication again.
Conclusion
In this tutorial, you learned how to install nginx and fail2ban in CentOS-7. Fail2ban provides a great deal of flexibility to construct policies that will suit your specific security needs. You can look at the variables and patterns within the /etc/fail2ban/jail.local file, and the files it depends on within the /etc/fail2ban/filter.d and /etc/fail2ban/action.d directories, you can make more secure environment for your website. By using fail2ban, you can stop security attacks on your web server and prevent this type of attack in automated manner.