Adventures in Firewalling or: How I Learned to Stop Worrying and Love PF Back in the 1970's Bill Joy at UC Berkeley re-wrote some networking code that sucked, and made it very fast, and quite robust.  This gave birth to present day TCP/IP, the current dominant networking standard in the industry.  IP addresses are essentially phone numbers for computers, each one is unique, and assigned in a hierarchical manner, and a single computer can have more than one.  The BSD networking code is the basis for most major networking components, such as Windows 2000, and Mac OS X. This talk is aimed at people who have some networking knowledge.  This paper assumes knowledge in the following areas: ip4 addresses; tcp/udp ports function and purpose; how to edit config  files; and how to run commands in unix. For more background information, please read the following documentation: OpenBSD FAQ #6; netintro(4). Firewalling         Turn everything off!  This is one of the most visible targets for crackers, therefore use the KISS principle.  The less things your firewall do, the less possibilities the crackers have to gain access, or otherwise cause mischief. Set system configure options         sysctl -w net.inet.ip.forwarding=1                 $ cat /etc/sysctl.conf                  net.inet.ip.forwarding=1        # 1= forward ip4 packets                  #net.inet6.ip6.forwarding=1     # 1= forward ip6 packets                 $         It is /possible/ to have a bridging firewall without turning on ipforwarding, but it /will/ be slow.  I went from 50k/sec at 60% CPU time to 160k/sec (my DSL line max) at 0% CPU time by turning on ipforwarding.   Turn it on. Two types of firewalls         Routing firewall: the good, the bad, and the ugly                 Accessible, requires changes in networking configurations, attackable, full on router (it routes packets from one network to another), links different subnets together         Bridging firewall: the good, the bad, and the ugly                 Invisible firewall                         No IP address = not accessible, no changes to your network or hardware, not a router, just a blocker, cannot link subnets together.                          OpenlySecure.org gives some excellent warnings about invisible bridging: "* Remember -- this machine has NO IP address, so it can't be reached from anywhere other than the console. Test ALL of your rules thoroughly before officially activating this thing and leaving for the week. This also means you can't reach any other machines from the bridge -- so don't freak when "ping" or "telnet" fails to connect with a machine that's directly connected to the bridge. Regardless of the direct connection, nothing will be able to talk with the bridge other than at the ARP level. * Likewise, IPNat ain't gonna work neither. So don't even bother. " ifconfig(8) $ ifconfig -a lo0: flags=8049 mtu 33224         inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4         inet6 ::1 prefixlen 128         inet 127.0.0.1 netmask 0xff000000 lo1: flags=8008 mtu 33224 gm0: flags=8863 mtu 1500         media: Ethernet autoselect (100baseTX full-duplex)         status: active         inet aaa.bbb.ccc.160 netmask 0xffffff00 broadcast aaa.bbb.ccc.255         inet6 fe80::AAAA:BBBB:CCCC:DDDD%gm0 prefixlen 64 scopeid 0x1 pflog0: flags=141 mtu 33224 sl0: flags=c010 mtu 296 sl1: flags=c010 mtu 296 ppp0: flags=8010 mtu 1500 ppp1: flags=8010 mtu 1500 tun0: flags=10 mtu 3000 tun1: flags=10 mtu 3000 enc0: flags=0<> mtu 1536 bridge0: flags=0<> mtu 1500 bridge1: flags=0<> mtu 1500 vlan0: flags=0<> mtu 1500 vlan1: flags=0<> mtu 1500 gre0: flags=8010 mtu 1450 gif0: flags=8010 mtu 1280 gif1: flags=8010 mtu 1280 gif2: flags=8010 mtu 1280 gif3: flags=8010 mtu 1280 hostname.if(5)         $ cat /etc/hostname.ep0          !ifconfig ep0 delete          !ifconfig ep0 media 10baseT          !ifconfig ep0 up         $ cat /etc/hostname.xl0          !ifconfig xl0 delete          !ifconfig xl0 media autoselect          !ifconfig xl0 up /etc/mygate (empty) brconfig(8) /etc/bridgename.if(5)         $ cat /etc/bridgename.bridge0 # Uncomment the next two lines to prevent (or at least make it more difficult) # MAC address attacks, may be a good idea on Cable. # This MAC address is for the box that sits on top of my firewall #!brconfig bridge0 -learn ep0 static xl0 00:00:a2:cb:b7:d5 #!brconfig bridge0 -discover ep0 !brconfig bridge0 add xl0 add ep0 up static routes: the good, the bad, and the ugly         $ arp -a ep0          gateway.example.com (aaa.bbb.ccc.1) at 00:00:a2:cb:b7:d5          mymachine.example.com (aaa.bbb.ccc.56) at 00:04:76:2a:52:f6         $ Now that people are traversing over your firewall, you need to be able to protect it, and to kick off connections to it.  Most people are aware of [netstat -an], but not many are aware of [fstat | grep internet].   fstat(1) will help you determine what pid is associated with what connection, and based on that, you can disconnect them, without impacting the other connections to your system. $ netstat -an Active Internet connections (including servers) Proto Recv-Q Send-Q  Local Address          Foreign Address        (state) tcp        0      0  aaa.bbb.ccc.76.22      aaa.bbb.ccc.56.1697     ESTABLISHED tcp        0      0  *.22                   *.*                    LISTEN tcp        0      0  *.21                   *.*                    LISTEN udp        0      0  *.514                  *.* udp        0      0  *.68                   *.* udp        0      0  *.*                    *.* $ fstat | grep internet root     sshd       20269    4* internet stream tcp 0xe0776400 aaa.bbb.ccc.76:22 <-- aaa.bbb.ccc.56:1697 root     sshd       28443    3* internet stream tcp 0xe0776700 *:22 root     ftpd        1460    4* internet stream tcp 0xe0776f00 *:21 root     syslogd    31949    4* internet dgram udp *:514 root     dhclient   15892    3* internet dgram udp *:0 root     dhclient   15892    4* internet dgram udp *:68 $ ps auxww | grep 20269 root     20269  0.0  2.9   372  1424 ??  S      9:02AM    0:00.49 sshd: user@ttyp0 (sshd)          [kill -15 20269] Will kick the SSH connection.] pfctl(8)         pf vs. ipf         /etc/pf.conf(5)         logging                 pflogd(8)                 tcpdump(8)                  Display the logs in real time                   $ tcpdump -n -e -ttt -i pflog0 /usr/sbin/tcpdump -e -n -ttt -l -i pflog0 | /usr/bin/logger -t pf -p local0.err                 aliases                 scrub (Reattaches fragments)                 modulate state (tcp only)                   ISN generation  This allows attackers to insert (one-way) information into the data stream, such as additional RCPT in a smtp connection.  -Excellent paper at [http://razor.bindview.com/publish/papers/tcpseq.html]                 IP Flags                         F: FIN, for closing connections                         S: SYN, for opening connections                         R: RST, for connection resets                         P: PSH, for making sure all data has arrived                         A: ACK, for acknowledgement packets                         U: URG, indicating this packet is urgent # These exist, but are not appropriate at this time authpf(8)         Strange authentication/firewall rules magic altq(9)         Hooks to guarantee data rate for servers. $ cat /etc/pf.conf #        1         2         3         4         5         6         7 #23456789012345678901234567890123456789012345678901234567890123456789012345 # Based on the OpenBSD 3.0 pf.conf(5) man page # # External interface of ep0 (3com 509B-TP non-pnp isa) # Internal interface of xl0 (3com 905B-TX pci) # Everyone has a real ip. (therefor no NATing required) # pf is able to bridge in both directions properly (as opposed to ipf) # # # # # |-------|   100baseTX xl0|--------|   $my_gateway|-------|    |--------| # |       |              in|  --->  |out           |       |    |        | # |Network|----------------|firewall|--------------|gateway|-//-|Internet| # |       |             out|  <---  |in            |       |    |        | # |-------|$my_net         |--------|ep0 10baseT   |-------|    |--------| # # #       __PF_explanation_web_pages_(at)__ #       http://www.benzedrine.cx/pf.html #       http://www.openbsd.org/faq/faq6.html#6.2 #       http://www.openbsd.org/cgi-bin/man.cgi?query=pf.conf&sektion=5 #       http://www.openbsd.org/cgi-bin/man.cgi?query=pflogd&sektion=8 #       http://www.openbsd.org/cgi-bin/man.cgi?query=pfctl&sektion=8 #       http://www.inebriated.demon.nl/pf-howto/ #       http://www.theapt.org/openbsd/pf.html ;-) # # #       IP addresses are cleaned, assuming aaa.bbb.ccc.0/24 is my local #        network and aaa.bbb.ccc.1 is my local gateway. #       aaa.bbb.ccc.165 is a dnscache system. #       aaa.bbb.ccc.166 is a ssh/web/mail/tinydns system. #       aaa.bbb.ccc.167 is a web/dnscache system. # # use a macro for the interface name, so it can be changed easily ext_if = "ep0" int_if = "xl0" my_net = "aaa.bbb.ccc.0/24" my_gateway = "aaa.bbb.ccc.1" spoofed = "{ 0.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, \                 192.168.0.0/16, 255.255.255.255/32 }" my_www = "{ aaa.bbb.ccc.166/32 }" my_dns = "{ aaa.bbb.ccc.165/32, aaa.bbb.ccc.166/32, aaa.bbb.ccc.167/32 }" ######################################################################## # Rules for $ext_if ######################################################################## # normalize all incoming traffic # Cool magic trick to clean tcp fragments before we apply filters scrub in on $ext_if all # block ip6 cause we don't have it block in quick on $ext_if inet6 all # block and log outgoing packets that don't have our address as source #  (Should have been dropped on the inside, log so we can investigate why) block out log quick on $ext_if from ! $my_net to any # block and log incoming packets from reserved address space and invalid # addresses, they are either spoofed or misconfigured, we can't reply to # them anyway (hence, no return-rst) block in log quick on $ext_if from $spoofed to any # silently drop broadcasts (Cablemodem noise) block in quick on $ext_if from any to 255.255.255.255 # block incoming packets that don't have our address as destination block in quick on $ext_if from any to ! $my_net # block nmap scans block in log quick on $ext_if inet proto tcp from any to any flags FUP/FUP # block everything by default block             in  on $ext_if           all block return-rst  in  on $ext_if proto tcp all block return-icmp in  on $ext_if proto udp all # pass out/in certain ICMP queries and keep state (ping) # state matching is done on host addresses and ICMP id (not type/code) # so replies (like 0/0 for 8/0) will match queries # ICMP error messages (which always refer to a TCP/UDP packet) are # handled by the TCP/UDP states pass in quick on $ext_if inet proto icmp all keep state # pass in 41000 < to allow audiogalaxy and ftp work pass in on $ext_if proto tcp from any to any port > 40999 keep state # pass in dns, email, http, and ssh connections to our dns/email/webserver box pass in on $ext_if proto tcp from any to $my_www port { 22, 25, 80, 110 } flags S/SA keep state # pass in dns requests to our dns servers pass in on $ext_if proto udp from any to $my_dns port 53 keep state # pass out all TCP connections and modulate state (bad-ass magic!) pass out on $ext_if proto tcp all modulate state # pass out all udp connections and keep state pass out on $ext_if proto udp all keep state # pass out all connections, and try to keep state pass out log on $ext_if all keep state ######################################################################## # Rules for $int_if ######################################################################## # normalize all incoming traffic scrub in on $int_if all # # block ip6 cause we don't have it block in quick on $int_if inet6 all # # block and log outgoing packets that don't have our address as source block in quick on $int_if from ! $my_net to any # # block incoming packets that don't have our address as destination #  (Should have been dropped on the outside, log so we can investigate why) block out log quick on $int_if from any to ! $my_net # # silently drop broadcasts (Dont be rude to our neighbors) block in quick on $int_if from any to 255.255.255.255 # # pass out/in certain ICMP queries and keep state (ping) # state matching is done on host addresses and ICMP id (not type/code) # so replies (like 0/0 for 8/0) will match queries # ICMP error messages (which always refer to a TCP/UDP packet) are # handled by the TCP/UDP states pass in on $int_if inet proto icmp all icmp-type 8 code 0 keep state # # pass out all TCP/UDP connections and modulate state pass out on $int_if inet proto tcp all modulate state pass out on $int_if inet proto udp all keep state # pass out all UDP connections and keep state pass in on $int_if proto udp all keep state Thanks: Theo de Raadt, for making an Operating System that doesn't suck; Daniel Hartmeier, for his initial, and continuing, work on pf; Henning Brauer, for his incredible depth of knowledge on unix servers in all forms; openlysecure.org, for their tutorial on invisible firewalling, which was incredibly helpful when I was trying to learn how to do it; misc@ for their replies to my asinine questions, and the occasional LART.