GEO Location Access Control With NGINX Reverse Proxy

NGINX can be deployed as a reverse proxy in front of applications and can be used to control access to the application based on the GEO Location of the end user. NGINX can be used to restrict access to different parts of applications for specific countries or cities. NGINX uses GeoIP2 database from Maxmind to identify the end user location based on users IP address.

NGINX Plus repository has GeoIP2 module which can be installed as part of NGINX plus installation. NGINX Plus requires paid subscription. In order to use NGINX open source version, the GeoIP2 module needs to be compiled from source and added into the path of NGINX configuration.

The following guide outlines the deployment methodology and installation instructions for deploying NGINX (opensource) as reverse proxy along with GeoIP2 module. The solution comprises of NGINX, GeoIP2 module and Maxmind DB library.

Installation of NGINX Application

Add the NGINX repository for CentOS 7 in the path /etc/yum.repos.d/nginx.repo. Run yum update and install the nginx application. Enable nginx service to start on boot.

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1
$ sudo yum update
$ sudo yum install nginx

Adding GeoIP2 module of NGINX

Verify the nginx version installed via the repository.

$ nginx -v
nginx version: nginx/1.20.1

Download the same nginx source code version.

$ wget http://nginx.org/download/nginx-1.20.1.tar.gz

Before compiling the GeoIP2 module, the required libraries and development files need to be installed.

$ sudo yum install make gcc git openssl-devel pcre-devel \
libmaxminddb libmaxminddb-devel libxslt-devel gd-devel \
perl-ExtUtils-Embed gperftools-devel redhat-rpm-config

Clone the git repository for the GeoIP2 module and untar the nginx source code under the same directory path.

$ cd ~
$ git clone https://github.com/leev/ngx_http_geoip2_module.git
$ tar zxvf nginx-1.20.1.tar.gz

The NGINX code will be compiled with the same compile time options used for the installed nginx version via the repository. For getting the compile options “nginx -V” command will be used. In addition the GeoIP2 module will be compiled as a dynamic module.

$ nginx -V
nginx version: nginx/1.20.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.1.1g FIPS  21 Apr 2020
TLS SNI support enabled
configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-compat --with-debug --with-file-aio --with-google_perftools_module --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-threads --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --with-ld-opt='-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E'
$
$
$ cd nginx-1.20.1
$ ./configure --add-dynamic-module=../ngx_http_geoip2_module --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-compat --with-debug --with-file-aio --with-google_perftools_module --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-threads --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --with-ld-opt='-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E'
$
$ make

This will compile dynamic libraries for http and stream geoip2 modules under the objs directory. Copy these modules to nginx installation.

$ ls -l ~/nginx-1.19.9/objs/*.so
-rwxrwxr-x. 1 jahanzeb jahanzeb 136936 اپريل  9 10:26 objs/ngx_http_geoip2_module.so
-rwxrwxr-x. 1 jahanzeb jahanzeb 112544 اپريل  9 10:26 objs/ngx_stream_geoip2_module.so
$
$ sudo cp objs/*.so /usr/share/nginx/modules/
$ sudo chown root:root /usr/share/nginx/modules/*.so

Add the geoip2 modules to the nginx configuration file /etc/nginx/nginx.conf

user  nginx;
worker_processes  auto;

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

load_module modules/ngx_http_geoip2_module.so;
load_module modules/ngx_stream_geoip2_module.so;

.....
.....
.....

Configure the GeoIP variable mappings for City and Countries in the /etc/nginx/nginx.conf file.

.....
.....
.....
http {
.....
.....
.....
    geoip2 /etc/nginx/GeoIP2/GeoLite2-City.mmdb {
        $geoip2_data_city_name   city names en;
        $geoip2_data_postal_code postal code;
        $geoip2_data_latitude    location latitude;
        $geoip2_data_longitude   location longitude;
        $geoip2_data_state_name  subdivisions 0 names en;
        $geoip2_data_state_code  subdivisions 0 iso_code;
    }

    geoip2 /etc/nginx/GeoIP2/GeoLite2-Country.mmdb {
        $geoip2_data_continent_code   continent code;
        $geoip2_data_country_iso_code country iso_code;
    }
}

In the website configuration file configure the map which will allow or block the countries. The map will be called under the server configuration. In the file /etc/nginx/conf.d/www.website.com.conf

map $geoip2_data_country_iso_code $allowed_country {
        default yes;
        CA no;
        CB no;
        CC no;
}
.....
.....

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name  careers.nayatel.com;
.....
.....


        if ($allowed_country = no) {
            return 444;
        }
.....
.....
}

Maxmind GeoLite2 Database

Register for a free account and download the GeoLite2 database from Maxmind. Copy the mmdb file to the directory /etc/nginx/GeoIP2.

To check via command line the location of an IP address use the following command.

$ mmdblookup --file /etc/nginx/GeoIP2/GeoLite2-City.mmdb --ip 1.1.1.50
$ mmdblookup --file /etc/nginx/GeoIP2/GeoLite2-City.mmdb --ip 2.2.2.51 country iso_code
$ mmdblookup --file /etc/nginx/GeoIP2/GeoLite2-City.mmdb --ip 3.3.3.52 city names en

Download the geoipupdate package from https://github.com/maxmind/geoipupdate/releases and install it.

$ wget https://github.com/maxmind/geoipupdate/releases/download/v4.8.0/geoipupdate_4.8.0_linux_amd64.rpm
$ sudo yum localinstall geoipupdate_4.8.0_linux_amd64.rpm

Get the GeoIP configuration file from https://www.maxmind.com/en/accounts/current/license-key/GeoIP.conf.

Verify the configuratio options in the file /etc/GeoIP.conf

AccountID 123456
LicenseKey XXXXXXXXXXXXX

# Enter the edition IDs of the databases you would like to update.
# Multiple edition IDs are separated by spaces.
EditionIDs GeoLite2-Country GeoLite2-City


# The directory to store the database files. Defaults to /usr/share/GeoIP
DatabaseDirectory /etc/nginx/GeoIP2

Edit the cron file /etc/crontab to schedule weekly updates of the GeoIP database.

53 18 * * 3 root /usr/bin/geoipupdate

References

https://docs.nginx.com/
https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-by-geoip/
https://github.com/leev/ngx_http_geoip2_module
https://gist.github.com/kmjones1979/fcabb4731bbf85b9c50189e90d76b1c1
https://dev.maxmind.com/geoip/geoip2/geolite2/
https://github.com/maxmind/geoipupdate/releases

Leave a Reply