Building a Transparent Firewall with Linux, Part IV

Now that you understand what the network looks like, let's decide how to
manage its dataflows. In my example scenario, the firewall will have
a “deny by default” policy, as any good firewall should. The task,
therefore, will be one of anticipating and allowing the dataflows you
need the firewall to allow.

First, assuming the LAN's DHCP server is upstream of the firewall, you
need to allow DHCP traffic between UDP port 67 (the DHCP server port)
and UDP port 68 (the DHCP client port).

Next, you don't want to lock yourself out of the firewall itself! You
need to allow traffic from the LAN to TCP port 22 on the firewall.

As you can see in Figure 1, the example network has an outbound Web
proxy. Because one of the best uses of a firewall is to enforce use of
a Web proxy, you'll for sure want to allow only outbound Web traffic
originating from the Web proxy. You'll also allow outbound DNS queries
(and corresponding replies).

That's it! Things downstream of the firewall—that is, transactions
between hosts connected to the broadband router shown in Figure 1—don't need to be allowed by the firewall. For example, print jobs sent
from wired and wireless DHCP clients to the network printer don't need an
“allow LPR” rule, because those packets should never reach the transparent
firewall in the first place.

(If, however, you have only a few hosts on your LAN and elect to omit
the downstream switch or broadband router and cable them directly to
the transparent firewall, this will not be the case. You
will
need to allow for “LAN-to-LAN” transactions of that type.)

Creating a Custom Firewall Script

Now, finally, you're ready to write a custom firewall script! You could,
of course, simply edit the file /etc/init.d/firewall. But, that would make
it harder to revert to OpenWrt's native uci-driven firewall
system later—better to leave that script alone. I prefer to create a new
script from scratch, arbitrarily named /etc/init.d/iptables.custom.

Listing 2 shows what /etc/init.d/iptables.custom needs to look like
in order to implement the firewall policy we arrived at in the previous
section. Let's dissect it.

First, note the includes file /etc/rc.common at the top: this provides
functions like enable, disable and other
housekeeping functions
that OpenWrt uses to manage startup files.

Next, START=46 specifies the priority/order for running this script
at startup. 46 is the same slot that the default
OpenWrt “firewall”
startup script uses, which is to say, after networking is enabled but
before the DropBear SSH server and other network services are started.

Next come some “shorthand” variables we'll use throughout the
script. IPTABLES, obviously enough, specifies the full path to the
local iptables command. LOCALIP is the firewall's bridge IP address;
LOCALLAN is the network address of the local LAN,
and WEBPROXY
gives the IP address of the Web proxy.

The “stop” function (as in ./iptables.custom
stop) causes the script to
flush all iptables rules from kernel memory and to load default
ACCEPT
policies for all three default firewall tables,
INPUT, FORWARD and
OUTPUT. This does not
“stop all traffic”; rather, it stops all
restrictions on traffic (thus, the warning message).

Now we come to the heart of the script: the “start” function,
containing the firewall policy in the form of a list of iptables commands.

First, flush any active rules and delete any custom tables, so
you begin with a clean slate ($IPTABLES --flush and
$IPTABLES
--delete-chain). Next, set default deny policies for the INPUT,
FORWARD and ACCEPT chains. (You could just as easily choose
REJECT
as the default policy, but because this involves sending ICMP replies to
jilted clients, versus DROP's simply ignoring them, there's a slight
performance benefit to DROP.)

Next come two rules to allow interprocess communication on the
firewall itself, by allowing all packets arriving from and destined
for the “loopback” interface. This is followed immediately, however,
by an antispoofing rule that blocks traffic addressed to the firewall
from the firewall's own IP address.

Next are two rules allowing DHCP requests—that is, packets from UDP
port 68 sent to UDP port 67—and DHCP responses—that is, packets from
UDP port 67 to UDP port 68. These two rules are necessary only if your
DHCP server is on the other side of your firewall from your DHCP clients.

You may have noticed that these two DHCP rules and the subsequent
rules for SSH, HTTP proxying and DNS are “stateless”. Rather than
invoking the iptables “state” module, which lets you allow, for example,
outbound DHCP queries while letting the kernel decide what constitutes
a valid response, you're explicitly allowing the reply traffic. This is
an admittedly archaic way to write iptables rules.

However, as I mentioned in the sidebar, OpenWrt has significant
performance issues when used as a bridging firewall. Because the
“state”
module imposes still more of a performance hit, and because this firewall
policy is simple to begin with, I'm doing it the old-fashioned way. For
a bridging firewall on a better-performing distribution/hardware
combination, I definitely would take advantage of Linux's state-tracking
features!

The next pair of rules in Listing 2 allows SSH connections to the firewall
itself, but only from the local LAN. Note that the “incoming” leg of
SSH transactions is handled in the INPUT table, whereas the
“outbound”
leg is processed in the OUTPUT table. If you were using -m
state,
the OUTPUT leg would be implicit.

Next come two pairs of rules allowing only the Web proxy to send and
receive traffic to/from TCP ports 80 and 443, which, of course, correspond
to HTTP and HTTPS, respectively.

This wouldn't work unless DNS did also, so next are rules allowing DNS
queries to TCP and UDP ports 53 (ordinarily, DNS queries just use UDP,
but once in a while they can occur over TCP as well).

Finally, the script ends with three “cleanup” rules that place a
“drop all” rule at the bottom of each of the default tables. These are,
of course, redundant with the default “DROP” policies I set near the
beginning of the start() function, but specifying
such cleanup rules
are a firewall best practice; sometimes redundancy is desirable!

When you type in any firewall script, be careful! At the very least,
double- and triple-check the SSH rules that allow access to the firewall.
If there's any problem with those rules, you'll be locked out once
you run the script, and you may even need to re-flash your firewall to
recover. You can fix other things if SSH works, but if SSH doesn't work,
you'll be stuck.

Once you're confident enough to test your rules, save the new script. Be
sure to set the “execute” bit on it like so:

root@sugartongs:/etc/init.d# chmod a+x ./iptables.custom

And, enable the script at startup, like this:

root@sugartongs:/etc/init.d# ./iptables.custom enable

Now for the moment of truth—load the rules:

root@sugartongs:/etc/init.d# ./iptables.custom start

Test the rules by making sure the things you want to work still do
(connecting back to the firewall via SSH, surfing the Web via your
Web proxy and so forth). Also, be sure to test some things
you don't expect to work, such as surfing the Web
without going
through the proxy or connecting to an FTP server using an FTP client
application. In my own experience, the challenge with OpenWrt is getting
iptables to “see” and act on traffic; the real test is ensuring that
it's blocking anything!

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.