This article taken from
http://www.acme.com/firewall.html
FreeBSD 3.x and 4.x make it very easy to set up a rule-based packet filtering firewall.
You can protect just one host, or an entire network. You can easily add Network Address Translation too,
so that you can connect up your whole internal network via only one IP address from the outside.
There are three parts to this.
First, you have to make a few changes to your kernel. This isn't as hard as it sounds. Su to root,
cd /usr/src/sys/i386/conf, and copy GENERIC to a new file. Let's call it ACME.
This will be your new kernel config. Here are the changes you need to make:
*** GENERIC Tue Sep 14 15:53:30 1999
--- ACME Thu Nov 18 16:27:03 1999
***************
*** 22,28 ****
cpu "I486_CPU"
cpu "I586_CPU"
cpu "I686_CPU"
! ident GENERIC
maxusers 32
options MATH_EMULATE #Support for x87 emulation
--- 22,28 ----
cpu "I486_CPU"
cpu "I586_CPU"
cpu "I686_CPU"
! ident ACME
maxusers 32
options MATH_EMULATE #Support for x87 emulation
***************
*** 211,213 ****
--- 211,218 ----
# The number of devices determines the maximum number of
# simultaneous BPF clients programs runnable.
pseudo-device bpfilter 1 #Berkeley packet filter
+
+ # Enable ipfw and natd.
+ options IPFIREWALL
+ options IPFIREWALL_VERBOSE
+ options IPDIVERT
In other words, change the ident and add the firewall options. If you're running FreeBSD 4.x and you want to set up an IPv6 firewall too, you should also add IPV6FIREWALL and IPV6FIREWALL_VERBOSE. Note that the regular IPv4 firewall does not affect IPv6 packets at all. If you set up a v4 firewall and not a v6 firewall, all v6 packets will be allowed through. You really need to do both, or disable IPv6 altogether.
After setting up the config, build and install the new kernel:
# /usr/sbin/config ACME
# cd ../../compile/ACME
# make depend
# make
# make install
Second, edit /etc/rc.conf and add these defines to the end:
# Enable ipfw.
firewall_enable="YES"
firewall_type="type" # see rc.firewall for what goes here
firewall_quiet="NO"
The firewall_type should be "client" to secure a single stand-alone machine, or "simple" for a gateway guarding an internal network.
If you want to do Network Address Translation, add these defines too:
# Enable natd.
natd_enable="YES"
natd_interface="fxp0" # your public network interface
natd_flags="-m" # preserve port numbers if possible
And if you're running FreeBSD 4.x and also want to enable the separate IPv6 firewall, add these:
# Enable ip6fw.
ipv6_firewall_enable="YES"
ipv6_firewall_type="type" # see rc.firewall6 for what goes here
ipv6_firewall_quiet="NO"
Third, you have to make a few edits in rc.firewall. The comments there explain what is needed, it's real easy. Look for the section with rules for your firewall type, either "client" or "simple". At the beginning of the section there will be a few defines for your IP numbers, network interfaces, etc.; fill these in. If you're setting up the IPv6 firewall too, make similar edits in rc.firewall6.
That's it, for a starter setup anyway. Reboot and you should be up and running.
Important Troubleshooting Note
FreeBSD's firewall facility is designed so that it's secure by default.
If you enable it and then don't add any rules, it drops ALL packets. This means if you mess something up in your firewall setup, you may find that you can't get to your machine through the network to fix it. You will have to log in via the system console.
This happened to me once during debugging. It's no big deal as long as you understand what's going on. It's easy to recover from as long as you have access to the console; just edit /etc/rc.conf, change firewall_type to "open" or just comment out the firewall lines, and boot again. But do be careful if you're tweaking your firewall setup over the net.
FTP Note
Firewall setups like this prevent regular FTP from working. This is really FTP's fault. It's an old-fashioned and over-complicated protocol, which requires the server to initiate a callback connection to the client. Since firewalls prevent new connections from the outside (except for a few protocols such as email), FTP fails.
There is a workaround - use FTP's "passive" mode, which basically tells it to stick to a regular client-server protocol. Every time you run ftp, just give the "passive" command. With recent versions of the FTP client, you can make this the default by setting the environment variable FTP_PASSIVE_MODE to "yes". Unfortunately, I don't know of a way to configure web browsers to use passive mode when they do FTP.
The other workaround, of course, is to avoid FTP and just use HTTP instead.
More Advanced Topics
Once you have a firewall set up, you may find you don't like the canned rule sets. If so, it's easy to make your own. The first thing you might do is allow ssh connections through. (ssh is a secure replacement for telnet/rlogin; you can fetch it from http://www.openssh.com/.) Where your ruleset says "Allow setup of incoming email", add a similar rule for ssh by changing the port number 25 to a 22.
Or, you can go whole hog and make an entirely new ruleset. I ended up making two new ones, called acme-solo and acme-net, which are souped-up versions of the default client and simple rulesets. Here's the code:
[Aa][Cc][Mm][Ee]-[Ss][Oo][Ll][Oo])
############
# ACME single-machine custom firewall setup. Protects somewhat
# against the outside world.
############
# Set this to your ip address.
ip="192.100.666.1"
# Allow anything outbound from this address.
${fwcmd} add pass all from ${ip} to any out
# Deny anything outbound from other addresses.
${fwcmd} add deny log all from any to any out
# Allow TCP through if setup succeeded.
${fwcmd} add pass tcp from any to any established
# Allow IP fragments to pass through.
${fwcmd} add pass all from any to any frag
# Allow all IPv6 packets through - they are handled by the separate
# ipv6 firewall rules in rc.firewall6.
${fwcmd} add pass ipv6 from any to any
# Allow inbound ftp, ssh, email, tcp-dns, http, https, pop3, pop3s.
${fwcmd} add pass tcp from any to ${ip} 21 setup
${fwcmd} add pass tcp from any to ${ip} 22 setup
${fwcmd} add pass tcp from any to ${ip} 25 setup
${fwcmd} add pass tcp from any to ${ip} 53 setup
${fwcmd} add pass tcp from any to ${ip} 80 setup
${fwcmd} add pass tcp from any to ${ip} 443 setup
${fwcmd} add pass tcp from any to ${ip} 110 setup
${fwcmd} add pass tcp from any to ${ip} 995 setup
# Deny inbound auth, netbios, and ldap without logging.
${fwcmd} add deny tcp from any to ${ip} 113 setup
${fwcmd} add deny tcp from any to ${ip} 139 setup
${fwcmd} add deny tcp from any to ${ip} 389 setup
# Deny some chatty UDP broadcast protocols without logging.
${fwcmd} add deny udp from any 137 to any
${fwcmd} add deny udp from any to any 137
${fwcmd} add deny udp from any 138 to any
${fwcmd} add deny udp from any 513 to any
${fwcmd} add deny udp from any 525 to any
# Allow inbound DNS and NTP replies. This is somewhat of a hole,
# since we're looking at the incoming port number, which can be
# faked, but that's just the way DNS and NTP work.
${fwcmd} add pass udp from any 53 to ${ip}
${fwcmd} add pass udp from any 123 to ${ip}
# Allow inbound DNS queries.
${fwcmd} add pass udp from any to ${ip} 53
# Deny inbound NTP queries without logging.
${fwcmd} add deny udp from any to ${ip} 123
# Allow traceroute to function, but not to get in.
${fwcmd} add unreach port udp from any to ${ip} 33435-33524
# Allow some inbound icmps - echo reply, dest unreach, source quench,
# echo, ttl exceeded.
${fwcmd} add pass icmp from any to any icmptypes 0,3,4,8,11
# Everything else is denied and logged.
${fwcmd} add deny log all from any to any
;;
[Aa][Cc][Mm][Ee]-[Nn][Ee][Tt])
############
# ACME network custom firewall setup. The assumption here is that
# the internal hosts are trusted, and can do anything they want.
# The only thing we have to be careful about is what comes in over
# the outside interface. So, you'll see a lot of "in via ${oif}"
# clauses here.
############
# Set these to your outside interface network and netmask and ip.
oif="fxp0"
onet="63.197.1234.0"
omask="255.255.255.0"
oip="63.197.1234.1"
# Set these to your inside interface network and netmask and ip.
iif="fxp1"
inet="192.100.666.0"
imask="255.255.255.0"
iip="192.100.666.1"
# Stop spoofing
${fwcmd} add deny log all from ${inet}:${imask} to any in via ${oif}
${fwcmd} add deny log all from ${onet}:${omask} to any in via ${iif}
# Stop RFC1918 nets on the outside interface
${fwcmd} add deny log all from any to 10.0.0.0/8 via ${oif}
${fwcmd} add deny log all from any to 172.16.0.0/12 via ${oif}
${fwcmd} add deny log all from any to 192.168.0.0/16 via ${oif}
# Stop draft-manning-dsua-03.txt (1 May 2000) nets (includes RESERVED-1,
# DHCP auto-configuration, NET-TEST, MULTICAST (class D), and class E)
# on the outside interface
${fwcmd} add deny log all from any to 0.0.0.0/8 via ${oif}
${fwcmd} add deny log all from any to 169.254.0.0/16 via ${oif}
${fwcmd} add deny log all from any to 192.0.2.0/24 via ${oif}
${fwcmd} add deny log all from any to 224.0.0.0/4 via ${oif}
${fwcmd} add deny log all from any to 240.0.0.0/4 via ${oif}
# Network Address Translation. This rule is placed here deliberately
# so that it does not interfere with the surrounding address-checking
# rules. If for example one of your internal LAN machines had its IP
# address set to 192.0.2.1 then an incoming packet for it after being
# translated by natd(8) would match the `deny' rule above. Similarly
# an outgoing packet originated from it before being translated would
# match the `deny' rule below.
case ${natd_enable} in
[Yy][Ee][Ss])
if [ -n "${natd_interface}" ]; then
${fwcmd} add divert natd all from any to any via ${natd_interface}
fi
;;
esac
# Stop RFC1918 nets on the outside interface
${fwcmd} add deny log all from 10.0.0.0/8 to any via ${oif}
${fwcmd} add deny log all from 172.16.0.0/12 to any via ${oif}
${fwcmd} add deny log all from 192.168.0.0/16 to any via ${oif}
# Stop draft-manning-dsua-03.txt (1 May 2000) nets (includes RESERVED-1,
# DHCP auto-configuration, NET-TEST, MULTICAST (class D), and class E)
# on the outside interface
${fwcmd} add deny log all from 0.0.0.0/8 to any via ${oif}
${fwcmd} add deny log all from 169.254.0.0/16 to any via ${oif}
${fwcmd} add deny log all from 192.0.2.0/24 to any via ${oif}
${fwcmd} add deny log all from 224.0.0.0/4 to any via ${oif}
${fwcmd} add deny log all from 240.0.0.0/4 to any via ${oif}
# Allow anything on the internal net
${fwcmd} add pass all from any to any via ${iif}
# Allow anything outbound from this net.
${fwcmd} add pass all from ${onet}:${omask} to any out via ${oif}
# Deny anything outbound from other nets.
${fwcmd} add deny log all from any to any out via ${oif}
# Allow TCP through if setup succeeded.
${fwcmd} add pass tcp from any to any established
# Allow IP fragments to pass through.
${fwcmd} add pass all from any to any frag
# Allow all IPv6 packets through - they are handled by the separate
# ipv6 firewall rules in rc.firewall6.
${fwcmd} add pass ipv6 from any to any
# Allow inbound ftp, ssh, email, tcp-dns, http, https, pop3, pop3s.
${fwcmd} add pass tcp from any to ${oip} 21 setup in via ${oif}
${fwcmd} add pass tcp from any to ${oip} 22 setup in via ${oif}
${fwcmd} add pass tcp from any to ${oip} 25 setup in via ${oif}
${fwcmd} add pass tcp from any to ${oip} 53 setup in via ${oif}
${fwcmd} add pass tcp from any to ${oip} 80 setup in via ${oif}
${fwcmd} add pass tcp from any to ${oip} 443 setup in via ${oif}
${fwcmd} add pass tcp from any to ${oip} 110 setup in via ${oif}
${fwcmd} add pass tcp from any to ${oip} 995 setup in via ${oif}
# Deny inbound auth, netbios, and ldap without logging.
${fwcmd} add deny tcp from any to ${oip} 113 setup in via ${oif}
${fwcmd} add deny tcp from any to ${oip} 139 setup in via ${oif}
${fwcmd} add deny tcp from any to ${oip} 389 setup in via ${oif}
# Deny some chatty UDP broadcast protocols without logging.
${fwcmd} add deny udp from any 137 to any in via ${oif}
${fwcmd} add deny udp from any to any 137 in via ${oif}
${fwcmd} add deny udp from any 138 to any in via ${oif}
${fwcmd} add deny udp from any 513 to any in via ${oif}
${fwcmd} add deny udp from any 525 to any in via ${oif}
# Allow inbound DNS and NTP replies. This is somewhat of a hole,
# since we're looking at the incoming port number, which can be
# faked, but that's just the way DNS and NTP work.
${fwcmd} add pass udp from any 53 to ${oip} in via ${oif}
${fwcmd} add pass udp from any 123 to ${oip} in via ${oif}
# Allow inbound DNS queries.
${fwcmd} add pass udp from any to ${oip} 53 in via ${oif}
# Deny inbound NTP queries without logging.
${fwcmd} add deny udp from any to ${oip} 123 in via ${oif}
# Allow traceroute to function, but not to get in.
${fwcmd} add unreach port udp from any to ${oip} 33435-33524 in via ${oif}
# Allow some inbound icmps - echo reply, dest unreach, source quench,
# echo, ttl exceeded.
${fwcmd} add pass icmp from any to any in via ${oif} icmptypes 0,3,4,8,11
# Broadcasts are denied and not logged.
${fwcmd} add deny all from any to 255.255.255.255
# Everything else is denied and logged.
${fwcmd} add deny log all from any to any
And here's the version for rc.firewall6:
[Aa][Cc][Mm][Ee]-[Ss][Oo][Ll][Oo])
############
# ACME single-machine custom firewall setup. Protects somewhat
# against the outside world.
############
# Set this to your ipv6 address.
ip="3ffe:505:2:1::1"
# Allow anything outbound from this address.
${fw6cmd} add pass all from ${ip} to any out
# Deny anything outbound from other addresses.
${fw6cmd} add deny log all from any to any out
# Allow TCP through if setup succeeded.
${fw6cmd} add pass tcp from any to any established
# Allow IP fragments to pass through.
${fw6cmd} add pass all from any to any frag
# Allow inbound ftp, ssh, email, tcp-dns, http, https, pop3, pop3s.
${fw6cmd} add pass tcp from any to ${ip} 21 setup
${fw6cmd} add pass tcp from any to ${ip} 22 setup
${fw6cmd} add pass tcp from any to ${ip} 25 setup
${fw6cmd} add pass tcp from any to ${ip} 53 setup
${fw6cmd} add pass tcp from any to ${ip} 80 setup
${fw6cmd} add pass tcp from any to ${ip} 443 setup
${fw6cmd} add pass tcp from any to ${ip} 110 setup
${fw6cmd} add pass tcp from any to ${ip} 995 setup
# Deny inbound auth, netbios, and ldap without logging.
${fw6cmd} add deny tcp from any to ${ip} 113 setup
${fw6cmd} add deny tcp from any to ${ip} 139 setup
${fw6cmd} add deny tcp from any to ${ip} 389 setup
# Deny some chatty UDP broadcast protocols without logging.
${fw6cmd} add deny udp from any 137 to any
${fw6cmd} add deny udp from any to any 137
${fw6cmd} add deny udp from any 138 to any
${fw6cmd} add deny udp from any 513 to any
${fw6cmd} add deny udp from any 525 to any
# Allow inbound DNS and NTP replies. This is somewhat of a hole,
# since we're looking at the incoming port number, which can be
# faked, but that's just the way DNS and NTP work.
${fw6cmd} add pass udp from any 53 to ${ip}
${fw6cmd} add pass udp from any 123 to ${ip}
# Allow inbound DNS queries.
${fw6cmd} add pass udp from any to ${ip} 53
# Deny inbound NTP queries without logging.
${fw6cmd} add deny udp from any to ${ip} 123
# Allow traceroute to function, but not to get in.
${fw6cmd} add unreach port udp from any to ${ip} 33435-33524
# Allow some inbound icmps - dest unreach, too big, time exceeded,
# echo, echo reply, neighbor solicit, neighbor ad.
${fw6cmd} add pass ipv6-icmp from any to any icmptypes 1,2,3,128,129,135,136
# Everything else is denied and logged.
${fw6cmd} add deny log all from any to any
;;
[Aa][Cc][Mm][Ee]-[Nn][Ee][Tt])
############
# ACME network custom firewall setup. The assumption here is that
# the internal hosts are trusted, and can do anything they want.
# The only thing we have to be careful about is what comes in over
# the outside interface. So, you'll see a lot of "in via ${oif}"
# clauses here.
############
# Set these to your outside interface network and prefixlen and ip.
oif="ed0"
onet="3ffe:505:2:1::"
oprefixlen="64"
oip="3ffe:505:2:1::1"
# Set these to your inside interface network and prefixlen and ip.
iif="ed1"
inet="3ffe:505:2:2::"
iprefixlen="64"
iip="3ffe:505:2:2::1"
# Stop spoofing.
${fw6cmd} add deny log all from ${inet}/${iprefixlen} to any in via ${oif}
${fw6cmd} add deny log all from ${onet}/${oprefixlen} to any in via ${iif}
# Stop site-local on the outside interface
${fw6cmd} add deny log all from fec0::/10 to any via ${oif}
${fw6cmd} add deny log all from any to fec0::/10 via ${oif}
# Disallow "internal" addresses to appear on the wire.
${fw6cmd} add deny log all from ::ffff:0.0.0.0/96 to any via ${oif}
${fw6cmd} add deny log all from any to ::ffff:0.0.0.0/96 via ${oif}
# Disallow packets to malicious IPv4 compatible prefix.
${fw6cmd} add deny log all from ::224.0.0.0/100 to any via ${oif}
${fw6cmd} add deny log all from any to ::224.0.0.0/100 via ${oif}
${fw6cmd} add deny log all from ::127.0.0.0/104 to any via ${oif}
${fw6cmd} add deny log all from any to ::127.0.0.0/104 via ${oif}
${fw6cmd} add deny log all from ::0.0.0.0/104 to any via ${oif}
${fw6cmd} add deny log all from any to ::0.0.0.0/104 via ${oif}
${fw6cmd} add deny log all from ::255.0.0.0/104 to any via ${oif}
${fw6cmd} add deny log all from any to ::255.0.0.0/104 via ${oif}
${fw6cmd} add deny log all from ::0.0.0.0/96 to any via ${oif}
${fw6cmd} add deny log all from any to ::0.0.0.0/96 via ${oif}
# Disallow packets to malicious 6to4 prefix.
${fw6cmd} add deny log all from 2002:e000::/20 to any via ${oif}
${fw6cmd} add deny log all from any to 2002:e000::/20 via ${oif}
${fw6cmd} add deny log all from 2002:7f00::/24 to any via ${oif}
${fw6cmd} add deny log all from any to 2002:7f00::/24 via ${oif}
${fw6cmd} add deny log all from 2002:0000::/24 to any via ${oif}
${fw6cmd} add deny log all from any to 2002:0000::/24 via ${oif}
${fw6cmd} add deny log all from 2002:ff00::/24 to any via ${oif}
${fw6cmd} add deny log all from any to 2002:ff00::/24 via ${oif}
${fw6cmd} add deny log all from 2002:0a00::/24 to any via ${oif}
${fw6cmd} add deny log all from any to 2002:0a00::/24 via ${oif}
${fw6cmd} add deny log all from 2002:ac10::/28 to any via ${oif}
${fw6cmd} add deny log all from any to 2002:ac10::/28 via ${oif}
${fw6cmd} add deny log all from 2002:c0a8::/32 to any via ${oif}
${fw6cmd} add deny log all from any to 2002:c0a8::/32 via ${oif}
${fw6cmd} add deny log all from ff05::/16 to any via ${oif}
${fw6cmd} add deny log all from any to ff05::/16 via ${oif}
# Allow anything on the internal net.
${fw6cmd} add pass all from any to any via ${iif}
# Allow anything outbound from this net.
${fw6cmd} add pass all from ${onet}/${oprefixlen} to any out via ${oif}
# Deny anything outbound from other nets.
${fw6cmd} add deny log all from any to any out via ${oif}
# Allow TCP through if setup succeeded.
${fw6cmd} add pass tcp from any to any established
# Allow IP fragments to pass through.
${fw6cmd} add pass all from any to any frag
# Allow inbound ftp, ssh, email, tcp-dns, http, https, pop3, pop3s.
${fw6cmd} add pass tcp from any to ${oip} 21 setup in via ${oif}
${fw6cmd} add pass tcp from any to ${oip} 22 setup in via ${oif}
${fw6cmd} add pass tcp from any to ${oip} 25 setup in via ${oif}
${fw6cmd} add pass tcp from any to ${oip} 53 setup in via ${oif}
${fw6cmd} add pass tcp from any to ${oip} 80 setup in via ${oif}
${fw6cmd} add pass tcp from any to ${oip} 443 setup in via ${oif}
${fw6cmd} add pass tcp from any to ${oip} 110 setup in via ${oif}
${fw6cmd} add pass tcp from any to ${oip} 995 setup in via ${oif}
# Deny inbound auth, netbios, and ldap without logging.
${fw6cmd} add deny tcp from any to ${oip} 113 setup in via ${oif}
${fw6cmd} add deny tcp from any to ${oip} 139 setup in via ${oif}
${fw6cmd} add deny tcp from any to ${oip} 389 setup in via ${oif}
# Deny some chatty UDP broadcast protocols without logging.
${fw6cmd} add deny udp from any 137 to any in via ${oif}
${fw6cmd} add deny udp from any to any 137 in via ${oif}
${fw6cmd} add deny udp from any 138 to any in via ${oif}
${fw6cmd} add deny udp from any 513 to any in via ${oif}
${fw6cmd} add deny udp from any 525 to any in via ${oif}
# Allow inbound DNS and NTP replies. This is somewhat of a hole,
# since we're looking at the incoming port number, which can be
# faked, but that's just the way DNS and NTP work.
${fw6cmd} add pass udp from any 53 to ${oip} in via ${oif}
${fw6cmd} add pass udp from any 123 to ${oip} in via ${oif}
# Allow inbound DNS queries.
${fw6cmd} add pass udp from any to ${oip} 53 in via ${oif}
# Deny inbound NTP queries without logging.
${fw6cmd} add deny udp from any to ${oip} 123 in via ${oif}
# Allow traceroute to function, but not to get in.
${fw6cmd} add unreach port udp from any to ${oip} 33435-33524 in via ${oif}
# Allow RIPng
#${fw6cmd} add pass udp from fe80::/10 521 to ff02::9 521
#${fw6cmd} add pass udp from fe80::/10 521 to fe80::/10 521
# Allow some inbound icmps - dest unreach, too big, time exceeded,
# echo, echo reply, neighbor solicit, neighbor ad.
${fw6cmd} add pass ipv6-icmp from any to any icmptypes 1,2,3,128,129,135,136
# Everything else is denied and logged.
${fw6cmd} add deny log all from any to any
;;