< Day Day Up > |
So, assuming you have chosen to build a FreeBSD-based firewall, where do you start? Well, first you need a machine to run the firewall on. The horsepower you require will depend on how much data you send through the firewall, what you do to the data, and any other activities you have the host perform. You could easily run a FreeBSD firewall with a DSL connection, and internal connection, and a wireless network connection on a 200MHZ Pentium with 64 megabytes of RAM. This is definitely not a state of the art machine, but for a simple home firewall, it doesn't break a sweat. Obviously, you need enough network interfaces to suit your needs. Your setup will vary depending on the architecture you have chosen. For the basic IPFW setup here in this chapter, we will be using a bump in the wire architecture, unless otherwise specified. 8.4.1. Kernel ConfigurationThe first step in creating an IPFW firewall is to compile IPFW support into the kernel. The following kernel configurations control the functionality of IPFW.
The only option absolutely required for IPFW to function is IPFIREWALL. The others can be enabled if you need them. If you enable logging, we recommend that you compile in the logging limit because you never know when a rule will run away from you. 8.4.2. Startup ConfigurationSince IPFW is not a process, it does not need to be started as a daemon would. However, it does need to be activated when the machine boots. Add firewall_enable="YES" to your /etc/rc.conf, to enable firewall capabilities at boot. However, if you need to turn the firewall off and on by hand (for instance, when you're testing) you can set the sysctl variable net.inet.ip.fw.enable to 1 or 0 depending on if you are turning the firewall on or off. If you are in secure level 3, once the firewall is enabled, it cannot be disabled. By default, IPFW will use the /etc/rc.firewall shell script for its firewall configuration file. This stock firewall configuration file ships with FreeBSD and contains several different firewall behaviors. You can specify one of the following firewall_types in your rc.conf to use these canned rulesets:
You will need to modify rc.firewall to for the right IP addresses and netmasks for your network. 8.4.3. Firewall ConfigurationThe default firewall configuration in rc.firewall will likely not be exactly what you want. You can choose to modify the existing rc.firewall, create your own, or configure the runtime firewall by hand. However if you choose to configure the runtime firewall by hand, you must understand the ipfw utility and the things you can make the firewall do. The basic syntax for any ipfw command is the following: ipfw [-N] command [index] action [log] protocol addresses [options] 8.4.3.1 Optional argumentsFirst, let's look at the optional parts of the ipfw command. The -N flag instructs ipfw to resolve IP addresses and services to names when displaying them to the user. This has no effect on the way the firewall works, however it may slow display down as the host needs to look up hostnames through DNS. The index option specifies where in the firewall rules this action will be inserted. The index values range from 1 to 65534. Rules are processed in order of their index (starting at 1). Using an index lets you insert a rule into the middle of an already existing and running ruleset. The last rule, 65535, is the default rule for the firewall, generally denying all traffic. The log option indicates that ipfw should send a message to syslog whenever this rule is matched. Be careful to use this only for rules that match infrequently or in conjunction with the firewall kernel logging limit. Without the limit, a rule with a lot of matches can easily fill up the logging filesystem. ipfw has the capability to track the state of TCP and UDP streams. For TCP, it is a relatively straightforward process. The firewall, upon detecting a new TCP session via a SYN packet, will set up a dynamic rule that allows all other packets within that TCP session to be allowed. UDP does not have the same session construct as TCP, so the firewall must guess what packets are part of a UDP flow. For most installations, this guess works just fine. Finally, the options control extra abilities to some ipfw commands. The following are some of the more important options.
There are many other options that are documented in the ipfw(8) manpage. 8.4.3.2 Required argumentsipfw accepts the following commands:
There are many actions ipfw can take on a packet. Chances are, if there's something you want to do based on matching a specific packet, there is an existing ipfw action that will do it. Here are some of the common actions.
For the sake of simplicity, we will examine a simple small office firewall as shown in Figure 8-8. This is a DMZ architecture with a web and mail server running on the server network. The clients on the internal network are standard office workstations. The rules presented in this section could be used as a replacement for the rc.firewall in the default install. Figure 8-8. Example DMZ network
First, to make the rules readable, define each network, network mask, interface, and firewall IP address as a variable in rc.firewall. Servers and other machines that will have specific rules should have variables assigned to them as well. For instance, the DMZ interface can be defined as follows: dmz_if="xl0" dmz_net="10.0.0.0" dmz_mask="255.255.255.0" dmz_ip="10.0.0.1" To further simplify maintenance, a variable should be defined for the ipfw command itself. This allows you to manipulate the command used on all rules by only making one change (for instance, to use the -q flag for quiet output). fwcmd="/sbin/ipfw" For the first rule, make sure that traffic across the loopback interface is allowed. Further, make sure that traffic destined for localhost does not arrive on any external interface. Traffic to 127.0.0.0/8 should always go through the loopback interface. By definition, this is what the loopback interface is for. Anything addressed to the localhost subnet received on a physical interface is completely bogus. ${fwcmd} add pass all from any to any via lo0 ${fwcmd} add deny all from any to 127.0.0.0/8 If a rule indicates that state should be tracked, then a dynamic rule is created for each session. Normally, these dynamic rules aren't checked until a packet reaches the first keep-state rule. However, if you'd like the firewall to check the dynamic rules first (that is, if you are going to have many state-aware sessions), then specify the check-state action early in the ruleset. In this case, we will be using state checking for the servers on the DMZ. Since we assume a fair bit of traffic to these servers, we utilize check-state. ${fwcmd} add check-state Next, we need to prevent spoofing attacks and packets that should never be seen on the Internet. Traffic from the netblocks specified in RFC 1918: Address Allocation of Private Internets should never be received on the public Internet interface: # anti-spoofing ${fwcmd} add deny all from ${int_net}:${int_mask} to any via ${dmz_if} ${fwcmd} add deny all from ${ext_net}:${ext_mask} to any via ${dmz_if} ${fwcmd} add deny all from ${dmz_net}:${dmz_mask} to any via ${int_if} ${fwcmd} add deny all from ${ext_net}:${ext_mask} to any via ${int_if} # RFC 1918 address ${fwcmd} add deny all from any to 10.0.0.0/8 via ${ext_if} ${fwcmd} add deny all from any to 172.16.0.0/12via ${ext_if} ${fwcmd} add deny all from any to 192.168.0.0/16via ${ext_if} These anti-spoofing rules are not perfect. Ideally, the rule would state "allow traffic through an interface if and only if its source IP address belongs to a network that exists on that interface." However, with ipfw, this logic can be difficult to enforce. Using the skipto command, rules can be created that enforce proper anti-spoofing logic. This, unfortunately, can make the rules difficult to understand and maintain, so generally the above construct is used.
Traffic to the servers on the DMZ should be allowed from anywhere. Regardless of whether the client is on the internal network or on the Internet, the web server should be reachable. Also, the SMTP port of the mail server should be reachable from everywhere while the IMAP port should only be accessible from the internal network. In this case, we will use state tracking to make the firewall more efficient. # Server rules ${fwcmd} add pass tcp from any to ${websrv} http keep-state ${fwcmd} add pass tcp from any to ${mailsrv} smtp keep-state ${fwcmd} add pass tcp from ${int_net}:${int_mask} to ${mailsrv} imap keep-state
Now for the internal clients, we allow all outbound TCP and UDP connections from our internal network. Whether or not this is an acceptable ruleset is a matter of your local policy. However, for the sake of maintainability and minimizing user impact in our relatively low security environment, these rules are reasonable. ${fwcmd} add pass ip from ${int_net}:${int_mask} to any Next, the firewall itself should be protected. There's generally no reason for anyone except for administrators to communicate directly with the firewall. The exception is traffic used for network troubleshooting such as ping and traceroute. Allowing ping and TRaceroute packets in your firewall ruleset is another one of those religious wars that firewall administrators and security experts like to wage. Some say that allowing this traffic provides attackers with critical information that can be leveraged for an attack. Others feel that ping and tracerouting is a key part of maintaining a network. The authors, who have been responsible for running networks in the past, feel that concise troubleshooting ability outweighs the security consideration. ${fwcmd} add pass tcp from ${admin_host} to ${int_if} ssh keep-state ${fwcmd} add pass icmp from any to any Finally, we explicitly deny all other traffic and log it. ${fwcmd} add deny ip from any to any log The idea of logging all denied traffic may sound good, but in reality, this is really unworkable. Depending on your network, you may find you have certain types of traffic constantly being dropped by your firewall. For instance, the router to which you are attached may be constantly probing the firewall. By examining your logs, you're able to determine the types of benign traffic that will do nothing but fill your logs. You can then create specific rules before your deny all rule that silently drops this annoying traffic. This basic set of rules should be enough to get you up and running with a few site-specific modifications. As with any active open source project, the features and operation of IPFW will change periodically. Further, IPFW is very feature rich, and much of it's functionality is out of the scope of this book. For the latest news and complete configuration options, see the FreeBSD web site and the ipfw(8) manpage. 8.4.4. Using the FirewallNow that the firewall has rules, there is still the pesky aspect of actually running the firewall. Turing the firewall on and off, viewing and modifying the active ruleset, and resetting the log counters are all key administrative activities for any firewall. IPFW makes these processes intuitive and easy. First, as was stated before, the firewall can be turned on and off by setting the sysctl variable net.inet.ip.fw.enable to 1 or 0 respectively. Depending on your setup, especially if you have NAT running, turning the firewall off can completely isolate the internal network. If NAT isn't running and forwarding is enabled (net.inet.ip.forwarding is set to 1), then your firewall just became a router and the internal network is completely exposed. Be aware of what you're doing if you turn your firewall off. To see the currently running firewall rules, issue the show command to ipfw. For example: % sudo ipfw show 00100 36 1986 allow ip from any to any via lo0 00200 0 0 deny ip from any to 127.0.0.0/8 ... This list has been cut short for the sake of example, but all your firewall rules will be listed. The first column is the rule number of a particular rule. By default, ipfw will increment the rule number by 100 from the previous rule. So, a basic ruleset will iterate as follows: 100, 200, 300, 400, etc. The next column indicates how many packets have matched the rule and the following column shows the number of bytes. For instance, the first rule has been matched 36 times for a total of 1986 bytes. The final column is the rule itself. If you're using keep-state to track connections, ipfw is creating dynamic rules in the background to handle state-based traffic. To see what dynamic rules have been created through stateful sessions, use the -d flag: % sudo ipfw -d show ... ## Dynamic rules (3): 00400 172 24784 (296s) STATE tcp 192.168.0.78 49783 <-> 192.168.0.10 22 00400 16 1456 (1s) STATE tcp 192.168.0.78 49761 <-> 192.168.0.10 22 The first column is the rule number to which this dynamic rule is associated. The number in parenthesis specifies how long that dynamic rule has until it is aged out of the state table. The rest is similar to standard show output. Rules can also be added to the ruleset on the fly. For instance, if you wanted to put a rule allowing all UDP port 53 traffic immediately following the loopback rules, you could do the following: % sudo ipfw add 250 pass udp from any to any 53 We've chosen 250 arbitrarily; the important thing is that the number you specify must fall in between the two adjacent rule numbers. Rules can also be deleted on the fly as well. If we realized that inserting a rule to allow all UDP port 53 traffic before our anti-spoofing rules was a really bad idea, we could do the following. % sudo ipfw delete 250 Voìla, the rule is gone. The ability to add and delete rules on the fly is a great administrative feature. However, it's critical to update the static ruleset with the rule as well. That way, if the firewall were to reboot for some reason, the new rule would be read out of your firewall configuration file automatically at boot time. Rebuilding the "correct" ruleset can be a time consuming process. Also, be careful when modifying rulesets remotely. One typo, and you can lock yourself out of the host completely. Direct console access is now the only way to change the firewall configuration. There are various tricks to prevent a typo from causing this problem. One solution is to have the first rule in your firewall ruleset explicitly allowing traffic from your workstation to the firewall. Another trick is to schedule a reboot at some point in the future using the at(1) command (echo reboot | at now + 10 minutes) and then modify the firewall. If things go badly, the firewall will reboot automatically. If things go well, you can use atrm(1) to cancel the reboot. Finally, you may find that you've reached your logging (verbose) limit on a rule. Either through inspection of ipfw show output (hey, I see my rule has more hits than my limit) or through syslog output (messages about the log limit being exceeded showing up in syslog), you discover that the rule is no longer logging. The counter on a rule can be reset by using the zero command and the rule number. % sudo ipfw zero 100 Entry 100 cleared Now all of rule 100's accounting as been cleared: % sudo ipfw show 100 00100 0 0 allow ip from any to any via lo0 If you would rather get a clean slate for the entire ruleset, you can zero out all statistics by leaving off the rule number. |
< Day Day Up > |