• Get In Touch
June 27, 2016

How to Secure Nginx Using Fail2ban on Centos-7

Want your very own server? Get our 1GB memory, Xeon V4, 25GB SSD VPS for £10.00 / month.
Get a Cloud Server

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.

Want your very own server? Get our 1GB memory, Xeon V4, 25GB SSD VPS for £10.00 / month.
Get a Cloud Server

Share this Article!

Related Posts

Node.js Authentication – A Complete Guide with Passport and JWT

Node.js Authentication – A Complete Guide with Passport and JWT

Truth be told, it’s difficult for a web application that doesn’t have some kind of identification, even if you don’t see it as a security measure in and of itself. The Internet is a kind of lawless land, and even on free services like Google’s, authentication ensures that abuses will be avoided or at least […]

Node.js and MongoDB: How to Connect MongoDB With Node

Node.js and MongoDB: How to Connect MongoDB With Node

MongoDB is a document-oriented NoSQL database, which was born in 2007 in California as a service to be used within a larger project, but which soon became an independent and open-source product. It stores documents in JSON, a format based on JavaScript and simpler than XML, but still with good expressiveness. It is the dominant […]

Using MySQL with Node.js: A Complete Tutorial

Using MySQL with Node.js: A Complete Tutorial

Although data persistence is almost always a fundamental element of applications, Node.js has no native integration with databases. Everything is delegated to third-party libraries to be included manually, in addition to the standard APIs. Although MongoDB and other non-relational databases are the most common choice with Node because if you need to scale an application, […]

Node.Js Vs Django: Which Is the Best for Your Project

Node.Js Vs Django: Which Is the Best for Your Project

Django and NodeJs are two powerful technologies for web development, both have great functionality, versatile applications, and a great user interface. Both are open source and can be used for free. But which one fits your project best? NodeJs is based on JavaScript, while Django is written in Python. These are two equally popular technologies […]

Nodejs Vs PHP:  Which Works Best?

Nodejs Vs PHP: Which Works Best?

Before getting into the “battle” between Node.js and PHP we need to understand why the issue is still ongoing. It all started with the increased demand for smartphone applications, their success forcing developers to adapt to new back-end technologies that could handle a multitude of simultaneous requests. JavaScript has always been identified as a client-side […]