NGINX: IP Geolocation by Cloudflare and “nested” if conditions

By | 04/02/2022
 

Among other features provided by Cloudflare, it can add a special header with a country value, from where a visitor came.

As a Ukrainian, I’d like to ban all visitors from russia, but:

  1. redirect all visitors from russian IPs to another web domain – russki-voenny-korabl-idi-nahuy.com
  2. during this, I’d like to filter requests and leave requests from the Yandex search system unimpacted, so it will proceed to return RTFM blog in its search results for Russian auditory.

Cloudflare IP Geolocation and NGINX

The simplest part here is to configure the filter by the header from Cloudflare.

At first, enable the CF-IPCountry header.

Go to your Cloudflare Dashboard > Network, activate the option, it’s free even in its basic plan:

Then, in your virtualhost config for NGINX, in the server {} block use the proxy_set_header to create a variable $http_cf_ipcountry:

...
proxy_set_header CF-IPCountry $http_cf_ipcountry;
...

NGINX and “nested” if

And the second part is more interesting.

So, we need to check two conditions:

  1. is a visitor from russia
  2. is it a common visitor, or a Yandex search bot?

In NGINX we can not use really nested conditions with if, e.g:

...
    if ($country_allowed = no) {
        if ($http_user_agent !~* (YandexBot) ) {
            rewrite ^ https://russki-voenny-korabl-idi-nahuy.com break;
        }
    }
...

But what we can do here, is to use variables and dedicated if.

Let’s check.

Enable a VPN, and check where are you now, for example using the https://www.iplocation.net service:

GB – Great Britain.

In the nginx.conf you can also enable an extended log format for access_log to see more information, and check the values of the $http_cf_ipcountry and a new variable $visitor that we will add below:

...
    log_format  main_ext '$time_local client: $remote_addr fwd_for: $http_x_forwarded_for '
                         'status: $status user_agent: "$http_user_agent" '
                         'server: "$server_name" country: $http_cf_ipcountry visitor: $visitor';
...

Next, create conditions and run tests. After tests will be successful, we will change conditions to region RU and “if not equal” Yandex (see Yandex robots in server logs).

So:

  1. create a variable called $http_cf_ipcountry from the CF-IPCountry header;
  2. in the first ifcondition check the value of the $http_cf_ipcountry: for now, check for the GB and create a variable $visitor that will keep a value rus, if the region was == CA;
  3. in the second  if condition  check the User-Agent, and if it’s == Firefox then add another value to the $visitor variable – “redirect;
  4. in the third if condition check the value of the $visitor variable, and if it is equal to the rusredirect string, then make a rewrite to the russki-voenny-korabl-idi-nahuy.com domain;

Also, you can set the rewrite_log parameter to on, and set the error_log to the notice level, so you’ll be able to check if rewrites are working as expected.

This is how my config looked during the tests:

...
    rewrite_log on;
    access_log /var/log/nginx/rtfm.co.ua-access-ext.log main_ext;
    error_log /var/log/nginx/rtfm.co.ua-error.log notice;
...
    proxy_set_header CF-IPCountry $http_cf_ipcountry;
    
    if ($http_cf_ipcountry = GB) {
        set $visitor rus;
    }
    
    if ($http_user_agent ~* (Firefox) ) {
        set $visitor "${visitor}redirect";
    }
    
    if ($visitor = "rusredirect") {
        rewrite ^ https://russki-voenny-korabl-idi-nahuy.com break;
    }
...

Check the config file and reload nginx:

[email protected]:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[email protected]:~# systemctl reload nginx

Go to the blog with the Firefox browser:

access_log:

[email protected]:~# tail -f /var/log/nginx/rtfm.co.ua-access-ext.log | grep GB
02/Apr/2022:06:22:29 +0000 client: 162.158.91.190 fwd_for: 146.70.46.20,146.70.46.20 status: 302 user_agent: "Mozilla/5.0 (X11; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0" server: "rtfm.co.ua" country: GB visitor: rusredirect

error_log:

[email protected]:~# tail -f /var/log/nginx/rtfm.co.ua-error.log | grep 'rewritten redirect'
2022/04/02 06:22:29 [notice] 21018#21018: *33766595 rewritten redirect: "https://russki-voenny-korabl-idi-nahuy.com", client: 162.158.91.190, server: rtfm.co.ua, request: "GET / HTTP/1.1", host: "rtfm.co.ua"

Got a visitor from the country: GB with the user_agent: "Firefox", and got a  redirect: "https://russki-voenny-korabl-idi-nahuy.com" – “It works!” (c).

Now, change the conditions to the final version with the RU region and Yandex for User-Agent:

  • set the region: if ($http_cf_ipcountry = RU)
  • and change the ~* (Firefox) to the not-equal, i.e. !~* (Yandex)if ($http_user_agent !~* (Yandex) ):
...
    proxy_set_header CF-IPCountry $http_cf_ipcountry;
    
    if ($http_cf_ipcountry = RU) {
        set $visitor rus;
    }

    if ($http_user_agent !~* (Yandex) ) {
        set $visitor "${visitor}redirect";
    }

    if ($visitor = "rusredirect") {
        rewrite_log on;
        rewrite ^ https://russki-voenny-korabl-idi-nahuy.com break;
    }
...

Done.



Also published on Medium.