Block visitors from certain countries using NGINX

NGINX, at its core, is a collection of modules. It contains several in built modules as well as hundreds of third party modules, contributed by developers from all over the world. With the addition of dynamic module support, modules have given us greater flexibility in adding functionalities to NGINX. In this chapter, we will be working with one such third party module called GeoIP to either allow or block traffic from certain countries. Let's start our chapter by installing NGINX in our system.

To install NGINX web server, click on the following link:

https://www.nodexplained.com/deploy-web-applications-with-nginx-web-server/

Now that the NGINX web server is successfully installed, check the version of it.

   
   	 nginx -v
   

For successful installation, it should give following output:

   
   	 nginx version: nginx/1.22.0
   

To either allow or block network traffic from only certain countries, we need to handle the traffic based on geo locations. For Geo-location based filtering, we have a third party module called ngx_http_geoip_module. This module is not built by default, we need to enable it by dynamically loading it in runtime.

To start the geo-location based filtering process, we need to first download the nginx source files of version 1.22.0, as shown in above version check section. Go to the following url to see the list of available download options:

http://nginx.org/download/

Navigate to the home directory and then download nginx source files:

   
	 cd $HOME

   	 wget http://nginx.org/download/nginx-1.22.0.tar.gz
   

To extract a tar.gz file, we can issue the following command:

   
   	 tar -xvf nginx-1.22.0.tar.gz
   

Navigate to the nginx source folder directory:

   
   	 cd nginx-1.22.0/
   
NGINX source files

Next, we need to download the third party module called ngx_http_geoip2_module. As of writing this article, the latest version for ngx_http_geoip2_module is 3.4

To check for latest releases, go to the following url:

https://github.com/leev/ngx_http_geoip2_module/releases

From home directory, download the ngx_http_geoip2_module and then extract it.

   
   	 cd $HOME

   	 wget https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/3.4.tar.gz

   	 tar -xvf 3.4.tar.gz
   

Remove the tar.gz file after successful extraction:

   
   	 rm -rf 3.4.tar.gz
   
NGINX GeoIP2 moduleNGINX GeoIP2 module

We need to install some software dependencies before moving ahead:

   
   	 sudo yum install gcc gcc-c++ make pcre-devel zlib-devel openssl-devel kernel-devel automake libtool libmaxminddb-devel  -y
   

Next, we need to install libmaxminddb library, as it is one of the primary dependency of GeoIP2 module. The libmaxminddb library provides a C library for reading MaxMind DB files, including the GeoIP2 databases from MaxMind. It helps to facilitate the fast lookups of IP addresses and also allows for greater flexibility with the type of data associated with an address.

From home directory, download the library, extract it and then install it using following commands:

   
   	cd $HOME
   
   	wget https://github.com/maxmind/libmaxminddb/releases/download/1.6.0/libmaxminddb-1.6.0.tar.gz

	tar -xvf libmaxminddb-1.6.0.tar.gz

	cd libmaxminddb-1.6.0/
    
    ./configure
	make
	make check
	sudo make install
	sudo ldconfig
   

Now that, all of our pre-requisite requirements are fulfilled, we can start the compilation of NGINX with the GeoIP2 module. Issue following command to start the compilation process:

   
	 cd $HOME/nginx-1.22.0/
	
   	 ./configure --with-compat --add-dynamic-module=$HOME/ngx_http_geoip2_module-3.4/
   
NGINX compiling with GeoIP2 moduleNGINX compiling with GeoIP2 module

After successful compilation of NGINX with GeoIP2 module, we need to run the following command to build it:

   
   	make modules
   

Once the build is successful, a couple of .so files, also known as module library files  are generated inside of objs directory. Copy the necessary module library files to the /etc/nginx/modules directory:

   
   	sudo cp objs/ngx_http_geoip2_module.so /etc/nginx/modules/
   

Now, we need to load the module library files into NGINX using  load_module directive.

Note:

The load_module directive is only allowed in the main configuration context. It's better to put it right after pid directive or at the very top of main nginx configuration file, before any block directives. Else, we can face following issue:

   
   nginx: [emerg] "load_module" directive is specified too late in /etc/nginx/nginx.conf
   


Modify the nginx.conf file and load the module using following commands:

   
   	 sudo vi /etc/nginx/nginx.conf
   
   
   	load_module modules/ngx_http_geoip2_module.so;
   

Now that, we have loaded the GeoIP2 module into nginx, we also need to download the latest geo location databases supported by the GeoIP2 module. Maxmind provides both free and paid version of geo location databases. Free one is called GeoLite2 IP and paid version is called GeoIP2. GeoLite2 databases provide free geolocation for IP addresses. However, the data is less accurate than the one GeoIP2 provides. As per Maxmind, paid version product can identify users at a country level with 99.8% accuracy.

Check following url for paid databases:

https://www.maxmind.com/en/geoip2-databases

We will be using free one. To use free services, we need to create an account with Maxmind. Go to the following url for signup.

https://www.maxmind.com/en/geolite2/signup

Sign up for maxmind to get geoip2 database

Once Continue button is clicked with all the mandatory data submission, check your email for the password reset link from maxmind. Reset the password using that email link and then login using the credentials.

dashboard for maxmind to get geoip2 database

Click on the Download Databases link at the right bottom as shown in above image.

download  maxmind geoip2 database

Click on Download GZIP button to download the country database file in your local machine. You can also download and use database files for city as well. To copy the country database file from our local machine to the remote server, we can use scp command. SCP (secure copy) is a command-line tool used to securely copy files and directories between two locations/servers. It runs on port 22 by default.

   
   	scp GeoLite2-Country_20220805/GeoLite2-Country.mmdb ec2-user@35.166.76.5:/home/ec2-user 

   

Replace 35.166.76.5 in above scp command with the IP address of your server. Also, replace ec2-user with the username of your system.

GeoLite2-Country.mmdb file

We will use a separate configuration file for either allowing or blocking the users from some specific countries.

   
   	vi /etc/nginx/geo_ip_location.conf
   

If you are getting permission denied issue, run the following command:  

   
   	sudo chown ec2-user:ec2-user -R /etc/nginx
   

Block visitors from certain countries


Suppose, we are building a product that is of no relevance to the customers from certain countries. In that scenario, we can just block the network traffic entirely from those countries. Other reasons for blocking a network traffic might be due to a lot of suspicious traffic trying to hack into the systems.

Using the GeoLite2-Country.mmdb database file from Maxmind, which is located at the home directory of a machine, we can map the client IP address to a country code using a variable called geoip2_data_country_iso_code. After that, we can define rules to either allow or block network traffic from certain countries using that variable and assign the result of that rules check in yes/no format to a variable called allowed_country.

In the code below, we allow network traffic from all the countries except from Nepal (NP) - (country where i live).

We can block network traffic from multiple countries. To get the country code, click on the following url:

https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes


Paste the following contents into the geo_ip_location.conf file:

   
   	geoip2 /home/ec2-user/GeoLite2-Country.mmdb {
        $geoip2_data_country_iso_code country iso_code;
	}

	map $geoip2_data_country_iso_code $allowed_country {
	   default yes;
	   NP no; # Nepal
	}
   

Include the geo_ip_location.conf file before including site configuration files in the main configuration file. The final version of nginx.conf file looks like below:

   
   	user  nginx;
	worker_processes  auto;

	error_log  /var/log/nginx/error.log notice;
	pid        /var/run/nginx.pid;

	load_module modules/ngx_http_geoip2_module.so;

	events {
	    worker_connections  1024;
	}

	http {
	    include       /etc/nginx/mime.types;
	    default_type  application/octet-stream;

	    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
	                      '$status $body_bytes_sent "$http_referer" '
	                      '"$http_user_agent" "$http_x_forwarded_for"';

	    access_log  /var/log/nginx/access.log  main;

	    sendfile        on;
	    keepalive_timeout  65;
	    include /etc/nginx/geo_ip_location.conf;
	    include /etc/nginx/conf.d/*.conf;
	}
   

We have defined a variable called allowed_country in geo_ip_location.conf file which indicates whether a client IP is mapped to a list of blocked countries or not. We can use the same variable to check if the request is allowed or not using if clause as follows:

   
   	if ($allowed_country = no) {
       return 403;
   }
   

403 HTTP status code -  Forbidden - server understands the request but refuses to authorize it.

We need to put above code in default.conf file. The final version of default.conf file in conf.d directory looks like below:

   
   	server {
	    listen       80;
	    server_name  localhost;
	    access_log  /var/log/nginx/host.access.log  main;

	    if ($allowed_country = no) {
	       return 403;
	   }

	    location / {
	        root   /usr/share/nginx/html;
	        index  index.html index.htm;
	    }
	    error_page   500 502 503 504  /50x.html;
	    location = /50x.html {
	        root   /usr/share/nginx/html;
	    }
	}
   

To check the syntax of NGINX configuration, issue the following command:

   
   	sudo nginx -t
   

Restart the nginx server, so that all the above changes are reflected immediately:

   
   	 sudo systemctl restart nginx
   

We have blocked network traffic from Nepal and as shown in image below, we get 403 when we try to access the web server.

NGINX geo fencing deny

For any country other than Nepal, it is allowed as shown in below image:

NGINX geo fencing allow

Allow visitors from certain countries


Suppose, we want to allow traffic from only certain countries - say Nepal and Japan and block traffic from all the other countries. It can be done in following way:

   
   	geoip2 /home/ec2-user/GeoLite2-Country.mmdb {
        $geoip2_data_country_iso_code country iso_code;
	}

	map $geoip2_data_country_iso_code $allowed_country {
	   default no;
	   NP yes; # Nepal
	   JP yes; # Japan
	}
   

That's it for this chapter.