IPs ban on Linux

Ban Hammer

Who needs a quick ban?

Today we had a bruteforce attack on our nginx server. Well cannot say he was anywhere near successful though, the guy did POST /wp-login.php several times per second and all he got as an answer was 404. Fat chance…

But still, he had our access logs growing far larger than they usually do. So I tried to ban him. Unfortunately nginx does not use TCP wrappers by default (you can use ngx_tcpwrappers although it will have to be rebuilt from source).

So I made a little script, called ban-hammer to temporarily ban IPs using IPTables. There is also a cron.daily script to unban IPs each day. The script requires rpnc, but it is easy to adapt without it.

These scripts add and remove the IPs into a special IPT chain (which you can configure in the script). So you also have to configure your firewall to jump to the two chains and load banned IPs on boot:

echo "Bans"

load_bans() {
  ban_table=$1
  ban_chain=$2
  iptables=$3

  $iptables -N $ban_chain

  while read ban
  do
    ip=$(echo "$ban" | cut -d'=' -f 1)
    $iptables -A $ban_chain -s "$ip" -j DROP
  done < "$ban_table"

  $iptables -A INPUT -j $ban_chain
}

load_bans /etc/firewall/ip4.ban IP4BAN iptables
load_bans /etc/firewall/ip6.ban IP6BAN ip6tables

Nginx home directories and PHP

I use nginx as my main HTTP server.  I want the users to be able to publish their own pages in a special directory (public_html) within their home directory. They should also be able to use PHP scripts if they want to. You can access the user webpage (that is the content of their public_html directory) with this URL: http://[server]/~[user]/. Here is a snippet of the configuration I use to do so:

index index.html index.xml index.php;

# PHP in home directory
location ~ ^/~(.+?)(/.*\.php)(.*)$ {
  alias /home/$1/public_html;

  try_files $2 =404;
  fastcgi_split_path_info ^(.+\.php)(.*)$;
  fastcgi_pass unix:/var/run/php5-fpm.sock;
  fastcgi_index index.php;
  fastcgi_intercept_errors on;
  include fastcgi_params;

  fastcgi_param SCRIPT_NAME /~$1$fastcgi_script_name;
}

# Home directories
location ~ ^/~(.+?)(/.*)?$ {
  alias /home/$1/public_html$2;
}

You can see here the two locations that match the user directories. The first one matches the PHP scripts and passes them to the FastCGI process manager. For more information, see PHPFcgiExample. Note that I use a UNIX instead of an INET socket. Why would you bother IP on localhost when you can use an UNIX socket? I also set the SCRIPT_NAME parameter to ensure that it is derived correctly from the user URL. This need to be fixed for pages that point to themselves. I also had to restart php5-fpm to ensure that the changes were taken into account.