In the last article, I created a ruleset that told ipfw to allow
responses to the connection requests I had made to the Internet and to
also allow for DNS name resolution. In this article, I'd like to
start fine-tuning this ruleset and begin to test its behavior by using
the built-in logging facilities available to ipfw.
As my ruleset now stands, I'll enjoy my Internet connection until my DHCP lease runs out. At that point, everything will stop as I haven't created any rules that will allow me to receive a new DHCP lease. To know which rules will accomplish this, it is helpful to have a basic understanding of how DHCP works.
DHCP uses UDP packets, meaning I can't rely on my dynamic rules and the state table; instead, I will have to explicitly allow UDP packets between my machine and my provider's DHCP server. DHCP also uses two port numbers: My DHCP client uses port 68 and my provider's DHCP server uses port 67.
To see how the DHCP process actually works, let's take a look at my current DHCP lease:
more /var/db/dhclient.leases
lease {
interface "ed0";
fixed-address 24.141.119.162;
option subnet-mask 255.255.252.0;
option time-offset -18000;
option routers 24.141.116.1;
option domain-name-servers 24.226.1.90,24.226.1.20,24.2.9.34;
option host-name "my_hostname";
option domain-name "my_domainname";
option broadcast-address 255.255.255.255;
option dhcp-lease-time 604800;
option dhcp-message-type 5;
option dhcp-server-identifier 24.226.1.41;
renew 2 2001/5/15 13:12:11;
rebind 5 2001/5/18 04:12:11;
expire 6 2001/5/19 01:12:11;
}
Note that the DHCP server has provided an IP address, subnet mask, default gateway address, the IP addresses of three DNS servers, my hostname, and the IP address of the DHCP server that provided this lease. Since a DHCP lease is indeed a "lease," meaning I can't keep this information indefinitely, the last three lines deal with how my DHCP client will renew this lease information.
|
Also in FreeBSD Basics: |
The "renew" line tells my DHCP client when it should go out and try to
renew its lease. Note that this time is well before the actual
expiration date listed on the "expire" line. On 2001/5/15 at 13:12:11, my
computer will attempt to send out UDP packets to port 67 on the DHCP
server 24.226.1.41, so I should add a rule that will allow these packets to
go out. Assuming the DHCP server receives my packets, it should respond
that it is willing to renew my lease, and will send this information in
UDP packets to port 68 on my computer. Accordingly, I'll also want to add
a rule so that ipfw will allow in the response packets.
If I don't add these rules to my ipfw ruleset, or if for some reason the
DHCP server does not respond to my renewal request, the "rebind" line will
kick in on 2001/5/18 at 04:12:11. At this point my DHCP client is starting
to get a bit worried about its upcoming lease expiration and will send out
some more UDP packets destined for port 67. However, this time they will
not be addressed to a particular DHCP server, but will instead be sent
to the broadcast address of 255.255.255.255 in the hope that any DHCP
server will respond.
If no DHCP server responds, my lease will expire on 2001/5/19 at
01:12:11, meaning my DCHP client is no longer sure that it is allowed to
continue to use its lease information. At this point, several things may
occur. The client will still try to contact a DHCP server, so will
continue to send out broadcasts to UDP port 67. It will also try
pinging the default gateway address to see if its IP address is still
valid, meaning it will be sending out ICMP type 8 code 0 packets and
hoping to receive back ICMP type 0 code 0 packets. If worst comes to worst,
and my client actually ends up with an invalid IP address, any responses
that come back from a DHCP server will have to be received as broadcasts,
that is, be addressed to port 68 on 255.255.255.255.
Now, knowing all this, what should I add to my ruleset? Before I add
anything, I should first double-check what rules I currently have
as order is starting to become increasingly important. The more rules I
add to my ruleset, the greater the possibility that an earlier rule will
override my new rule. Also, the trick to a good ruleset is to only allow
the traffic you wish using as few rules as possible. Your firewall may
still work if you add too many rules, but you will end up putting an
unnecessary burden on ipfw as it has to read more rules before it finds
the one that applies to a packet, and you will also create more confusion
for yourself when you can't figure out which of your many rules is
preventing the behavior you expect.
|
I'll become the superuser and issue the ipfw show command to check my
current rules:
su
Password:
ipfw show
00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 0 0 allow tcp from any to any keep-state setup
00400 0 0 allow udp from 24.226.1.90 53 to any in recv ed0
00401 0 0 allow udp from 24.226.1.20 53 to any in recv ed0
00402 0 0 allow udp from 24.2.9.34 53 to any in recv ed0
00403 0 0 allow udp from any to any out
65535 0 0 deny ip from any to any
Because I need to allow UDP packets, I'll want to specify the DHCP port
numbers and the IP address of my DHCP server. As the superuser, I'll
consider adding the following lines to my /etc/ipfw.rules file:
#allow DHCP
add 00500 allow udp from any 68 to 24.226.1.41 67 out via ed0
add 00501 allow udp from 24.226.1.41 67 to any 68 in via ed0
These should be the bare minimum rules that will allow my DHCP client to
renew its lease with the DHCP server 24.226.1.41. Whether more rules will
be required will vary according to the dependability of that DHCP server. If the
DHCP server always responds to my renewal requests, I won't have to resort
to sending out UDP broadcasts, pinging my default gateway, or receiving
UDP broadcasts. If my DHCP server is not so dependable, I might have to
also add the following rules:
add 00502 allow udp from any 68 to 255.255.255.255 67 out via ed0
add 00503 allow udp from any 67 to 255.255.255.255 68 in via ed0
I won't immediately add rules 00502 and 00503, though, as up to this point, my DHCP server has been quite dependable. I have made a mental note to myself to remember to keep these rules in mind, just in case my provider ever has problems with this DHCP server or actually changes the IP address of my DHCP server.
Before I save my changes, I'll compare rules 00500 and 00501 to the rest of my ruleset to ensure there aren't any conflicts or overlaps. I immediately notice an overlap between rules 00403 and 00500:
add 00403 allow udp from any to any out
add 00500 allow udp from any 68 to 24.226.1.41 67 out via ed0
Since rule 00403 already allows "any" UDP packet to go out of my computer, the more specific rule of only sending out UDP packets from port 68 will never be read. At this point, I need to make a choice between creating a minimum number of rules or using a maximum amount of paranoia and responsibility.
I originally added rule 00403 when I created the rules to allow DNS resolution. (See last week's article.) If I decide to remove rule 00403, I'll have to replace it with three more rules that will allow UDP packets to be sent out to the three DNS servers. Also, if I ever need to access any other type of server that requires me to send it a UDP packet, I'll have to create an extra rule to do so. This will result in adding extra rules, and thus extra overhead, to my ipfw ruleset, instead of using one all-encompassing rule.
This goes against the philosophy of using a minimum amount of rules, but I also need to look at the implications of keeping that one all-encompassing rule. There's no security risk to me if I send out UDP packets, as long as I restrict whom I'm willing to accept UDP packets from. For example, rule 00403 allows me to send out any UDP packet, but rules 00400, 00401, 00402, and 00501 ensure I'll only accept UDP packets from my provider's three DNS servers and one DHCP server. This seems to be an acceptable policy for my standalone FreeBSD computer.
However, I would have to rethink this policy if I ever put any clients behind my FreeBSD firewall. For example, Microsoft clients send out an inordinate amount of UDP packets to advertise their shared resources. It would be both irresponsible of me and a security risk to allow those packets to leave my network by being allowed out through my firewall. In this case, I would have to use the overhead of extra rules to ensure that I was only sending out necessary UDP packets and I would have to remove the rule that allows any UDP packet to leave my firewall.
For now, I'll keep rule 00403 as I'm currently only protecting this standalone FreeBSD computer. I might as well delete rule 00500 as it will never be read. My change to this file now looks like this:
#allow DHCP
add 00501 allow udp from 24.226.1.41 67 to any 68 in via ed0
|
I'll save my change and test it using killall init. Once I've logged
back in, I'll become the superuser and see what happened:
su
Password:
ipfw show
00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 0 0 allow tcp from any to any keep-state setup
00400 8 1322 allow udp from 24.226.1.90 53 to any in recv ed0
00401 0 0 allow udp from 24.226.1.20 53 to any in recv ed0
00402 0 0 allow udp from 24.2.9.34 53 to any in recv ed0
00403 8 469 allow udp from any to any out
00501 4 1592 allow udp from 24.226.1.41 67 to any 68 in recv ed0
65535 29 8591 deny ip from any to any
It looks like I received eight UDP packets from the DNS server 24.226.1.90 and
four UDP packets from the DHCP server 24.226.1.41. Now I'll take a look at my
DHCP lease:
more /var/db/dhclient.leases<snip to just show bottom 3 lines>renew 3 2001/5/16 07:46:25; rebind 5 2001/5/18 08:50:46; expire 6 2001/5/19 01:12:14;
When I issued the killall init command, I also successfully contacted
the DHCP server and renewed my lease, so it looks my DHCP rule is
successful.
Now, let's take a look at allowing some ICMP, as my ruleset is currently denying all ICMP packets. If you remember from Examining ICMP Packets, denying all ICMP is a bad thing as it will break Path-MTU Discovery and will prevent Source Quench messages. You'll also remember that ICMP uses both "types" and "codes" to specify the actual ICMP message.
When creating an ipfw rule that refers to ICMP, you can only specify the ICMP "type," not the associated "code." I'll become the superuser and add
the following lines to my /etc/ipfw.rules file:
#allow some icmp types (codes not supported)
###########allow path-mtu in both directions
add 00600 allow icmp from any to any icmptypes 3
###########allow source quench in and out
add 00601 allow icmp from any to any icmptypes 4
While I'm at it, I should consider whether I want to be able to ping
hosts outside of my network, or run the traceroute command. Because I want
to be able to do both and receive responses, but I don't want anyone
on the Internet to try to ping or "traceroute" me, I'll add these rules:
###########allow me to ping out and receive response back
add 00602 allow icmp from any to any icmptypes 8 out
add 00603 allow icmp from any to any icmptypes 0 in
###########allow me to run traceroute
add 00604 allow icmp from any to any icmptypes 11 in
Remember that ICMP type 8 is an echo request and ICMP type 0 is an echo reply. Since I've only allowed echo requests out and echo replies in, I can ping out but no one can ping me.
When I run traceroute, I send out UDP packets that I've already allowed using rule 00403. However, if I want to receive any responses back, I have
to accept back in ICMP type 11 packets.
That looks pretty good so I'll save my changes and reset my ipfw counters using the ipfw zero command. I'll then do a killall init and once I've logged back in I'll try to do a ping and a traceroute:
ping www.freebsd.org
PING freefall.freebsd.org (216.136.204.21): 56 data bytes
64 bytes from 216.136.204.21: icmp_seq=0 ttl=239 time=85.250 ms
64 bytes from 216.136.204.21: icmp_seq=1 ttl=239 time=88.338 ms
64 bytes from 216.136.204.21: icmp_seq=2 ttl=239 time=83.757 ms
^C
--- freefall.freebsd.org ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 83.757/85.782/88.338/1.908 ms
traceroute www.freebsd.org
traceroute to freefall.freebsd.org (216.136.204.21), 30 hops max, 40 byte packets
1 10.69.4.1 (10.69.4.1) 8.678 ms 8.739 ms 10.055 ms
2 d226-12-1.home.cgocable.net (24.226.12.1) 9.800 ms 10.642 ms 7.876 ms
3 cgowave-0-158.cgocable.net (24.226.0.158) 25.910 ms 15.288 ms 13.693 ms
4 cgowave-busy-core.cgocable.net (24.226.1.1) 26.982 ms 16.521 ms 12.376 ms
5 cgowave-0-202.cgocable.net (24.226.0.202) 14.372 ms 14.224 ms 13.728 ms
6 216.197.153.65 (216.197.153.65) 14.112 ms 13.544 ms 42.612 ms
7 c1-pos8-0.bflony1.home.net (24.7.74.29) 15.093 ms 22.387 ms 18.530 ms
8 c1-pos1-0.hrfrct1.home.net (24.7.65.253) 25.953 ms 26.703 ms 26.514 ms
9 c1-pos3-0.nycmny1.home.net (24.7.69.2) 26.279 ms 29.810 ms 38.940 ms
10 * ibr02-p1-0.jrcy01.exodus.net (24.7.70.122) 32.121 ms 38.211 ms
11 bbr02-g5-0.jrcy01.exodus.net (216.32.223.130) 34.239 ms 34.815 ms 37.106 ms
12 bbr01-p2-0.okbr01.exodus.net (216.32.132.109) 37.643 ms 36.883 ms 36.201 ms
13 216.34.183.66 (216.34.183.66) 37.624 ms 39.455 ms 40.243 ms
14 bbr01-p0-0.snva03.exodus.net (206.79.9.85) 81.494 ms 82.421 ms 83.230 ms
15 64.15.192.34 (64.15.192.34) 79.431 ms 80.981 ms 115.289 ms
16 bbr02-p4-0.sntc05.exodus.net (209.185.9.70) 81.993 ms 99.964 ms 82.169 ms
17 dcr01-g6-0.sntc05.exodus.net (64.56.192.19) 81.324 ms 81.603 ms 80.146 ms
18 g2-1.bas1-m.sc5.yahoo.com (64.56.207.146) 81.867 ms 100.628 ms 94.995 ms
19 freefall.freebsd.org (216.136.204.21) 104.100 ms 95.821 ms 85.909 ms
So far, so good. I'll now become the superuser and doublecheck which rules were used:
su
Password:
ipfw show
00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 0 0 allow tcp from any to any keep-state setup
00400 29 5847 allow udp from 24.226.1.90 53 to any in recv ed0
00401 2 163 allow udp from 24.226.1.20 53 to any in recv ed0
00402 3 397 allow udp from 24.2.9.34 53 to any in recv ed0
00403 93 4712 allow udp from any to any out
00501 0 0 allow udp from 24.226.1.41 67 to any 68 in recv ed0
00600 3 168 allow icmp from any to any icmptype 3
00601 0 0 allow icmp from any to any icmptype 4
00602 3 252 allow icmp from any to any out icmptype 8
00603 3 252 allow icmp from any to any in icmptype 0
00604 53 2968 allow icmp from any to any in icmptype 11
65535 29 8591 deny ip from any to any
You can see those three echo request packets (rule 00602) and the three echo reply
packets (rule 00603) used by the ping utility. You can also see that it
took 53 ICMP type 11 packets to respond to the traceroute (rule 00604). It also looks like rule 00600 allowed three ICMP type 3 packets. However, I can't
tell "why" I received those three Destination Unreachable messages as I
wasn't able to specify an associated "code" in my rule.
We've actually covered a fair bit of ground in this article, so let's wait until next when we look at logging and do some further testing on the firewall's behavior.
Dru Lavigne is a network and systems administrator, IT instructor, author and international speaker. She has over a decade of experience administering and teaching Netware, Microsoft, Cisco, Checkpoint, SCO, Solaris, Linux, and BSD systems. A prolific author, she pens the popular FreeBSD Basics column for O'Reilly and is author of BSD Hacks and The Best of FreeBSD Basics.
Read more FreeBSD Basics columns.
Return to the BSD DevCenter.
Copyright © 2009 O'Reilly Media, Inc.