Register for the iXsystems Community to get an ad-free experience and exclusive discounts in our eBay Store.

NGINX - Make it more secure by adding filter by GeoIP

Zofoor

FreeNAS Experienced
Joined
Aug 16, 2016
Messages
208
Hi all!
This is my first how-to, and I'll explain you how did I added the filter by GeoIP for the nginx webserver. I use this to make my nextcloud installation more secure.
I hope that you'll enjoy that, and also that I didn't made too many grammar errors, as english is not my primary language!

I have installed nginx and nextcloud in a jail following this how-to:
https://forums.freenas.org/index.php?threads/how-to-owncloud-using-nginx-php-fpm-and-mysql.17786/

but I think that it can be applyed to every nginx installation, as long as you didn't installed it as a freenas plug-in.

STEP 1 - ACCESS TO YOUR JAIL
so, SSH to you freenas, and type
Code:
[root@freenas] /# jls
   JID  IP Address      Hostname                      Path
     7  -               nextcloud                     /mnt/main_volume/jails/nextcloud

STEP 2 - CHECK IF YOU ALREADY HAVE THE GeoIP nginx module
Now enter on your nextcloud machine, and check your nginx installation:
Code:
root@nextcloud:/ # nginx -V
nginx version: nginx/1.11.3
built by clang 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512
built with OpenSSL 1.0.2h  3 May 2016
TLS SNI support enabled
configure arguments: --prefix=/usr/local/etc/nginx --with-cc-opt='-I /usr/local/include' --with-ld-opt='-L /usr/local/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --sbin-path=/usr/local/sbin/nginx --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --user=www --group=www --modules-path=/usr/local/libexec/nginx --with-file-aio --with-ipv6 --http-client-body-temp-path=/var/tmp/nginx/client_body_temp --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp --http-proxy-temp-path=/var/tmp/nginx/proxy_temp --http-scgi-temp-path=/var/tmp/nginx/scgi_temp --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp --http-log-path=/var/log/nginx/access.log --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gzip_static_module --with-http_gunzip_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_stub_status_module --with-http_sub_module --with-pcre --with-http_v2_module --with-stream=dynamic --with-stream_ssl_module --with-threads --with-mail=dynamic --without-mail_imap_module --without-mail_pop3_module --without-mail_smtp_module --with-mail_ssl_module --with-http_ssl_module


You have to find a text like --with-http_geoip_module=dynamic
If you don't have that, then your nginx is missing the module you need. As I didn't find it on the pkg repository, I have installed again nginx.

STEP 3 - INSTALL THE GeoIP PACKAGE
First, let's install the GeoIP pkg package:
pkg install geoip

STEP 4 - DOWNLOAD nginx SOURCE CODES (only if you lake of the GeoIP module from the STEP 2)
You have to download the latest version. To do that I have used wget, that is missing by default. So, let's install it with
root@nextcloud: pkg install wget
Then, getting the url of the last version fron the nginx.org website, download it
wget http://nginx.org/download/nginx-1.11.3.tar.gz
uncompress it
tar -xzf nginx-1.11.3.tar.gz
and enter in the directory created
cd nginx-1.11.3

You have now to configure the source code and install it. I raccomand you to make a copy-paste of the "configure arguments:" you have obtained with the nginx -V command, adding at the end "--with-http_geoip_module=dynamic". In my case I would make:
./configure --prefix=/usr/local/etc/nginx --with-cc-opt='-I /usr/local/include' --with-ld-opt='-L /usr/local/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --sbin-path=/usr/local/sbin/nginx --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --user=www --group=www --modules-path=/usr/local/libexec/nginx --with-file-aio --with-ipv6 --http-client-body-temp-path=/var/tmp/nginx/client_body_temp --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp --http-proxy-temp-path=/var/tmp/nginx/proxy_temp --http-scgi-temp-path=/var/tmp/nginx/scgi_temp --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp --http-log-path=/var/log/nginx/access.log --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gzip_static_module --with-http_gunzip_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_stub_status_module --with-http_sub_module --with-pcre --with-http_v2_module --with-stream=dynamic --with-stream_ssl_module --with-threads --with-mail=dynamic --without-mail_imap_module --without-mail_pop3_module --without-mail_smtp_module --with-mail_ssl_module --with-http_ssl_module --with-http_geoip_module=dynamic

You should be able now to make the build
make
and install it
make install clean
you would be warned that some old files are going to be renamed. Allow that, so that if you have troubles you can come back by renaming these files to the original names.

STEP 5 - DOWNLOAD THE GeoIP DATABASES
Now we have to download the GeoIP database. Again, I didn't find it on the pkg directory, so I have downloaded it with wget:
Code:
cd /usr/local/share/GeoIP/

wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
wget http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz

gunzip GeoIP.dat.gz
gunzip GeoIPASNum.dat.gz
gunzip GeoLiteCity.dat.gz


Now we have almost done all.

STEP 6 - CHECK THAT GeoIP WORKS LOCALLY AND OBTAIN YOUR COUNTRY CODE
You can run this command to check that GeoIP is working, so that in case of troubles you know if the problem belongs to the geoip installation or to the nginx installation:configuration:
Code:
root@nextcloud:/ # /usr/local/bin/geoiplookup -f /usr/local/share/GeoIP/GeoLiteCity.dat 8.8.8.8
GeoIP City Edition, Rev 1: US, CA, California, Mountain View, 94035, 37.386002, -122.083801, 807, 650


As you can see, it has correctly found that the IP passed (that is the public google DNS server 1) is located on the US.


STEP 7 - CHANGES TO nginx.conf TO ADD FILTER BY GeoIP
Let's edit the nginx.conf file, in my case
vi /usr/local/etc/nginx/nginx.conf

You can add on the first line (changing the path based on the locations of the nginx modules if needed)
Code:
load_module /usr/local/libexec/nginx/ngx_http_geoip_module.so;


Then, edit as follow (remember to enter in "EDIT MODE" by pressing the "INS" key on your keyboard):
Code:
... cut ...
http {
    geoip_country /usr/local/share/GeoIP/GeoIP.dat;

    map $geoip_country_code $allow_visit {
      default no;
      DE yes;
      IT yes;
   }

   geo $lan {
      default no;
      192.168.1.0/24 yes;
   }

... cut ...

    server {
        if ($lan = yes) {
                set $allow_visit yes;
        }

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

       ... cut ...

you have to adjust these lines as you need, but they are pretty easy to understand.
"DE yes;" means that line means that connections from germany. IT refers to Italy. You need to change these lines as your needs.

The section about $lan tells the IP address that can allow to access to the nextcloud server from your local LAN.

When you have done, save the changes (press the "ESC" button, and then digit :w and press enter to save. Then :q to quit).

STEP 8 - RESTART THE nginx SERVICE AND CHECK THAT IT WORKS

Finally we can restart the service and check that it works correctly
root@nextcloud:/ # service nginx restart

If your IP is of a country that is not allowed the client will receive and HTTP 403 error (that means, access denied).

Also, please note that IP address changes, so you'll have sometimes to update the db manually


COMMON TROUBLES:
Code:
root@nextcloud:~/nginx-1.11.3 # service nginx start
Performing sanity check on nginx configuration:
nginx: [emerg] "geoip_country" directive is not allowed here in /usr/local/etc/nginx/nginx.conf:23
nginx: configuration file /usr/local/etc/nginx/nginx.conf test failed

this means that you have written the geoip_country directory on the wrong point of your nginx.conf file. Please find where the line with "server {" and add the lines down of it

Code:
root@nextcloud:/ # service nginx restart
Performing sanity check on nginx configuration:
nginx: [emerg] dlopen() "/usr/local/libexec/nginx/ngx_http_geoip_module.so" failed (Cannot open "/usr/local/libexec/nginx/ngx_http_geoip_module.so") in /usr/local/etc/nginx/nginx.conf:4
nginx: configuration file /usr/local/etc/nginx/nginx.conf test failed

This means that the module that enables nginx filter by geoip has not been found. The problem could be:
1. you have pointed the wrong path for the module. You can find the correct path using this command:
Code:
root@nextcloud:/ # find / -name ngx_http_geoip_module.so
/usr/local/libexec/nginx/ngx_http_geoip_module.so


2. you haven't installed nginx with that module. Please check the step 2 of the tutorial
 
Last edited by a moderator:

depasseg

FreeNAS Replicant
Joined
Sep 16, 2014
Messages
2,862
STEP 3 - INSTALL THE GeoIP PACKAGE
First, let's install the GeoIP pkg package:
pkg install gepip
That's a great writeup. Thanks! (one question: is the pkg name really "gepip"?)
 

Jailer

Not strong, but bad
Joined
Sep 12, 2014
Messages
4,290
Is there any advantage to compiling nginx vs installing it from ports and selecting the modules you want to install?
 

Zofoor

FreeNAS Experienced
Joined
Aug 16, 2016
Messages
208
Is there any advantage to compiling nginx vs installing it from ports and selecting the modules you want to install?
The main advantage is that it is more updated. In my case I needed a specific feature that doesn't exist in the port version:
Feature: the "ssl_certificate" and "ssl_certificate_key" directives can be specified multiple times to load certificates of different types (for example, RSA and ECDSA).
Anyway you can compile from ports if you feel that it's better for you :)
 
Joined
Nov 18, 2016
Messages
2
Thanks for the write up. I'm having an issue on another platform and wondering if you can offer some advice? I used the following:

https://gist.github.com/sumardi/5559803

to install nginx on EC2 with Amazon Linux and my nginx -V shows:

--with-http_geoip_module=dynamic -

but I cannot find the ngx_http_geoip_module.so anywhere on my system. I've tried to load the module and have configured nginx using: https://www.howtoforge.com/nginx-how-to-block-visitors-by-country-with-the-geoip-module-debian-ubuntu to use the installed geoip db, but can't get past -

[root@IP-10-0-0-116 nginx]# service nginx restart
nginx: [emerg] dlopen() "/usr/share/nginx/modules/ngx_http_geoip_module.so" failed (/usr/share/nginx/modules/ngx_http_geoip_module.so: cannot open shared object file: No such file or directory) in /etc/nginx/nginx.conf:5
nginx: configuration file /etc/nginx/nginx.conf test failed

I don't have /usr/share/nginx/modules/ but do have a /usr/lib64/nginx/modules/ (which is empty), so I didn't bother editing the load module path.


Thanks,
sm
 
Joined
Nov 18, 2016
Messages
2
I wasn't able to determine why there were no modules even though they showed up with nginx -V so I just went ahead a compiled the latest stable nginx using the arguments from nginx -V, along with a few others.

My php-fpm continued to work, I needed to install a few additioanal packages,

yum install -y pcre-devel zlib-devel openssl-devel gcc gcc-c++ make
yum install libxml2-devel libxslt-devel
yum install gd-devel
yum install perl-ExtUtils-Embed
yum install gperftools-devel

./configure --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=/var/run/nginx.pid --lock-path=/var/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' --with-ld-opt=' -Wl,-E'


Hopefully it's not too heavy a binary. I think Amazon Linux comes with nginx, perhaps that and using the earlier linked instructions caused some conflict, or even deleted modules? Not sure. It works now, I'm successfully blocking countries!

Thanks again,
sm
 
Joined
Jul 31, 2017
Messages
1
Wow, great article. This is the first one I've tried that worked. I tried two other ones from different sites that didn't work.

I kept everything pretty much the same except changed:

if ($allow_visit = no) {
return 403;

to:

if ($allow_visit = no) {
return 444;

444 returns nothing so less work for Nginx and no point in feeding the bots anything. :) Also, I had an additional module loaded in Nginx below. I'm not sure what this one does but its there.

load_module modules/ngx_stream_geoip_module.so;
 

pakka

Newbie
Joined
Jan 25, 2018
Messages
37
I installed nginx as pkg from port and am a little bit confused how to install an nginx-module now. Is it possible?
 
Top