Create a file where we can declare some rules to use:
sudo nano /etc/iptables.firewall.rules
Inside there you'll want to paste the following:
*filter
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT
# Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow all outbound traffic - you can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
# Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
# Allow SSH connections
#
# The -dport number should be the same port number you set in sshd_config
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
# Allow ping
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# Drop all other inbound - default deny unless explicitly allowed policy
-A INPUT -j DROP
-A FORWARD -j DROP
COMMIT
Save that. Next, we need to apply those rules – this is just a text file, and we need to instruct iptables to actually use it.
sudo iptables-restore < /etc/iptables.firewall.rules
That should have loaded the rules and applied them; you can check by
iptables -L
The output of that command ought to look like
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
REJECT all -- anywhere 127.0.0.0/8 reject-with icmp-port-unreachable
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere tcp dpt:http
ACCEPT tcp -- anywhere anywhere tcp dpt:https
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
ACCEPT icmp -- anywhere anywhere
LOG all -- anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "iptables denied: "
DROP all -- anywhere anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
Great, it's working! But if you reboot the server it won't be. So lets fix that by creating a file which will run at boot.
sudo nano /etc/network/if-pre-up.d/firewall
Inside that file paste:
#!/bin/sh
/sbin/iptables-restore < /etc/iptables.firewall.rules
Save it. Now we must make sure it's allowed to execute:
sudo chmod +x /etc/network/if-pre-up.d/firewall
Done. The firewall is now running with those rules applied and those rules will be re-applied every time the server reboots. But it's not blocking China yet; it's only blocking anything not on port 80 or 443 (http and https).
Using ipset to block China
You can't manually add a few thousand IP addresses to your iptables, and even doing it automatically is a bad idea because it can cause a lot of CPU load (or so I've read). Instead we can use ipset which is designed for this sort of thing. ipset handles big lists of ip addresses; you just create a list and then tell iptables to use that list in a rule.
Note; I assume that the entirety of the following is done as root. Adjust accordingly if your system is based on sudo.
apt-get install ipset
Next, I wrote a small Bash script to do all the work, which you should be able to understand from the comments in it. Create a file:
nano /etc/block-china.sh
Here's what you want to paste into it:
# Create the ipset list
ipset -N china hash:net
# remove any old list that might exist from previous runs of this script
rm cn.zone
# Pull the latest IP set for China
wget -P . http://www.ipdeny.com/ipblocks/data/countries/cn.zone
# Add each IP address from the downloaded list into the ipset 'china'
for i in $(cat /etc/cn.zone ); do ipset -A china $i; done
# Restore iptables
/sbin/iptables-restore < /etc/iptables.firewall.rules
Save the file. Make it executable:
chmod +x /etc/block-china.sh
This hasn't done anything yet, but it will in a minute when we run the script. First, we need to add a rule into iptables that refers to this new ipset list the script above defines:
nano /etc/iptables.firewall.rules
Add the following line:
-A INPUT -p tcp -m set --match-set china src -j DROP
Save the file. To be clear, my full iptables.firewall.rules now looks like this:
*filter
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT
# Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Block anything from China
# These rules are pulled from ipset's china list
# The source file is at /etc/cn.zone (which in turn is generated by a shell script at /etc/block-china.sh )
-A INPUT -p tcp -m set --match-set china src -j DROP
# Allow all outbound traffic - you can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
# Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
# Allow SSH connections
#
# The -dport number should be the same port number you set in sshd_config
#
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
# Allow ping
-A INPUT -p icmp -j ACCEPT
# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# Drop all other inbound - default deny unless explicitly allowed policy
-A INPUT -j DROP
-A FORWARD -j DROP
COMMIT
Right now, nothing has changed with the server because no new rules have been applied; to do so, run the block-china.sh script:
/etc/block-china.sh
This should show some output as it pulls a fresh list of Chinese based IPs and then, after a few seconds or so, it will complete and drop you back to a command prompt.
To test if it worked, run:
iptables -L
You should now see a new rule blocking China – the output ought to look like this:
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
REJECT all -- anywhere loopback/8 reject-with icmp-port-unreachable
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
DROP tcp -- anywhere anywhere match-set china src
ACCEPT tcp -- anywhere anywhere tcp dpt:http
ACCEPT tcp -- anywhere anywhere tcp dpt:https
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
ACCEPT icmp -- anywhere anywhere
LOG all -- anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "iptables denied: "
DROP all -- anywhere anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
Almost done! This works, and will continue to work on re-boots. But, IP addresses change and that list will grow stale over time. If you want to pull and apply an updated list of IPs you can just run the block-china.sh script again.
Configure your websever:
We use Ngnix, So steps to block traffic from China do as follow :
Check ip modules are enabled
nginx -V
If you see --with-http_geoip_module in the output, you are ready to use the GeoIP database with nginx:
root@server1:~# nginx -V
nginx version: nginx/1.2.1
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-pcre-jit --with-debug --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl --with-mail --with-mail_ssl_module --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-auth-pam --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-echo --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-upstream-fair --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-dav-ext-module
root@server1:~#
Installing The GeoIP Database
On Debian/Ubuntu, the GeoIP database can be installed as follows:
apt-get install geoip-database libgeoip1
This places the GeoIP database in /usr/share/GeoIP/GeoIP.dat.
It is possible that it is a bit outdated. Therefore we can optionally download a fresh copy from the GeoIP web site:
mv /usr/share/GeoIP/GeoIP.dat /usr/share/GeoIP/GeoIP.dat_bak
cd /usr/share/GeoIP/
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
gunzip GeoIP.dat.gz
Configuring nginx
Open /etc/nginx/nginx.conf...
vi /etc/nginx/nginx.conf
... and place this in the http {} block, before any include lines:
[...]
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default yes;
FK no;
FM no;
EH no;
}
[...]
This allows all countries, except the three countries set to no (you can find a list of country codes here). To do it the other way round, i.e. block all countries and allow only a few, you'd do it this way:
[...]
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default no;
FK yes;
FM yes;
EH yes;
}
[...]
Now, this actually doesn't block any country, it just sets the $allowed_country variable. To actually block countries, you must open your vhost configuration and place the following code in the server {} container (this can go inside and also outside any location {} block):
[...]
if ($allowed_country = no) {
return 444;
}
[...]
This returns the 444 error code to any visitor from a blocked country. What this does is it closes the connection without sending any headers. You can also use another error code like 403 ("Forbidden") if you like.
Reload nginx afterwards:
/etc/init.d/nginx reload
4 Links
· nginx: http://nginx.org/
· nginx Wiki: http://wiki.nginx.org/
· HttpGeoipModule: http://wiki.nginx.org/HttpGeoipModule