Changing the Rules

Router or Firewall with Dynamic IP address


The Problem

So you have this Linux router - firewall with a basic ruleset, and you want to add some rules to make it more secure : allow only traffic to specified DNS servers (in stead of allow all DNS traffic), or specify rules where you need to include the external IP address of your router. That's quite simple, except if these addresses are assigned through DHCP, like when you have a cheap internet account : no fixed address.

In theory, once your ISP has assigned an address, it is not likely to change soon (your computer will ask to extend its DHCP lease before it expires), but at least it would be nice if the firewall rules get adapted to the dynamically assigned addresses whenever you run the script (eg. during the boot)

The Solution

The solution is to interrogate the operating system and get these addresses. The IP address of your external network interface can be found with the ifconfig command. The ifconfig command returns the complete configuration of the network card, including subnetmask, packets sent and received, etc.

	#ifconfig eth0

	eth0      Link encap:Ethernet  HWaddr 00:20:AF:48:1F:4A  
       		  inet addr:193.252.81.136  Bcast:255.255.255.255  Mask:255.255.240.0
	          RX packets:316745 errors:163 dropped:0 overruns:164 frame:163
	          TX packets:117968 errors:0 dropped:0 overruns:0 carrier:0
	          Interrupt:5 Base address:0x220 
	

So, here you have to 'read' the output, and look for the part that says' inet addr:[something that looks like an IP address]' (so that the broadcast address and subnet mask are excluded), then read the address from the 'inet addr: ...' string.

A human can do that on sight, but your computer does not understand what it reads, so you make it look for patterns that it can recognise - e.g. an ip address (version 4) looks like
[number between 1 and 254].[number between 0 and 254].[number between 0 and 254].[number between 1 and 254]

These patterns are called 'regular expressions' - or RegExps. A RegExp to describe an IP address could be : [0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]* : "4 groups of 1 to 3 digits, separated by dots". This does not exactly match our description of a valid IP address, eg. 0.666.999.11 matches the expression but can never be an IP address - but as we expect ifconfig to return valid addresses, that's not too much of a problem. Also : repeating the same part ([0-9][0-9]*) 4 times is also a regular pattern, so the expression could probably be made shorter. And : in regexp, the dot (.) is a wild card : it represents 'any character'. So to specify that it needs to be a dot and nothing else, you'd have to write \. .
Again, for the IP-address this doesn't matter : the string "inet addr:193.250.81.136" will match and nothing else in the output of ifconfig will, so it works.

So we can now recognize [something that looks like an ip addresses] in the output of ifconfig. To 'read' the output of ifconfig, we use grep. grep has options to return the pattern we're looking for, or the complete line on which the pattern occurs, or the name of the file in which a given pattern occurs, etc. we use '-o' : show only the part that matches PATTERN. So,
So, here you have to 'read' the output, and look for the part that says 'inet addr:[something that looks like an IP address]' (so that the broadcast address and subnet mask are excluded), then read the address from the 'inet addr: ...' string.
That would translate to something like :

	#-all in one line !!- 

	WAN_IP=`ifconfig eth0 | grep -o 'inet addr:[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*' | 
								grep -o '[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*'` 

	#check
	echo the public IP address of this machine is $WAN_IP
	

Something similar

The DNS servers can be found in the /etc/resolv.conf file, etc. If we want to create a rule our (personal) firewall should only allow DNS traffic from it's own external IP address to these DNS servers (in stead of allowing a more general : allow all DNS because we need it), we could read the /etc/resolv.conf file (with 'cat'), and create a rule for each IP address found there. Finding the IP addresses is done by pattern matching as demonstrated with the WAN_IP example.


	for NAMESERVER in `grep -o '[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*' /etc/resolv.conf`; do

        	iptables -A OUTPUT -d $NAMESERVER -s $WAN_IP -p udp --dport 53 -j ACCEPT
        	iptables -A INPUT  -s $NAMESERVER -d $WAN_IP -p udp --sport 53 -j ACCEPT	
	done
	

The Flaws

This is a poor example of regular expressions, in fact it's just a first attempt to see how they work. There may be better patterns to match an IP address.

And as for the firewall rules, using the interface name instead of the address may also work - so you don't need to go through all that trouble :-)
Still, could be good to know something like this exists ...

Alternative

an alternative way to extract the ip address from the output of ifconfig is by using 'cut'. Cut works on 'fields" based on delimiters (-d):

	## masquerading to the WAN address of the router

	IF_WAN=$( ifconfig eth0 | grep "inet addr" | cut -d':' -f2 | cut -d' ' -f1 - )
	iptables -t nat -A POSTROUTING -o $IF_WAN -j MASQUERADE
	

Example of a firewall script

Because a firewall script is just a shell script that calls iptables to add rules, you can use variables and control structures such as loops and conditions to make it do almost anything. here's an example.


Koen Noens
January 2005