Wednesday, January 10, 2007

Sharing Internet Connections

Adding Rules

The rulebase created in the last article allows your computer to access the internet, and allows fwbuilder to use SSH on the localhost to install firewall policies. This means you should be able to do most internet-related activities including web browsing, sending email, using pkg_add to install software, using cvsup and portupgrade to keep your software up-to-date, and using most of the common internet applications such as Gaim for chat, Skype for free telephone calls, XMMS to listen to music, and RSS feeders to keep abreast of the latest news.

Occasionally, you'll run across an application your firewall blocks. The reason is that the application needs to make a connection to you in order to send packets. Some internet games do this; a Google search should tell you which ports you need to open on your firewall. Simply make a TCP or UDP object which contains the required port(s) and insert a rule that allows that service from any source.

Some other services, besides internet games, may not work as you expect. As an example, I use /usr/ports/mail/fetchmail to download email from my ISP's POP3 server. After installing my firewall, I was surprised to discover that I could send email but not receive it. fetchmail had no problem connecting to the ISP to download the email; the problem was that fetchmail expected to contact sendmail before it delivered that email into my inbox. The problem disappeared after I installed that package and added this rule:

Source Destination Service Action Options Comment
Any test:lo0:ip smtp Accept used by fetchmail

I created that smtp object by starting fwbuilder, right-clicking the TCP object in the Services tree, and choosing New TCP Service from the menu. I gave it the name smtp and a Destination Port Range Start of 25. Notice that the destination is the loopback address, which only local mail delivery uses. Even if you don't use fetchmail, you should still add this rule so that your periodic scripts can successfully send mail to the superuser account.

You may be surprised to learn that ping won't work until you add another firewall rule. This is because ping uses two types of ICMP packets: one goes out from your machine (an echo request), but you'll never know your request made it to the destination unless your firewall allows an echo reply packet to come in. The current firewall rules only allow your packets to go out.

To fix this, start by creating two ICMP objects. Right-click ICMP and select New ICMP Service from the menu. Name the first object Echo Request, and enter an ICMP Type of 8 and an ICMP Code of 0. When you make the second ICMP object, name it Echo Reply and enter an ICMP type of 0 and an ICMP Code of 0.

Note: if you're curious, the types and codes for ICMP packets are available from the IANA.

Next, decide if you just want the firewall to be able to ping out or if you also wish to allow others to ping your firewall. This rule will allow all pings:

Source Destination Service Action Options Comment

Any Any echo request Accept Allow all pings
echo reply

Note: technically, you don't need to add the echo request service, as the existing firewall rules already allow your outbound packets. I've included it because it makes more sense to me when I view the rule.

If you want to be more restrictive and only allow your firewall to ping out and not allow others to ping your firewall, modify the rule so it looks like:

Source Destination Service Action Options Comment

Any firewall echo reply Accept Allow sites I've pinged to reply back

Again, you don't have to allow your echo requests out, as they already fall under the rule that allows you to access the internet. It is up to you whether to include that service in this rule.

Preparing Your Computers to Share an Internet Connection

Before creating the rules you need within fwbuilder to share your internet connection, make sure that your network is properly set up. The computer running fwbuilder needs to have a NIC, which it uses to communicate with the other computers in your home network. This NIC is separate from the hardware you use to communicate with your ISP; that might also be a NIC (in the case of a cable or DSL connection) or it might be a modem (in the case of a dial-up PPP connection). Make sure the NIC you use to communicate with your other computers is plugged into the same hub or switch as your other computers.

You also need to decide on an addressing scheme to use on your home computers. The easiest method is to choose one of the addresses from the private address ranges. These addresses always start with either 10, 172.16 up to 172.31, or 192.168.

In my example, a FreeBSD system running fwbuilder will share its internet connection with a Windows XP system. I've assigned the address to the FreeBSD system and to the XP system. Starting on the XP system:

* From Control Panel, select Network Connections (depending upon your view, Network Connections might be inside Network and Internet Connections)
* Double-click the icon that represents the NIC, then click Properties
* Double-click Internet Protocol TCP/IP
* Click the button for Use the Following IP Address, and input for the address, for the mask, and for the gateway
* Under preferred DNS server, enter the first address found in /etc/resolv.conf on the FreeBSD system, then keep clicking OK until you exit this utility

On the FreeBSD system, become the superuser and type ifconfig to determine the name of the NIC you will use to communicate with the XP system. If you have multiple NICs and are currently connected to the internet, choose the NIC that currently doesn't have an IP address. In my example, rl0 connected to the XP system and xl0 connected to the ISP:

# ifconfig
rl0: flags=8802 mtu 1500
ether 00:11:d8:ea:16:d7
media: Ethernet autoselect (10baseT/UTP)
status: active
xl0: flags=8843 mtu 1500
inet netmask 0xffffff00 broadcast
ether 00:04:75:ee:e0:21
media: Ethernet autoselect (100baseTX )
status: active
lo0: flags=8049 mtu 16384
inet netmask 0xff000000

Assigning the IP address is simple. (Replace rl0 with the FreeBSD name of your NIC):

# ifconfig rl0

Double-check that the connection is good with a ping:

# ping
(press ctrl c to end)

Configuring Shared Internet Connection on Firewall

Now that your computers are ready, it's time to add object(s) to your firewall to represent the computer(s) on your home network, recheck your rules to ensure all computers are allowed internet access, and then add a NAT rule to enable the actual connection sharing.

There are several ways to represent the computers on your network: you can create host objects for each computer, or you can create a network object to represent all of the computers on your home network. Because I have only one other computer, I've chosen to add a host object to represent my XP computer.

Right-click Hosts and select New Host from the menu. Give the host a descriptive name; I called mine XP. When asked to configure the interfaces manually, add the IP address and subnet mask for the computer; you can leave the rest of the parameters empty. Just make sure that you have added the address. In my case, I entered and with a label of my_network.

Next, review your current firewall rules and ask yourself, should only the firewall computer be able to do this or should all my computers? For example, I should add the XP computer as a Source for the rule that allows access to the internet, but I should leave the firewall loopback as the only Destination for the SSH rule that allows me to install a firewall policy. My complete rulebase resembles Figure 1.

Figure 1. My complete rulebase (Click for full-size image)

Creating the NAT rule is easy. In the right frame, click on the NAT tab. Your firewall rules should disappear (don't worry, they are still available from the Policy tab). You should see an empty frame, as you haven't created any NAT rules yet. Right-click and choose Insert Rule. Notice that NAT rules have different fields than regular firewall rules:

Original Src default value of Any
Original Dst default value of Any
Original Svr default value of Any
Translated Src default value of Original
Translated Dst default value of Original
Translated Svr default value of Original
Comment empty by default

This is what you want to happen: when your other computer needs to access the internet, it should go through the firewall and then out its other interface to the ISP. Create a rule that does that by changing two of the default values:

Original Src host object
Translated Src external interface of firewall

In my case, I called my host object XP and my external firewall interface ISP.

Don't forget to install your policy when you finish creating your NAT rule. Then try to access a website from your other computer. Assuming you remembered to add that host to your internet access rule, everything should just work.

Hint: if you'd like to see your NAT rules using pfctl, type pfctl -s rules as the superuser. To see your NAT translations or your current NAT connections, type pfctl -s state.

Fancy Stuff: Logging

There are several things you need to do if you'd like to view your firewall logs. First, make sure that you've chosen the Logging On Action in at least one of your firewall rules.

Hint: choose wisely when deciding which rules to log; if you log everything, you will have to wade through very large logfiles! If you only want to log when you think there is a problem--for example, one of your applications doesn't seem to work through the firewall--enable logging temporarily for your internet access rule until you've figured out the problem.

Next, make sure that you have pflog loaded:

# kldstat | grep pflog
7 1 0xc52d4000 2000 pflog.ko

If you see this, pflog is good to go. If you only get your prompt back, load the module:

# kldload pflog

... and add these lines to /etc/rc.conf to reload the module at boot time:


If that logfile doesn't already exist, create it:

# touch /var/log/pflog

You should now be able to start pflog:

# /etc/rc.d/pflog start
Starting pflog.

# /etc/rc.d/pflog status
pflog is running as pid 95363.

The logfile that this creates is not a text file, meaning you can't read it directly or use a utility such as tail -f to watch the log.

Instead, use tcpdump:

# tcpdump -n -e -ttt -r /var/log/pflog

To view the file as it grows, use:

# tcpdump -n -e -ttt -i pflog0

Admittedly, those are some pretty long commands just to view a log. This is an excellent time to create some key bindings. These bindings work from a terminal, so I run them from Alt-F2 instead of the GUI. The first command will bind Ctrl-L to the command that reads the logfile, and the second command will bind Ctrl-g to the command that watches the log as it grows:

# bindkey -s "^L" "tcpdump -n -e -ttt -r /var/log/pflog"
# bindkey -s "^g" "tcpdump pn -e -ttt -i pflog0"

I find that pressing Ctrl-L or Ctrl-g is much quicker. If you prefer to have your bindings work in your GUI, install and configure xbindkeys_config.

Hint: BSD Hacks has more directions for creating shell, terminal, and GUI bindings.
Fancier Stuff: Advanced Logging

Even with key bindings, tcpdump can still be a little inconvenient; it displays your log entries in pure text. There currently aren't any GUI pflog entry readers, but you can hack an HTML equivalent that will allow you to view your logs in a browser. Start by installing the pflogx utility:

# cd /usr/ports/sysutils/pflogx
# make install

Note that I've chosen to install the port, not the binary package, and that I didn't use the clean target to make. This is because I want to use an .xls file that doesn't come with the package. make clean will delete it. Also, during the install, you'll see a menu asking if you want to use Expat; selecting this option will give you the ability to merge logfiles.

Once installed, check out the installed .xsl files:

# ls pflogx/work/xsl
export_csv.xsl export_xhtml.xsl last_date.xsl
export_html.xsl first_date.xsl

The /usr/local/share/doc/pflogx/README holds directions for using pflogx and descriptions of each .xsl file.

Here is an example to get you started. Using the logfile as input (-i), create an XML file as output (-o):

# pflogx -i /var/log/pflog -o ~/log.xml

Copy export_html.xsl to your home directory:

# cp /usr/ports/sysutils/pflogx/work/pflogx/xsl/export_html.xsl ~

Open ~/log.xml in your favourite text editor. The first line should say:

Right after that line, add:

After you save your change, type the full path to log.xml into your browser. You should see something like Figure 2.

Figure 2. An HTML firewall log report (Click for full-size image)
Suggested Reading

I've barely scratched the surface of pf's features. More advanced users can explore how to integrate these features into fwbuilder. Here is some reading material to get you started:

* fwbuilder User Guide (PDF)
* pf FAQ
* Peter Hansteen's pf Guide

If you right-click your firewall object and choose Edit, then Firewall Settings, you'll find many interesting tunables. If you wish to implement some pf features not currently supported by fwbuilder, such as altq or carp, experiment with adding the required lines to the Prolog/Epilog tab.