nginx - Redirect visitors based on IP-List

Amitz

New Member
Dear all,


does anyone have an idea how to achieve the following:


I want nginx to redirect all visitors with an IP listed in CSF's deny list to a certain page.


That deny list, for those unfamiliar with CSF, is just a text file with banned IPs, one per line.


I am aware that I can use CSF itself to present a custom error page in case of IP denial but that does not always work for me as it should and I would like to be more flexible.


Is there any nginx directive that could do the job?


Thank you very much in advance for your hints!


Cheers,


-A
 

Mun

Never Forget
Might be able to use one of the if statements in the nginx config, but it will be costly for processing power.
 

Amitz

New Member
Thanks, mun. The thing is: nginx should get the list of IPs from a file. I cannot fill them in by hand because they are changing dynamically. 

I would need something like this:

Code:
if ($in-ip-list) {
rewrite ^ http://domain.com/special-page.html;
 

Mun

Never Forget
Thanks, mun. The thing is: nginx should get the list of IPs from a file. I cannot fill them in by hand because they are changing dynamically. 

I would need something like this:


if ($in-ip-list) {
rewrite ^ http://domain.com/special-page.html;
Correct. but that is very very very very bad. That means that every request that hits the servers for said site will have to go through the if statement for every request. It would probably be better to write up a script to add deny and an end tag of ; instead.
 

Amitz

New Member
Correct. but that is very very very very bad. That means that every request that hits the servers for said site will have to go through the if statement for every request. It would probably be better to write up a script to add deny and an end tag of ; instead.
To be honest - I know that this is the worst possible solution. It was more meant like an illustration. I am hoping that there is some function included in nginx, I guess I will have to read through their extensive wiki. I really wonder why that built-in CSF function never works properly for me.

Maybe using the Geo Module somehow?

http://serverfault.com/questions/380642/nginx-how-to-redirect-users-with-certain-ip-to-special-page
 
Last edited by a moderator:

Mun

Never Forget
To be honest - I know that this is the worst possible solution. It was more meant like an illustration. I am hoping that there is some function included in nginx, I guess I will have to read through their extensive wiki. I really wonder why that built-in CSF function never works properly for me.
 

I think the deny section would be the best. You could even put it in its own config file. 

I saw some scripts laying around that will probably do it for you. 
 

Amitz

New Member
I feel incredible stupid right now... Of course. Damn! That drugs & Rock 'n Roll...


Could someone please kill me? Or better: DELETE this thread? ;)
 
Last edited by a moderator:

acd

New Member
Assuming CSF wasn't going to tank your incoming requests and you still wanted to solve this problem of ACLing based on a dynamic list of incoming IP addresses, I would set up a server block in nginx (vhost) that directs you to the desired (or perhaps for the client, undesired) page. Then, I would set up an iptables rule that checks for the IP and on match, uses -j REDIRECT to send the client to the unusual port. I would use ipset ( http://ipset.netfilter.org/ ) to store the list of ips to redirect.

Requirements:

  • ipset is not a kernel builtin for ovz, you need an HVM (Xen HVM, ESXi, KVM) to use custom kernel modules
  • Enough free time to figure out how to build ipset and load it up.
Assumptions:

  • The redirect IP file contains only single ip addresses (not CIDR 10.50.128.0/23 style address ranges), one per line.
  • addresses in the IP file are retained until the IP should be removed from the redirect list.
script - mkIpset.sh


#!/bin/sh

REDIRECTFILE=/var/lib/redirect.lst
IPSETNAME=redirectlist

if ipset -n --list redirectlist > /dev/null
then
  sort -u $REDIRECTFILE | awk 'BEGIN{print "-N '"$IPSETNAME"'-new iptreemap";} {print "-A '"$IPSETNAME"'-new $1";} END{print "COMMIT";}' | ipset -R
  ipset -W "$IPSETNAME" "$IPSETNAME"-new
  ipset -X "$IPSETNAME"-new
else
  sort -u $REDIRECTFILE | awk 'BEGIN{print "-N '"$IPSETNAME"' iptreemap";} {print "-A '"$IPSETNAME"' $1";} END{print "COMMIT";}' | ipset -R
fi
iptables updates:


#!/bin/sh

#change this to match your system
IN_IP=10.0.0.20
IN_IF=eth0
IN_PORT=80
RD_PORT=82
IPSETNAME=redirectlist

iptables -t nat -A PREROUTING -i $IN_IF -p tcp --dport $IN_PORT -d $IN_IP -m set --match-set $IPSETNAME -j REDIRECT --to-port $RD_PORT

script - redirect_new.sh:


#!/bin/sh
REDIRECTFILE=/var/lib/redirect.lst
IPSETNAME=redirectlist

if [ "$#" == "0" ]; then
  echo "Usage $0 ip [ip..ip]"
  exit 1
fi

while (( "$#" )); do
if expr match "$1" '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' > /dev/null
then
  echo "$1" >> "$REDIRECTFILE"
  ipset -A "$IPSETNAME" "$1"
else
  echo "Invalid IPv4:" $1
fi

shift;
done

script - unredirect.sh:


#!/bin/sh
REDIRECTFILE=/var/lib/redirect.lst
IPSETNAME=redirectlist

if [ "$#" == "0" ]; then
  echo "Usage $0 ip [ip..ip]"
  exit 1
fi

while (( "$#" )); do
if expr match "$1" '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' > /dev/null
then
  TMP=`mktemp`
  ipset -D "$IPSETNAME" "$1"
  grep -v "$1" "$REDIRECTFILE" > "$TMP"
  mv "$TMP" "$REDIRECTFILE"

else
  echo "Invalid IPv4:" $1
fi

shift;
done
nginx_vhost: #adjust to preference


server {
    listen 82 default_server;
    server_name localhost;
    root /var/www/redirectserver;
    location / {
        try_files $uri /blockpage.html;
    }
}
Ipset is pretty flexible. I use it with pg2ipset.c with bluetack level1 lists for *coughbtcough*, from which I yanked most of the code. I wrote this up as a thought experiment though, so you might need to tweak it so it actually works right. ^^; If you're dealing with ipv6s, you'll have to use ip6tables, and make appropriate script changes. It would take very little to modifiy this to pull down say, a list of TOR exit nodes, and dump them in an ipset w/ redirect.

I would do it this way because unless you sit down with the nginx module api and implement red/black trees in memory, you're going to lose a lot of performance with every connection doing the disk hit for the redirect-or-not lookup. Better if you stored it in say bdb, worse if you do a list scan (ip block list volume dependent). Generally speaking, the conntrack table is going to be big enough to handle all your redirects and ipset lookup is extremely fast.

best regards,

-tw

edit: I would prefer if you folks did *not* delete this thread.
 
Last edited by a moderator:
Top