*** Use this page at your own risk *** FreeBSD NAT/firewall via cable modem Step-by-Step It is really easy to make a FreeBSD gateway for a private network. You only need to do several easy tasks. Details are given below. 1. Lock down the box 2. Install second Ethernet card (NIC) in the FreeBSD box 3. Customize the kernel 4. Enable packet forwarding, firewall and address translation 5. Configure machines on LAN 6. Restart everything 7. Add-on 8. References 1. Lock down the box The first step to lock down a box for NAT/firewall is to disable all running services that are not necessary with the installation. Edit /etc/rc.conf and make sure inetd, portmap, sendmail daemons are all disabled. inetd_enable="NO" portmap_enable="NO" sendmail_enable="NO" Also comment out each service line in /etc/inetd.conf for extra precaution. If you need to remote logon, use ssh and add sshd_enable="YES" to /etc/rc.conf. Once you disabled unnecessary services, go to http://www.unixcircle.com/memberonly/portscan.php3 to remotely port scan your own box from the outside. Be careful when you do this behind a NAT/firewall box as the port scan script will scan the NAT/firewall instead. If you have another box, use http://www.insecure.org/nmap/ to scan the box from the inside. Get the latest FreeBSD security patches manually and apply them from here: ftp://ftp.FreeBSD.org/pub/FreeBSD/CERT/patches/ or use CVSup to synchronize to stable release and build from source: http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/synching.html 2. Install second Ethernet card (NIC) in the FreeBSD box Use any supported Ethernet card for the second NIC in the FreBSD machine. One card will be given a public IP address (assigned by your ISP or obtained dynamically, e.g., with DHCP) and the other will be given an IP address in a non-routable network. Your choices for private network addresses must come from one of these ranges (see RFC 1918): 10.0.0.1 - 10.255.255.254 netmask 255.0.0.0 172.16.0.1 - 172.31.255.254 netmask 255.240.0.0 192.168.0.1 - 192.168.255.254 netmask 255.255.0.0 Add the first card to the list of interfaces in rc.conf and add an appropriate line for this card in order to cause the card to be configured on boot up. Example: network_interfaces="ep0 ep1 lo0" # List of interfaces ifconfig_ep0="inet 192.168.1.1 netmask 255.255.255.0" # First NIC - private And if you have a static IP address for the second NIC, you naturally need to have it configured in rc.conf as well. Example: ifconfig_ep1="inet xxx.xxx.xxx.xxx netmask xxx.xxx.xxx.xxx" Be sure to indicate a correct IP address and netmask for both interfaces. Once you have chosen a private network address range for your inside machines, stay with that same range. Do not mix network numbers on the inside. Whatever address you choose for this first NIC in the FreeBSD gateway (with the non-routable address) becomes the default gateway IP address for all machines on the inside private network. 3. Customize the kernel To compile the new kernel you need the kernel source (part of kernel developer distribution). If you haven't done that, run /stand/sysinstall and make sure to install the kernel source. # cd /sys/i386/conf # cp GENERIC firewall I usually name the kernel to the machine hostname, but you can give it any name. Edit the kernel config file: # vi firewall In the options section, add these lines: options IPFILTER #ipfilter support options IPFILTER_LOG #ipmon(8) log support options IPFILTER_DEFAULT_BLOCK #block all packets by default Remove any hardware related options that are not relevant to your machine. One way to find out what to keep is to consult the dmesg output and remove all the rest. For all available kernel options, refer to LINT in the same directory as your kernel file. Save the kernel config file and then compile and install it: # cd /usr/src # make buildkernel KERNCONF=firewall ( kernel building output... ) ... # make installkernel KERNCONF=firewall # reboot This will retain the old kernel as /kernel.old just in case something has gone awry with the new one and the box doesn't boot. If that happens you can type 'kernel.old' at the boot: prompt to boot the old kernel. 4. Enable packet forwarding, firewall and address translation To enable packet forwarding go back into rc.conf and add: gateway_enable="YES" And if you receive your public address assignment dynamically through DHCP, edit /etc/rc.conf and add: ifconfig_ep1="DHCP" and edit /etc/dhclient.conf so that the dhcp client can query the dhcp server for appropriate information in order to connect. Here's my /etc/dhclient.conf send host-name "crxxxxxx-a"; # Put your client IP here request subnet-mask, broadcast-address, routers, domain-name-servers; timeout 30; retry 60; select-timeout 5; Since you don't know what to block yet, you need to open up ingress and outgress traffic to flow through. Edit /etc/ipf.rules and add: pass in from any to any pass out from any to any At minimum, you will need the below mappings for NAT to work: # Make sure all the `proxy' lines are before any generic `portmap' lines, # as the first match always wins. # Use internal proxy for ftp map ep1 192.168.1.0/24 -> 0.0.0.0/32 proxy port ftp ftp/tcp # Map all internal UDP and TCP traffic to the external IP address. map ep1 192.168.1.0/24 -> 0.0.0.0/32 portmap tcp/udp 40000:60000 # Map incoming ICMP dynamically # If you ping from an internal machine, only that machine will # see incoming ICMPs map ep1 192.168.1.0/24 -> 0.0.0.0/32 Now, turn on NAT/firewall feature in /etc/rc.conf: ipfilter_enable="YES" #Stateful firewall ipnat_enable="YES" #Network Address Translation ipmon_enable="YES" #Firewall logging
5. Configure machines on LAN All the machines on the private network should be configured to use the address of the PRIVATE interface in the FreeBSD box as the default gateway. Make sure you use a correct netmask. 6. Restart everything Reboot all machines, starting with the FreeBSD gateway. Watch for errors of any kind on startup. If there are no errors, reboot the machines on the internal LAN and see if everything works.
Once your NAT/firewall is online, you should start to read the IPFILTER-HOWTO at http://www.unixcircle.com/ipf/ and add more blocking/passing rules to /etc/ipf.rules. Some other useful links can be found on the www.ipfilter.org home page. Each time /etc/ipf.rules or /etc/ipnat.rules is modified, you have to them as follow. Reloading these rules will flush all current active connections. /sbin/ipf -Fa -f /etc/ipf.rules /sbin/ipnat -CF -f /etc/ipnat.rules
7. Add-ons Assume everything is ok and you can surf the net from internal PCs, here are additional miscellaneous stuffs. Pre-FreeBSD 4.3 RELEASE, I often get: Apr 6 09:14:25 guava /bsd: arpresolve: can't allocate llinfo in my log file. Basically, this happens when you get an ARP for a system with an address that's not on one of the directly-connected subnets. Starting with FreeBSD 4.3 RELEASE, you can suppress this annoying message with: net.link.ether.inet.log_arp_wrong_iface: 1 It's turned on by default so you don't need to do anything. Otherwise just edit /etc/sysctl.conf and add: net.link.ether.inet.log_arp_wrong_iface=1 You can use ipfstat to display firewall statistics a la ``top" command: cd /usr/src/sbin/ipfstat make clean make depend make -DSTATETOP -DSTATETOP_INC="" -DSTATETOP_LIB="-lncurses" -DSTATETOP_CFLAGS="-DSTATETOP" make install Now, issue ipfstat -t and you will see something similar: IP Filter: v3.4.16 - state top 20:23:29 Src = 0.0.0.0 Dest = 0.0.0.0 Proto = any Sorted by = # bytes Source IP Destination IP ST PR #pkts #bytes ttl 192.168.1.200,1415 65.92.100.89,6699 4/4 tcp 8245 6923504 42:14:06 23.234.234.2,24064 208.31.160.30,22 4/4 tcp 576 199843 119:59:59 192.168.1.200,2091 64.124.41.191,8888 4/4 tcp 157 118770 51:36:40 192.168.1.200,1094 64.124.41.161,8888 4/4 tcp 125 94190 46:37:34 Some popular ipfilter commands you might want to use. man ipf(8), ipnat(8) and ipmon(8) and ipfstat(8) for details. To find out the ipfilter version: /sbin/ipf -V # ipf -V ipf: IP Filter: v3.4.16 (264) Kernel: IP Filter: v3.4.16 Running: yes Log Flags: 0 = none set Default: block all, Logging: available Active list: 0 Notice the ``block all" setting from our options IPFILTER_DEFAULT_BLOCK in the kernel. To display the current list of active MAP/Redirect filters and active sessions: /sbin/ipnat -l To find out the ``hit" statistic for each individual rule in /etc/ipf.rules: /sbin/ipfstat -hio Finally, watch your log for portscans :) 8. References IPFilter home page: http://www.ipfilter.org IPFilter how-to: http://www.unixcircle.com/ipf/ IPFilter mailing list archive: http://false.net/ipfilter Guido van Rooij has written some real nice IPFilter papers: http://www.madison-gurkha.com/all_publications.shtml
EOF Hoang Q. Tran last update: July 16, 2001
It is really easy to make a FreeBSD gateway for a private network. You only need to do several easy tasks. Details are given below.
inetd_enable="NO" portmap_enable="NO" sendmail_enable="NO"Also comment out each service line in /etc/inetd.conf for extra precaution. If you need to remote logon, use ssh and add sshd_enable="YES" to /etc/rc.conf.
Once you disabled unnecessary services, go to unixcircle.com portscan to remotely port scan your own box from the outside. Be careful when you do this behind a NAT/firewall box as the port scan script will scan the NAT/firewall instead. If you have another box, use nmap to scan the box from the inside.
Get the latest FreeBSD security patches manually and apply them from here:
ftp://ftp.FreeBSD.org/pub/FreeBSD/CERT/patches/or use CVSup to synchronize to stable release and build from source.
For more information on security vulnerabilities, read SANS The Twenty Most Critical Internet Security Vulnerabilities (Updated)
Setup network interfaces
Assume this box has 2 3com 509B network
cards and their FreeBSD driver are named as ep0 and ep1 for first and second
card. First network card will use the non-routable private address according to
rfc1918. The second will be assigned either static or dynamic via DHCP IP
address.
Non-routable IP addresses for private networks:
10.0.0.1 - 10.255.255.254 netmask 255.0.0.0 172.16.0.1 - 172.31.255.254 netmask 255.240.0.0 192.168.0.1 - 192.168.255.254 netmask 255.255.0.0Assume you choose 192.168.0.1 range, enable the first network interface in /etc/rc.conf:
ifconfig_ep0="inet 192.168.1.1 netmask 255.255.255.0"
If the second network card has static IP, enable it in /etc/rc.conf also:
ifconfig_ep1="inet xxx.xxx.xxx.xxx netmask xxx.xxx.xxx.xxx"
where xxx denotes your IP address and netmask provided by your ISP.
Be sure to indicate a correct IP address and netmask for both interfaces.
Once you have chosen a private network address range for your inside machines,
stay with that same range. The first network card will be the default FreeBSD
gateway IP address.
Customize the kernel
To compile the new kernel you need the kernel
source (part of kernel developer distribution). If you haven't done that, run
/stand/sysinstall and make sure to install the kernel source.
# cd /sys/i386/conf # cp GENERIC firewallI usually name the kernel to the machine hostname, but you can give it any name. Edit the kernel config file:
# vi firewallIn the options section, add these lines:
options IPFILTER # IPFilter support options IPFILTER_LOG # IPFilter logging support options IPFILTER_DEFAULT_BLOCK # Block all packets by default options RANDOM_IP_ID # RANDOM_IP_ID causes the ID field in IP packets to be randomized # instead of incremented by 1 with each packet generated.Remove any hardware related ``options'' that are not relevant to your machine. One way to find out what to keep is to consult the dmesg output and remove all the rest. For all available kernel options, refer to LINT in the same directory as the kernel file. Save the kernel config file and then compile and install it:
# cd /usr/src # make buildkernel KERNCONF=firewall ( kernel building output... ) ... # make installkernel KERNCONF=firewall # rebootThis will retain the old kernel as /kernel.old just in case something has gone awry with the new one and the box doesn't boot. If that happens you can type 'kernel.old' at the boot: prompt to boot the old kernel.
net.inet.ip.forwarding=1Hardening features:
Add to /etc/sysctl.conf:
To defending against sequence number attacks based on rfc1948 by randomize initial sequence number:
net.inet.tcp.strict_rfc1948=1To verify that an incoming packet arrives on an interface that has an address matching the packet's destination address:
net.inet.ip.check_interface=1To drop SYN packets destine to non-listening tcp/udp port. This will create a blackhole and protect against stealth port scans:
net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1Improve performance features:
To enable high performance data transfers on hosts according to http://www.psc.edu/networking/perf_tune.html, add the following in addition to ip forwarding to /etc/sysctl.conf:
# 1. Path MTU discovery: enabled by default # 2. TCP Extension (RFC1323): enabled by default # 3. Increase TCP Window size for increase in network performance net.inet.tcp.recvspace=65535 net.inet.tcp.sendspace=65535 # 4. SACK (RFC2018): FreeBSD doesn't have a SACK implementationDHCP client:
And if you receive your public address assignment dynamically through DHCP, edit /etc/rc.conf and add:
ifconfig_ep1="DHCP"and edit /etc/dhclient.conf so that the dhcp client can query the dhcp server for appropriate information in order to connect. Here's my /etc/dhclient.conf
send host-name "crxxxxxx-a"; # Put your client IP here request subnet-mask, broadcast-address, routers, domain-name-servers;Filter rule:
Since you don't know what to block yet, you need to open up ingress and outgress traffic to flow through. Edit /etc/ipf.rules and add:
pass in all pass out allAn example of a working /etc/ipf.rules
Network Address Translation rule:
For NAT and ftp clients behind NAT to work, add the following to /etc/ipnat.rules:
# Use ipfilter ftp proxy for ftp client transfers mode: active map ep1 192.168.1.0/24 -> 0.0.0.0/32 proxy port ftp ftp/tcp # Map all tcp and udp connections from 192.168.1.0/24 to external IP address, # changing the source port number to something between 40,000 and 60,000 inclusive map ep1 192.168.1.0/24 -> 0.0.0.0/32 portmap tcp/udp 40000:60000 # For all other IP packets, map to the external IP address map ep1 192.168.1.0/24 -> 0.0.0.0/32Make sure all the `proxy' lines are before any generic `portmap' lines, as the first match always wins.
An example of a working /etc/ipnat.rules
Now, turn on NAT/firewall feature in /etc/rc.conf:
ipfilter_enable="YES" #Stateful firewall ipfilter_flags="" #IPFilter is enabled in the kernel and not module ipnat_enable="YES" #Network Address Translation ipmon_enable="YES" #Firewall logging ipmon_flags="-Dsn" -D: Cause ipmon to turn itself into a daemon. -n: IP addresses and port numbers will be mapped, where possible, back into hostnames and service names. -s: Packet information read in will be sent through syslogd rather than saved to a file.
Some other interesting features with IPFilter you might find useful:
1. Transparent proxying
If there's a mail server as 192.168.1.2 inside the private network , use ``rdr'' statement to transparent proxying. Since NAT happens before ``rdr'', a ``pass in'' is required in /etc/ipf.conf for the translated packets to flow into the mail server.
/etc/ipnat.conf:
# Redirect incoming smtp traffic to mail server behind NAT rdr ep1 0.0.0.0/0 port 25 -> 192.168.1.2 port 25
/etc/ipf.conf:
# Allow the translated packets with fragment and SYN flag to flow in. Keep state the connection. pass in quick on ep1 proto tcp from any to any port = 25 flags S keep state keep frags2. Load balancing
To load balancing a farm of 6 web servers behind NAT with address 192.168.1.1-192.168.1.6, use ``round-robin'' statement. IPFilter will distribute the load using round robin method. IPFilter will distribute the load even if one of the web servers is down. l4check which is part of IPFilter and can deal with this scenario.
rdr ep1 0.0.0.0/0 port 80 -> 192.168.1.1,192.168.1.2 port 80 tcp round-robin rdr ep1 0.0.0.0/0 port 80 -> 192.168.1.3,192.168.1.4 port 80 tcp round-robin rdr ep1 0.0.0.0/0 port 80 -> 192.168.1.5,192.168.1.6 port 80 tcp round-robinLog filtering rules to a dedicated log host:
A nice way to backup log messages and rely on if the NAT/firewall is rooted is to use a dedicated log server. Here is how you setup:
1. On the NAT/firewall, send log messages to a dedicated log server called e.g. ``loghost''
Edit /etc/syslog.conf and uncomment out the following line:
# uncomment this to enable logging to a remote loghost named loghost *.* @loghostRestart syslogd:
# kill -1 `cat /var/run/syslog.pid`2. On the dedicated FreeBSD log server Kill the current running syslogd which does not log messages from remote machines
# kill -9 `cat /var/run/syslog.pid`Invoke it with -a to turn on remote logging for log messages from NAT/firewall
# syslogd -a 192.168.1.1The check out the /var/log files on the loghost for new log entries from the NAT/firewall. Caveat: The communication link between loghost and the firewall is not encrypted and therefore potentially vulnerable to eavedrop. Use IPsec to encrypt the channel.
To set the internal boxes to the default FreeBSD gateway on various operating systems:
Assume the FreeBSD box NAT/firewall has IP address: 192.168.1.1
AIX: edit /etc/rc.net and add /usr/sbin/route add 192.168.1.1 gateway >>$LOGFILE 2>&1 FreeBSD: edit /etc/rc.conf and add defaultrouter="192.168.1.1" HP-UX: edit /etc/rc.config.d/netconf and add ROUTE_GATEWAY[0]="192.168.1.1" Linux Redhat: edit /etc/sysconfig/network and add GATEWAY=192.168.1.1 NetBSD: echo "192.168.1.1" > /etc/mygate OpenBSD: echo "192.168.1.1" > /etc/mygate Solaris: echo "192.168.1.1" > /etc/defaultrouter Win2k: Start-Settings->Control Panel->Network and Dial-up Connections->Local Area Network-> Properties->Internet Protocol (TCP/IP)->Default Gateway->192.168.1.1If you don't want to reboot to pick up the IP address for the default gateway, use ``route'' to manually add the default route.
AIX: route add 0 192.168.1.1
HP-UX: route add 192.168.1.1
FreeBSD,NetBSD,OpenBSD,Solaris: route add default 192.168.1.1
Linux Redhat: route add default gw 192.168.1.1
2. /etc/resolv.conf on unix client hosts need to edit/add to have nameserver statements in order to resolve hostnames.
UNIX clients:
$ cat /etc/resolv.conf nameserver <ISP DNS IP> nameserver <ISP DNS IP>Win2k :
Start-Settings->Control Panel->Network and Dial-up Connections->Local Area Network-> Properties->Internet Protocol (TCP/IP)->->Advanced TCP/IP Settings->DNSand add the ISP DNS IPs.
# /sbin/ipf -Fa -f /etc/ipf.rules # /sbin/ipnat -CF -f /etc/ipnat.rulesYou can use ipfstat to display firewall statistics a la ``top" command:
# /sbin/ipfstat -t firewall.muine.org - IP Filter: v3.4.20 - state top 23:01:10 Src = 0.0.0.0 Dest = 0.0.0.0 Proto = any Sorted by = # bytes Source IP Destination IP ST PR #pkts #bytes ttl 192.168.1.200,1415 65.92.100.89,6699 4/4 tcp 8245 6923504 42:14:06 23.234.234.2,24064 208.31.160.30,22 4/4 tcp 576 199843 119:59:59 192.168.1.200,2091 64.124.41.191,8888 4/4 tcp 157 118770 51:36:40 192.168.1.200,1094 64.124.41.161,8888 4/4 tcp 125 94190 46:37:34To find out the ipfilter version:
# /sbin/ipf -V ipf: IP Filter: v3.4.20 (264) Kernel: IP Filter: v3.4.20 Running: yes Log Flags: 0 = none set Default: block all, Logging: available Active list: 0Notice the ``block all" setting from our options IPFILTER_DEFAULT_BLOCK in the kernel.
To display the current list of active MAP/Redirect filters and active sessions:
# /sbin/ipnat -lTo find out the ``hit" statistic for each individual rule in /etc/ipf.rules:
# /sbin/ipfstat -hioSee also ipftest(1), mkfilters(1), ipf(4), ipl(4), ipf(8), ipfstat(8), ipmon(8), ipnat(8) for details.
Some useful tools to test firewall:
FreeBSD comes with a bandwidth management in dummynet. dummynet can be extremely useful to slow down a potential fast link DoS another slower link. man dummynet(4) for other powerful features. Since dummynet is tied with ipfw -- FreeBSD specific firewall, you will need to include 3 ipfw options in the kernel along with dummynet.
options IPFIREWALL options IPFIREWALL_VERBOSE options IPFIREWALL_DEFAULT_TO_ACCEPT options DUMMYNETRecompile the kernel and reboot.
Enable ipfw to work with dummynet in /etc/rc.conf:
firewall_enable="YES" firewall_script="/etc/rc.dummynet" firewall_type="open" firewall_logging="YES"Assume you have a fast ethernet pipe and need to bandwidth limiting it to ethernet speed:
Construct a pipe 1 on host 192.168.1.4 to any destination:
# /sbin/ipfw add 100 pipe 1 ip from 192.168.1.4 to anyReduce the pipe to 10Mbits/s:
# /sbin/ipfw pipe 1 config bw 10Mbit/sYou can construct many pipes with different speeds, protocols.
To remove all pipes:
# /sbin/ipfw flushTo enable the above rules, add them to /etc/rc.dummynet
IPFilter home page: http://www.ipfilter.org IPFilter examples: http://coombs.anu.edu.au/~avalon/examples.html IPFilter how-to: http://www.unixcircle.com/ipf/ IPFilter mailing list archive: http://false.net/ipfilter Guido van Rooij has written some real nice IPFilter papers: http://www.madison-gurkha.com/all_publications.shtml Address Allocation for Private Internets: http://www.muine.org/rfc/rfc1918.txt The IP Network Address Translator (NAT): http://www.muine.org/rfc/rfc1631.txt Traditional IP Network Address Translator (Traditional NAT) http://www.muine.org/rfc/rfc3022.txt Bandwidth management: http://www.iet.unipi.it/~luigi/ip_dummynet/ The Twenty Most Critical Internet Security Vulnerabilities (Updated) http://66.129.1.101/top20.htm
Hoang Q. Tran
loves all unix flavours but prefers *bsd over anything else. Feedbacks are welcome and should send to hoang@muine.org last update: Feb 15, 2002