0

Background: I run several WordPress sites and I notice a huge amount of distributed brute force login attempts. fail2ban does not help me here because the attacker carefully attempts only up to 5 logins per IP and then switches to a different one.

My issue is not that I worry about passwords being guessed correctly. I only worry about the huge amount of load this causes on the server.

My idea is simple: I live in Switzerland and some (not all) sites will only ever be logged into from Switzerland, so I'd like to block login attempts from other countries on those sites. Sounds easy? Well nginx doesn't want to agree with me.

I have the following snippet set up as snippets/php.conf:

index index.html index.htm index.php;

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    fastcgi_read_timeout 300;
}

I include this snippet from most files in /etc/nginx/sites-enabled. I have enabled and configured the nginx geoip module and it seems to work fine (config not shown here). Many site configs look like this:

server {
    listen 80;
    server_name mydomain.com;

    location /  {
        # common WordPress config
        try_files $uri $uri/ /index.php;
    }

    location ^~ /wp-login.php {
        # CH is the country code for Switzerland
        if ($geoip_country_code != CH) {
                return 444;
        }
        include snippets/php.conf;    # <-- #1
    }

    root /var/www/mydomain.com;
    include snippets/php.conf;
}

I have read everywhere that nginx only ever matches one location block, because that's just how it works. So it can only either match the location block for wp-login.php or match the block for .php, but not both.

So if I remove the line marked with #1 then things look good: In Switzerland I can access wp-login.php and outside of Switzerland I cannot access it. Great! However when I can access it, it just downloads the wp-login.php file instead of executing it. That seems expected, since the location block for executing .php wasn't matched and therefore the fastcgi stuff doesn't work.

That makes me believe that I actually do need to add the line marked with #1. But if I do that, then suddenly I can access the wp-login.php from all IPs no matter which country they are from.

What am I doing wrong? I found tons of articles on how to set up PHP with nginx and also tons of articles on how to block certain countries. But none that does both.

EDIT:

Here's the content of snippets/fastcgi-php.conf in case it matters, I believe that one came from nginx's default installation on Debian Stretch:

# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;

# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;

# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;

fastcgi_index index.php;
include fastcgi.conf;
jlh
  • 171
  • 7
  • What you need to do is include the three lines inside the `location ~ \.php$` block at your #1 point. The nested `location` block is breaking the `if` logic. – Richard Smith Jul 24 '19 at 06:02
  • Well that did indeed help, thank you very much! Would you care to explain why my setup was breaking the if logic in an answer? – jlh Jul 26 '19 at 17:29
  • The outer `location` block is a separate context to the inner `location` block. Your `if` block was not inside the `location` block that processed the request. The inner `location` block was unnecessary, therefore removing it was the simplest solution. – Richard Smith Jul 26 '19 at 17:51

0 Answers0