Dynamic firewall rules for iptables

Here's the problem: you're allowing access to some ports of your server based on source IPs. This is common practise, even if it's not 100% secure (since source IPs can be spoofed in certain situations). However what if you've no fixed source IP address(es) (which is common practise too) that you can feed into iptables rules? You can register a domain name at a dynamic DNS provider (eg. dyndns.org) and have your client (a DSL router or a client app on your PC) automatically update the IP of that domain name, whenever your client's internet connection get's up. But still, iptables does not allow use of domain names in firewall rules (and it's good so Smile ). Here's where my script comes into play. It allows you to specify a list of domain names and destinations (host+port) for which the script will automatically generate permitting iptables rules.

The script is self-documented: take a look at the comments at the start of the script to learn how it can be set up. Don't forget to configure cron to execute the script periodically so iptables rules are updated regularily as the clients' IPs change.

AttachmentSize
dynamic_address_rules-1.1.sh11.76 KB
dynamic_address_rules-1.2.sh12.05 KB
dynamic_address_rules-1.3.sh12.24 KB
dynamic_address_rules-1.4.sh12.36 KB

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

dynamic address rules update

I've uploaded a new version with some minor bugfixes and modifications in logging.

Would you

Would you pls provide me the txt format of this script?
Dynamic firewall rules for iptables
2009, February 22 - 17:46 — müzso

Re: Would you

The script is attached to my post and is downloadable for everybody. See the "Attachments" section towards the end of the post.

Hard Coded eth0

I noticed that you have hard coded eth0.

I recomend that you allow for non hard coded eth?

I will modify the code to allow this if you would like me to send it back?

Re: Hard Coded eth0

Thanks for the suggestion. I've uploaded v1.3 that has the network interface name as a parameter at the start of the script. If the script gains more popularity I might even add config file support (ie. the parameters would be pulled out from the script and placed in a separate config file in /etc).

Bugfix for my dynamic firewall rules script

If there was already a rule in the rule_generation_specs array and a new rule was added and the cache file already existed with the old rule's cache record, then the iptables chain's rules were regenerated, but only the new spec rule was applied ... thus IPs of all old rules were denied access.
This got fixed in v1.2.

iptables DNAT and SNAT

Hello,

Could be this script modified to manage DNAT and SNAT (POSTROUTING and PREROUTING rules), with dynamic ips?

Thanks

Re: iptables DNAT and SNAT

Yes, it could be modified.

Will this accept a port-range (rather than single port)

Wow, awesome script! Thank you for sharing this.

First, would it be easier to simply hard-code the dynamic DNS entries in a iptables script file, and re-run the file (flushing/resetting the rules) every X minutes? Or is there a reason this wouldn't work?

Second, I'm curious about how to open multiple ports...

The current example shows:
"dynamic2.address.example.com;22"

Could it open multiple ports by using a port-range, for example:
"dynamic2.address.example.com;0:1023"

Or is there any way to simply remove the second parameter for complete access by that host?
"dynamic2.address.example.com"

Re: Will this accept a port-range (rather than single port)


First, would it be easier to simply hard-code the dynamic DNS entries in a iptables script file, and re-run the file (flushing/resetting the rules) every X minutes? Or is there a reason this wouldn't work?

It'd be definitely easier. Whether it works as expected ... it depends. Under certain circumstances it might work. But that method would rely on the system-wide DNS resolver to return an up-to-date DNS response for the given domain name. Since if you specify a domain name in your iptables command, it'll use the DNS servers as given in your /etc/resolv.conf file (supposing you use the simple file based DNS resolver).
In case of DynDNS, the SOA DNS servers for the dyndns.org domain (eg. ns1.dyndns.org) return a TTL of 60 seconds. If your DNS forwarder honors that, then you should have no problem, since your iptables command will always get an up-to-date IP address for the given domain name. However if your DNS server does not honor the TTLs returned by the ns*.dyndns.org servers (or if you use another dynamic DNS provider that uses a significantly larger TTL), then it will not work well, since your iptables script will get a cached IP address that might not be up-to-date.

And there's another issue: DNS server failure. I've already experienced that dyndns.org name servers can fail sometimes. During the development of this script I've experienced multiple DNS reponse failures from the ns*.dyndns.org servers (probably due to high load). My script can query multiple DNS servers in case one fails. If you specify all dyndns.org DNS servers in your rule, then the script will query each until it gets an IP address from one of them. It is quite unlikely that all ns*.dyndns.org servers fail at the same time so that should be enough. There're five dyndns.org DNS servers (ns1.dyndns.org, ..., ns5.dyndns.org).

Could it open multiple ports by using a port-range, for example:
"dynamic2.address.example.com;0:1023"

The current version does not support port ranges (of course this enhancement might be added later). You can add multiple ports only by listing them one by one.
Eg.: "dynamic2.address.example.com;0,1,2,3,4,5,6,...,1023"
Of course in the above example the "..." should be replaced with all the ports in the range. It'd make quite a long line and I'm not even sure it'd work. I've never tested any rule strings that long. Theoretically it should, but who knows (there might be some secret buffer limitation that I cannot recall right now).

Or is there any way to simply remove the second parameter for complete access by that host?
"dynamic2.address.example.com"

No. The second parameter is compulsory. I could remove this limitation of course. I did not think that somebody would ever require all ports to be opened for a given client address. Of course there're cases (dumb protocols like Oracle's SQL*Net) when you've no choice.

NAT table

Hi,

Can you please let me know what changes are required to support NAT table ?


Warm Regards

Supratik

Re: NAT table

Could you be a bit more specific?

Re: NAT table

I have a rule mentioned in my NAT table which allows google.com from my network

/sbin/iptables -t nat -A POSTROUTING -d 216.239.61.104 -o eth0 -j SNAT --to-source $PUBLICIP [216.239.61.104 is the ip of google.com]

The problem is that, IP address for google.com changes after a certain period of time. Can you please tell me what changes
I have to make in the "dynamic_address_rules-1.3.sh " file for the same ?.

Re: NAT table

Ok, now I see your point.

My script currently supports generation of iptables rules only for incoming connections. The goal was to create rules for incoming connections from remote hosts with a dynamic IP address. You'd like to use it for outgoing connections to remote hosts with a dynamic IP address. It's certainly feasible, but requires quite some changes to the script. I could modify it to support such use cases out of the box, but ...

The google.com (or www.google.com) address is not a dynamic domain name! Multiple IPs are assigned to it, but the IP set is fairly constant. However for each DNS query a different IP address is returned in a round-robin fashion. I assume this is your problem. You've to create iptables rules for all the IPs of the google.com domain. Or you could add google.com to your /etc/hosts file with one of the addresses (to restrict all IP connections going to "google.com" to a single IP) and in this case you'd have to add only a single rule to your iptables list. I'd not suggest the latter approach, but it's certainly an option.

Currently my script does not support generation of iptables rules for all IPs of a multi-IP domain name. In such cases it takes the last IP retrieved from the DNS lookup answer. Later on I might create an option to support rule generation for all IPs of multi-IP domain names.

Re: NAT table

The following few lines of a shell script should achieve your goal:
#!/bin/bash

IPT=/sbin/iptables
DIG=/usr/bin/dig
IFACE=eth0
TABLE=nat
CHAIN=google_rules
HOSTNAMES=(
  "google.com"
)

if ! test -x ${IPT}; then
  echo "${IPT} is not executable" >&2
  exit 1
fi

if ! test -x ${DIG}; then
  echo "${DIG} is not executable" >&2
  exit 2
fi

if $IPT -t ${TABLE} -n -L ${CHAIN} > /dev/null 2>&1; then
  $IPT -t ${TABLE} -F ${CHAIN}
else
  $IPT -t ${TABLE} -N ${CHAIN}
  $IPT -t ${TABLE} -I POSTROUTING -o ${IFACE} -j ${CHAIN}
fi

for record in "${HOSTNAMES[@]}"; do
  for ip in $(${DIG} +noall +answer ${record} | awk '/[ \n\t][0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?$/{print $NF}'); do
    $IPT -t ${TABLE} -A ${CHAIN} -d ${ip} -j ACCEPT
  done
done

$IPT -t ${TABLE} -A ${CHAIN} -j RETURN

Put it in a script and execute it regularly (eg. every 5-10 minutes).
If you need these rules only for access to the HTTP port of google.com, then you might change the POSTROUTING rule to this:
  $IPT -t ${TABLE} -I POSTROUTING -o ${IFACE} -p TCP --dport 80 -j ${CHAIN}

Enterprise connection profiling

Hi,
I am looking for configuration which enables the laptop to identify the network i.e.
1.Office LAN
2. VPN and
3. Home(Internet)
When connected to them respectively it should open and block certain ports based on the connectivity.

For example i need to enable SSH/VNC while i am on office network and block it once i am over the internet.

Please let me know if i can get any help with this.

Re: Enterprise connection profiling

I'm assuming you're asking how to do this on linux, are you?

I'm not a networking expert, but automatic (and secure/dependable) identification of a network seems to be a tough challenge. There might be already standardized protocols and mechanisms for this, but I don't know of any (and even if there're, chances are that most networking devies -switches/routers- do not support them). Detecting the VPN connection (vs. the "plain" connections) is easier, since it'll probably use a different interface (i.e. /dev/* device file), but telling a connection to the office LAN from a connection to the home router is difficult. You could use some simplification, eg. if your PC can connect to a specific port of a specific IP address (and/or talk to some service with verifiable results), then we say it's connected to the office network. You could use similiar "identification" on your home network as well (eg. you could verify the MAC address of the home router that your PC connects to and base the decision -whether you're connected to the home LAN or not- on that).

As for the automation of all this ... there must be some entry points for the NIC's connect/disconnect events that you can use/hijack. I'd guess that probably some udev events are fired when you put the RJ45 plug into your computer's network port (or pull the plug), so you might be able to write custom udev rules to execute some shell script upon these events. And you could write these shell scripts so they detect (based on the previously outlined ideas) the network you're connecting to and pull up (or tear down) some firewall rules based on the given network.

I know these are some very general ideas, but that's all I have right now. Unfortunately I've not done this myself so I cannot provide you with a specific implementation.

P.S.: or you could just try to Google for a solution. There might be free or commercial software (with a GUI and easy setup) that could do what you're aiming for.