# nginx - Redirect visitors based on IP-List



## Amitz (May 17, 2013)

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 (May 17, 2013)

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


----------



## Mun (May 17, 2013)

http://www.cyberciti.biz/faq/unix-linux-nginx-custom-error-403-page-configuration/

NM use the deny command.

Schema as follows

http {

...

     deny 1.2.3.4;

     deny 250.250.250.0/24;

error_page 403 /403.html;

     location = /403.html {

     root html;

     allow all;

    }

...

}

Some side notes, root should probably be replaced with alias.


----------



## Amitz (May 17, 2013)

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;
```


----------



## Mun (May 17, 2013)

Amitz said:


> 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:
> 
> ...


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 (May 17, 2013)

Mun said:


> 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


----------



## Mun (May 17, 2013)

Amitz said:


> 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 (May 17, 2013)

Mun said:


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



Grateful for any link! ;-)


----------



## Mun (May 17, 2013)

http://bash.cyberciti.biz/web-server/nginx-shell-script-to-block-spamhaus-lasso-drop-spam-ip-address/

might need some patching.


----------



## Amitz (May 17, 2013)

Looks promising! Thanks!


----------



## George_Fusioned (May 17, 2013)

You do understand that since the IP is in the csf.deny file, the request won't even get to nginx, right?


----------



## Amitz (May 17, 2013)

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


Could someone please kill me? Or better: DELETE this thread?


----------



## Mun (May 17, 2013)

Amitz said:


> I feel incredible stupid right now... Of course. Damn! That drugs & Rock 'n Roll...
> 
> 
> Could someone please kill me? Or better: DELETE this thread?


I put a ton of effort into this post, so NO!


----------



## Amitz (May 17, 2013)

Mun said:


> I put a ton of effort into this post, so NO!


You are not to blame. Shoot me anyway. Pleeeeeaaaase.


----------



## acd (May 17, 2013)

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.


----------



## Amitz (May 17, 2013)

Wonderful! 


Thanks a ton, @acd!


----------



## George_Fusioned (May 17, 2013)

Awesome writeup *@**acd*!


----------

