Deploy Geo-Restriced Service with Nginx, GeoIP2, and Docker

by Sabbir Ahmed


Posted on: 2 years, 1 month ago


Country Restricted Service with Nginx and GeoIP (opensrouce image)
Country Restricted Service with Nginx and GeoIP (opensrouce image)

It is often a feature for a service to restrict content based on country to ensure compliance. From my personal experience, country restrictions can come in handy while under DDoS attacks. You can buy time for network and system engineers while they configure firewall and content filters. Another popular use case is we can forward the traffic to different upstreams based on geolocation with this solution. We can also use this for geofencing, local-marketing strategies, location-based user clustering... So let's get started!

Pre-requisites

To use this blog, you may need to have

  1. Basic level of understanding in docker and docker-compose
  2. Good understanding of NGINX, 3rd party module installation for NGINX, and Nginx configurations.

 


TL;DR

Git Repo

Create a MaxMind account and generate the license keys. Then create a geoupdate/.env file and update as following

GEOIPUPDATE_ACCOUNT_ID=<your-secret>
GEOIPUPDATE_LICENSE_KEY=<your-secret>
GEOIPUPDATE_EDITION_IDS=<your-secret>

clone the repo,


docker-compose up -d geoupdate
docker-compose up -d --build nginx

Then postgres it out with the following command

curl -X GET -H "Accept: application/json" http://<server-ip>

Then tweak the config/site.conf to block and unblock countries.

Please note that, this will not work with localhost. You need a real-ip to postgres it out.


Steps

  1. MaxMind Account and License Keys(Has free option)
  2. Install GeoIP module for Nginx
  3. Download and Configure the GeoIP database
  4. Nginx configuration

Installing GeoIP Module for Nginx

We have to install a third-party module to incorporate GeoIP-related tasks. We will do it in the easiest way possible, "the docker way"

The following is the first stage that will get the dependencies to build the Nginx GeoIP module. It will also help reduce image size and maintainability. 


ARG NGINX_VERSION=1.19.2

FROM nginx:$NGINX_VERSION

ARG NGINX_VERSION=1.19.2
ARG GEOIP2_VERSION=3.3

RUN mkdir -p /var/lib/GeoIP/

RUN apt-get update \
    && apt-get install -y \
        build-essential \
        libpcre++-dev \
        zlib1g-dev \
        libgeoip-dev \
        libmaxminddb-dev \
        wget \
        git

RUN cd /opt \
    && git clone --depth 1 -b $GEOIP2_VERSION --single-branch https://github.com/leev/ngx_http_geoip2_module.git \
    && wget -O - http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz | tar zxfv - \
    && mv /opt/nginx-$NGINX_VERSION /opt/nginx \
    && cd /opt/nginx \
    && ./configure --with-compat --add-dynamic-module=/opt/ngx_http_geoip2_module \
    && make modules 

The second stage will copy the dependencies and install maxmind library to interpret the mmdb files. 


FROM nginx:$NGINX_VERSION

COPY --from=0 /opt/nginx/objs/ngx_http_geoip2_module.so /usr/lib/nginx/modules

RUN apt-get update \
    && apt-get install -y --no-install-recommends --no-install-suggests libmaxminddb0 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && chmod -R 644 /usr/lib/nginx/modules/ngx_http_geoip2_module.so 

Finally, we are ready to config our server. The only notable part is at config/nginx.config file. On lines 30-33, you can configure the allowed or disallowed countries of your own, for example, I have blocked BD which is the ISO code for Bangladesh. (It's okay because I am from Bangladesh and so an obvious choice.)

map $geoip2_data_country_iso_code $allowed_country {
       default yes;
       BD no;
    }

Now, we can postgres out the full solution,

git clone https://github.com/by-sabbir/geoip2-nginx.git

Now, we have to address the chicken and egg problem. We can't get away without the geodatabases. So make sure you have run the geoupdate service first. Create a geoupdate/.env file and update as following

GEOIPUPDATE_ACCOUNT_ID=<your-secret>
GEOIPUPDATE_LICENSE_KEY=<your-secret>
GEOIPUPDATE_EDITION_IDS=<your-secret>

And run the following command
docker-compose up -d geoupdate

Then we are ready to run the main service

docker-compose up -d --build nginx

Check the logs to confirm nginx service is running well, (geoupdate service will exit after downloading the database)

docker-compose logs -f nginx

We can now postgres the solution,


curl -X GET -H "Accept: application/json" http://

You should get either one of the following messages back based on your configuration,

{
   "code" : "100",
   "message" : "Not Blocked"
}

or

{
   "code" : "444",
   "message" : "Blocked"
}

If so, congratulations! If not let me know in the Github issues, I am always checking. And please fill free to contribute. Cheers!