Generating Firewall Rules with Perl
Like most Linux users, I started out using a simple Bash script to configure the firewall policy on my Linux machines. Eventually, I got tired of writing the same code over and over again, so I decided to use a few program loops to try to factor out some of the redundancy. I also decided that I'd like to separate the actual policy from the rest of the program; this meant that the program would be reading external configuration files. As my Perl skills are vastly better than my Bash skills, I decided to write my firewall rules in Perl.
The program I'm outlining in this article could just as easily have been written in Bash or another scripting language (or even C++, for that matter). The language isn't important. The important thing to realize is that once you write a program like this and debug it, all you have to do to modify security rules is change a configuration file and rerun the script. The configuration files should have an intuitive format so that they are easy for humans to read, understand and modify.
Listing 1 shows the Perl script. I practice top-down programming, so the first several lines of code should give you a good idea of what the program does. Hopefully, you should be able to follow what the program does even if you aren't a Perl programmer.
Listing 1. firewall.pl
#!/usr/bin/perl
$default_policy = "DROP";
$iptables = "/sbin/iptables";
$work_dir = "/root/fw";
set_ip_forwarding(0);
load_interfaces();
$protocols{tcp}++; $protocols{udp}++; $protocols{icmp}++;
init();
set_default_policy();
add_good_hosts();
add_bad_hosts();
build_chains();
add_rules();
set_default_action();
set_ip_forwarding(1);
exit;
###################################################
sub load_interfaces {
my($int, $name);
local(*FILE);
open FILE, "$work_dir/interfaces.conf";
while (<FILE>) {
chomp($_);
if ($_ eq "") { next; }
($name, $int) = split(/\s*=\s*/, $_);
$interface{$name} = $int;
}
}
sub init {
iptables("-F"); # flush rules
iptables("-t nat -F");
iptables("-X"); # delete chains
iptables("-Z"); # zero counters
iptables("-t nat -A POSTROUTING -j MASQUERADE");
iptables("-A INPUT -m conntrack --ctstate ESTABLISHED
-j ACCEPT");
}
sub set_default_policy {
iptables("-P INPUT $default_policy");
iptables("-P OUTPUT ACCEPT");
iptables("-P FORWARD ACCEPT");
return;
}
sub build_chains {
my($interface, $protocol, $chain);
foreach $interface (keys %interface) {
foreach $protocol (keys %protocols) {
$chain = "$interface-$protocol";
iptables("-N $chain");
iptables("-A INPUT -i $interface{$interface}
-p $protocol -j $chain");
}
}
}
sub add_rules {
local(*FILE);
open FILE, "$work_dir/ports.conf";
while (<FILE>) {
chomp($_);
$_ =~ s/#.?//;
if ($_ eq "") { next; }
($int, $proto, $port) = split(/\t/, $_);
$i = $interface{$int};
$chain = "$int-$proto";
if ($proto eq "all") {
foreach $proto (keys %protocols) {
$chain = "$int-$proto";
iptables("-A $chain -i $i -p $proto -j ACCEPT");
}
next;
}
if ($proto eq "udp") {
iptables("-A $chain -i $i -p udp --dport $port
-j ACCEPT");
iptables("-A $chain -i $i -p udp --sport $port
-j ACCEPT");
}
if ($proto eq "tcp") {
iptables("-A $chain -i $i -p tcp --dport $port --syn
-j ACCEPT");
iptables("-A $chain -i $i -p tcp --dport $port
-j ACCEPT");
}
}
}
sub set_default_action {
my($interface, $protocol, $chain);
foreach $interface (keys %interface) {
foreach $protocol (keys %protocols) {
$chain = "$interface-$protocol";
iptables("-A $chain -j LOG
--log-prefix DEFAULT_$default_policy-$chain-");
iptables("-A $chain -j $default_policy");
}
}
}
sub iptables {
my($line) = @_;
print "$iptables $line > /dev/null\n" if ($debug);
$result = system("$iptables $line > /dev/null");
if ($result != 0) {
print "X: ($result) iptables $line\n";
}
}
sub set_ip_forwarding {
my($value) = @_;
local(*FILE);
print "Setting IP forwarding to $value.\n";
open FILE, ">/proc/sys/net/ipv4/ip_forward";
print FILE $value;
close FILE;
}
sub add_good_hosts {
my($host, $comment);
local(*FILE);
open FILE, "$work_dir/good_hosts.conf";
while (<FILE>) {
($host, $comment) = split(/\t/, $_);
iptables("-A INPUT -s $host -j ACCEPT");
iptables("-A OUTPUT -d $host -j ACCEPT");
}
}
sub add_bad_hosts {
my($host, $comment);
local(*FILE);
open FILE, "$work_dir/bad_hosts.conf";
while (<FILE>) {
chomp($_);
($host, $comment) = split(/\t/, $_);
iptables("-A INPUT -s $host -j LOG
--log-prefix $comment");
iptables("-A OUTPUT -d $host -j LOG
--log-prefix $comment");
iptables("-A INPUT -s $host -j DROP");
iptables("-A OUTPUT -d $host -j DROP");
}
}
As you can see, the program isn't very long and certainly isn't very complicated. However, the program is flexible enough to allow me to whitelist or blacklist either individual machines or entire networks completely. As we'll see later on, the build_chains() and add_rules() functions implement a rule-pruning algorithm that keeps the Linux kernel from having to evaluate irrelevant rules.
The set_ip_forwarding() function does what its name implies; it tells the Linux kernel either to forward IP packets, or not to forward packets. The function accepts a single parameter, either 0 or 1, which determines whether the kernel will forward. The script initially turns all forwarding off while it loads the firewall policy. Then, right before the script exits, the script turns forwarding back on. The reason for these extra steps is that we want the router to be in a safe state while we load the actual rules. It's better to block all traffic than it is to allow even one attack through.
The load interfaces() function reads in the names of the network interfaces and assigns mnemonic labels to them. These labels are then used in the rest of the configuration to refer to the actual interfaces. Being able to refer to an interface as lan or even vpn_to_work cuts down on misconfigurations. This also makes it easy to make changes in order to tailor the firewall for use by my friends. In many cases, I simply adjust the interfaces.conf file to reflect my friend's network and suddenly, my friend has a reasonable firewall configuration.
The script works via four configuration files: interfaces.conf, good_hosts.conf, bad_hosts.conf and ports.conf.
Listing 2 shows the contents of my interfaces.conf file. As you can see, I've got six network interfaces in my router. My Internet connection is on eth5. I've got a 10/100TX Ethernet for the house wiring. I've got a Gigabit Ethernet connecting my MythTV PVR to the router for file storage. I've got interfaces for the Wi-Fi and VoIP. Finally, I have a VPN connection to some of my friend's computers. It's much easier to remember that lan is my 10/100 copper network as opposed to trying to remember whether eth3 is my VoIP or Wi-Fi interface. The last thing you want to do is apply the right firewall rule to the wrong interface.
Mike Diehl is a freelance Computer Nerd specializing in Linux administration, programing, and VoIP. Mike lives in Albuquerque, NM. with his wife and 3 sons. He can be reached at mdiehl@diehlnet.com
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- seo services in india
19 min 46 sec ago - For KDE install kio-mtp
20 min 28 sec ago - Evernote is much more...
2 hours 20 min ago - Reply to comment | Linux Journal
11 hours 5 min ago - Dynamic DNS
11 hours 40 min ago - Reply to comment | Linux Journal
12 hours 38 min ago - Reply to comment | Linux Journal
13 hours 28 min ago - Not free anymore
17 hours 30 min ago - Great
21 hours 17 min ago - Reply to comment | Linux Journal
21 hours 25 min ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?




Comments
Error in add_good_hosts subroutine
Hi Mike,
Trying to get this working.
I've only one good host, namely loopback so my good_hosts.conf looks like this:
127.0.0.1 loopback
and I'm getting the following error on stdout when I run the script:
Setting IP forwarding to 0.
Try `iptables -h' or 'iptables --help' for more information.
X: (512) iptables -A INPUT -s 127.0.0.1 loopback -j ACCEPT
Try `iptables -h' or 'iptables --help' for more information.
X: (512) iptables -A OUTPUT -d 127.0.0.1 loopback -j ACCEPT
Setting IP forwarding to 1.
I'm not much a Perl coder I'm afraid, and by the looks of it it's an iptables issue rather than a Perl issue.
Any help much appreciated.
sdt
Error in add_good_hosts subroutine
Are you putting a TAB after the IP address, or a SPACE? The script splits on tab. I'll bet this fixes your errors.
Email me directly if it doesn't. My address in in the article.
Mike.