Random thoughts, tips & tricks about Slackware-Linux, Lego and Star Wars

Firewall - Blocking a specific IP

July 23rd, 2009 by Niels Horn in , , ,

This week I needed a solution to block and unblock certain IPs from accessing the internet at certain times.
I didn't want to change my firewall script too much and didn't want to change it every time the rules changed (which change a lot due to variable circumstances).

So I created a two-part solution. In this post I will describe how I changed my firewall and show a simple script that can block or unblock a specific IP address.

In a following post I will describe how I created a script with a configuration file where I can define when to do what.

iptables and chains
Firewalls in Linux are built with iptables and "chains". Check article for some basics about writing your own firewall script.

In a chain you can put several rules to filter network packets. It is possible to call a chain from another chain. If the called chain does not filter the packet, it automatically returns to the calling chain.

For instance, we have the standard "FORWARD" chain in a firewall.
In the beginning of this chain, we can always call a user-defined chain called "f_ip".

We create the user-defined chain with:

iptables -N f_ip

And pass all forwarded packets through this chain by including this line as one of our first rules in the FORWARD chain:

iptables -A FORWARD -j f_ip

If the "f_ip" chain is empty, or none of the rules filter the packet to be forwarded, the "FORWARD" chain takes over again:

Now I also want to know if a blocked IP address tried to use the internet, so I created another user-defined chain to log & drop packets:

iptables -N f_ip_drop
iptables -A f_ip_drop -j LOG --log-level 6 --log-prefix "FW: ip: "
iptables -A f_ip_drop -j DROP

Take note that for the time being, nothing goes to the f_ip_drop chain!

But now I can use an external script to add rules to the f_ip chain to filter a specific address without having to change my firewall script.
This external script could contain something like:

iptables -A f_ip -s a.b.c.d -j f_ip_drop

(where "a.b.c.d" is substituted by a real IP address of course)

This part of the firewall then works as follows:

The external script
So now I had my firewall altered and I was ready to write my script to block and unblock specific IP addresses.

The result is here:

#!/bin/bash
#
# fw_ipfilt Filter specific IP addresses
# Needs rc.firewall 0.2.3 or newer
#
# Version: 0.0.1 - Thursday, Jul 23, 2009
#
# Author: Niels Horn (niels.horn@gmail.com)

###################
## Configuration ##
###################

IPT=iptables

######################
## Clear IP filters ##
######################
ip_clear() {
# flush f_ip chain
$IPT -F f_ip
}

######################
## Add IP to filter ##
######################
ip_addip() {
ip=$1
# send all packets from or to $ip to f_ip_drop chain
$IPT -A f_ip -s $ip -j f_ip_drop
$IPT -A f_ip -d $ip -j f_ip_drop
}

###########################
## Remove IP from filter ##
###########################
ip_delip() {
ip=$1
# delete rules for $ip from f_ip chain
$IPT -D f_ip -s $ip -j f_ip_drop
$IPT -D f_ip -d $ip -j f_ip_drop
}

###################
## Check Command ##
###################

case "$1" in
'add')
ip_addip $2
;;
'clear')
ip_clear
;;
'del')
ip_delip $2
;;
*)
echo "use $0 add|del <ip> or $0 clear"
esac

I saved this script as /usr/local/sbin/fw_ipfilt and made it executable and readable only for root (chmod 700).
Now I can block an IP address at any time with:

fw_ipfilt add 192.168.1.123

and unblock it with>

fw_ipfilt del 192.168.1.123

To unblock all previously blocked addresses use:

fw_ipfilt clear

As always, if you have any questions or suggestions, feel free to comment on this post!