Configure bastion host and firewall iptables policies so you can see exactly what the security policy is.
Last month we used Firewall Builder to create a set of reusable objects for iptables policies. In this month's column, I show you how to use Firewall Builder to create two such rule sets: one for a bastion host that needs to defend itself and another for a firewall that needs to defend entire networks.
Let's consider the bastion host scenario first. A common misconception about Netfilter/iptables, and about packet filtering in general, is that packet inspection is strictly a function of firewalls. In-depth defense, however, dictates that it's foolish to put all your security eggs in one basket. Although you must use a carefully configured and monitored firewall to protect all your internet-connected hosts, those hosts also should be able to defend themselves, especially the bastion hosts on which you host publicly accessible services, such as FTP and WWW.
If, for example, your public web server runs Linux 2.4, it follows that you should configure its local Netfilter rules to provide an extra level of defense in case a clever attacker subverts or otherwise gets around your enterprise firewall. If your server runs a pre-2.4 kernel, you need to use ipchains rather than Netfilter/iptables. You also need to find a contributed ipchains compiler plugin for Firewall Builder to build your scripts.
Step one for creating any firewall rule base, even for a bastion host, is to give free rein to the local loopback interface. Loopback is used for certain transactions between local processes and dæmons. Without loopback-allowing rules, things like name-service caching and SSH port forwarding break when you run the iptables script.
Suppose you've got a web server to harden, named Trillian. You've installed Firewall Builder on your administrative workstation; remember, we avoid running the X Window System and therefore X-based applications on bastion hosts. You've subsequently created some objects that describe hosts, networks and groups in your environment, plus a firewall object for Trillian, complete with a loopback-interface definition. In other words, you've done the things I described in last month's column.
You need two rules for Trillian's loopback interface: one that allows all traffic leaving the loopback interface and one that allows everything coming in to it. Follow these steps to create two such rules (Figure 1):
Beneath and to the right of your firewall's loopback interface sub-object, on the left-hand side of the Firewall Builder screen (in Figure 1, this is named loopback), select the loopback interface's policy, which should be empty.
In the Rules menu, select Append rule at the bottom. A blank rule appears in the right-hand half of the window.
Drag the firewall icon next to the name Trillian into the blank rule's Source field. Be sure to wait until the cursor changes into a plus (+) before releasing the mouse button.
Right-click in the new rule's Action field and select Accept from the menu.
Right-click in the rule's Direction field and select Outbound.
Right-click on the paper and pencil icon in the rule's Options field and select Turn logging OFF.
Right-click again in the rule's Options field and select Modify options. In the resulting window, check the box near the bottom of the window, which disables stateful inspection. We don't need to waste CPU overhead on state tracking for loopback traffic.
Optionally, right-click in the new rule's Comment field and select Edit Comment if you wish to write a brief reminder of the rule's purpose, perhaps “allow loopback outbound”.
To create the second rule in Figure 1, repeat steps 2 through 8. In step 3, however, drag Trillian's icon into the new rule's Destination field rather than its source. In step 5, set the direction to Inbound.
How, you may ask, do these rules work? First, you should understand that they apply only to the loopback interface. It's possible to create rules specific to any interface, rules that are parsed before your firewall's global policy. Although we used Trillian as the source and destination, respectively, of our two loopback rules, this doesn't mean that the rules match packets with particular IP addresses, that is, Trillian's. They'll match any packets leaving or entering the loopback interface.
This leads me to my last point about loopback rules. It may seem counterintuitive to use two rules referencing the firewall object rather than one rule that says any source to any destination should be accepted. But in my own tests, the single-rule approach caused Firewall Builder to write its loopback rules for the FORWARD chain rather than for INPUT and OUTPUT, which counterproductively killed loopback on my test system. Changing to separate loopback in and loopback out rules fixed the problem. Don't worry; this is the only time I've seen Firewall Builder choose the wrong chain for its rules. At that, it did so only for single-homed hosts, not multi-interfaced firewalls.
Once your bastion host's loopback needs have been attended to, turn your attention to its global policy. This requires a little thought. You want Netfilter to provide a meaningful amount of protection but not at the expense of desired functionality.
Our example host, Trillian, is a web server, so we want to allow other hosts to access it with HTTP and HTTPS. We also want to allow Trillian to perform DNS lookups for coherent logging. In addition, some sort of administrative connectivity should be allowed. The tool of choice for this purpose is SSH, so we'll also allow inbound SSH connections but only from our internal network. Figure 2 shows such a policy as defined for Trillian.
I'll spare you a blow-by-blow description of how I created every single rule, but several things are worth noting. First, in the object hierarchy on the left-hand side of the window, you can see that I had selected the global Policy object in the hierarchical level directly below Trillian, rather than either of the interface-specific policy objects.
I also referenced not only the Trillian object, but also a network object named Net_Internal, which is the example network object from last month's column. This object refers to an entire network's worth of IP addresses, 192.168.111.0 to be exact. Whereas rule 02 uses a single IP address (Trillian's) as its source IP address, rule 03 matches packets whose source IP address is any of the entire range, 192.168.111.1-192.168.111.254.
Another important tip for rule building is to get at Firewall Builder's handy prebuilt service objects, click the Standard tab on the left-hand side of the window. Be careful, though; if you do anything besides drag a service object (for example, dns_tcp) into your rules, the rules display on the right-hand part of the window is replaced with information about whatever you've selected.
In other words, if you're working on a policy, you can click on the Standard tab, click on the + (expand) and - (collapse) icons in the hierarchy window and click and drag service objects from it, all without changing the mode of the right-hand part of the window. But if you simply select a service object or category in the Standard hierarchy (by clicking on it once without dragging), that object's properties are displayed on the right. You have to go back to the User tab and reselect your firewall's global policy to display your rules again. You do not lose any data, but this can be inconvenient and unsettling if you aren't expecting it.
A more substantial observation is that in all of these rules, I left stateful inspection turned on. I skipped step 7 from our loopback-rules procedure. Normally, we want the kernel to keep state information on network transactions; this is why we can describe most transactions with a single bidirectional rule rather than with two unidirectional rules. For example, thanks to stateful inspection, whenever a transaction matches rule 02 from Figure 2, which allows inbound SSH traffic from hosts on the internal network, Trillian's kernel matches not only those inbound SSH packets, but also the SSH packets that Trillian sends back out in reply. Had I turned off stateful inspection for rule 02, I'd need another rule allowing all packets originating from TCP port 22 on Trillian to accommodate those replies.
Finally, all rules but the last one have logging turned off, as described in step 6 of our loopback-rules procedure. Most people don't find it a useful or justifiable use of disk space or I/O overhead to log every packet their firewall rules process. Personally, I tend to focus on dropped packets and forego logging on allow rules. Thus, the sample rules in Figure 2 end with a cleanup rule at the bottom that explicitly drops any packet not matching the other rules or the rules in any interface-specific policies such as the loopback policy.
This rule's sole purpose in life is for logging. Firewall Builder automatically sets the default policy for all my iptables chains to DROP, but these dropped-by-default packets aren't logged unless you tell Netfilter to do so.
An experimental dropped-table patch is available for Netfilter that allows automatic logging of all dropped packets, but I recommend you wait for this code to stabilize before going out of your way to compile it into your kernel. If you can't wait for some reason, you can access this feature from Firewall Builder by selecting your firewall object, clicking its Firewall Properties tab and checking the box next to Log all dropped packets. For more information on the dropped-table patch, see www.netfilter.org/documentation/pomlist/pom-summary.html.
After you've finished your firewall policy, you need to convert it to an actual iptables script. To do so, first make sure that on the screen's left-hand hierarchy view your firewall's object, its global policy or one of its interface policies is selected—it doesn't matter which. Then, pull down the Rules menu and select Compile. Figure 3 shows the result.
Upon successful policy compiling, Firewall Builder writes a file whose name consists of the name of the firewall object whose policy you compiled and the suffix .fw. The example script we produced, trillian.fw, is shown in Listing 1. Listing 1 has been modified slightly due to space requirements, and some housekeeping material has been removed. All of the actual rules mentioned in the article are present.
This script now can be copied over manually to Trillian and run as is, or it can be converted manually into a startup script appropriate to Trillian's Linux distribution, for example, a standard Red Hat 7.3 startup script. Easier still, you can copy it over automatically and activate it with a Firewall Builder installer script such as fwb_install, available under contrib at sourceforge.net/project/showfiles.php?group_id=5314.
The latter in particular is an elegant and simple way to copy and activate your firewall scripts securely; fwb_install uses scp to copy the script to /etc/firewall on the remote host and then ssh to execute the script remotely. If you've downloaded fwb_install somewhere on your Firewall Builder system, you can configure Firewall Builder to use it from within each firewall object's Compile/Install properties.
Be sure to tweak fwb_install manually to match your system settings and to set up SSH keys for fwb_install to use. Once you've set this up, all you need to do to install your policies after compiling them is pull down the Rules menu and select Install.
As handy as fwb_install is, you'll want a startup script in place on your target system that also executes the firewall script on startup and after any reboot. Otherwise, the system is vulnerable between each startup or reboot and the next time you execute Install from within Firewall Builder. It's easy to copy and adapt existing scripts in your system's /etc/init.d directory.
I've devoted most of this column to the bastion host example, but building a policy for a multihomed (multi-interfaced) network firewall is quite similar. Create loopback policies, create anti-spoofing policies for the other interfaces, create a global policy, compile the policy and install it.
The big differences have to do with the fact that a firewall, unlike a server, has multiple network interfaces. Because a single-interfaced system receives all packets except loopback at one physical point, it can't distinguish spoofed packets from legitimate packets; it must take each packet's source-IP address at face value. But a multihomed system can distinguish easily between packets that truly originate from local networks and packets that arrive from the Internet but have forged source-IP addresses matching a local or trusted network.
For instance, our example internal network is numbered 192.168.111.0 (subnet mask 255.255.255.0). If we have a firewall named Slartibartfast between this network and the rest of the world, we can use anti-spoofing rules to tell Slartibartfast to drop any packet immediately from any interface other than the one facing our internal network, if that packet has a source IP beginning with 192.168.111. Such a packet is obviously spoofed. Figure 4 shows Slartibartfast's anti-spoofing rule.
Before I made this rule, I created several network objects that refer to the reserved IP address spaces defined in RFC 1918, “Address Allocation for Private Internets”. RFC 1918 address spaces are for use only within an organization and can't be routed over the Internet, so any internet firewall should consider inbound packets bearing such addresses to be spoofed, which is precisely what the rule in Figure 4 does. Because my RFC 1918 Class C object expands to 192.168.0.0, subnet mask 255.255.0.0 and my internal network address is 192.168.111.0 (part of RFC 1918 address space), it wasn't necessary to include my Net_Internal object in this rule.
By the way, if you're not familiar with RFC 1918, my RFC 1918 Class A object refers to 10.0.0.0, subnet mask 255.0.0.0, and RFC 1918 Class B is 172.16.0.0, subnet mask 255.240.0.0.
Figure 5 shows Slartibartfast's global policy; because this article is already too long I won't explain it in-depth. But the whole point of Firewall Builder is to display firewall rules in an easy-to-read format, so Figure 5 should be self-explanatory.
Speaking of self-explanatory, did I mention that all rules, whether loopback, anti-spoofing or global, can be generated quickly and automatically using Firewall Builder's policy druid? You can run it by selecting a firewall object, pulling down the Rules menu and selecting Help me build a firewall policy.
Don't get too irked at me for not mentioning this until after making you slog through all my instructions on building policies the hard way. Firewall rules are too important to trust entirely to druids. Hopefully, you now can understand and tweak or even correct the rules Firewall Builder generates for you. Regardless of how you build your policies, I hope you find Firewall Builder as useful as I have.