Home
Über die LugBE | Mailing List | Treff & Events | Projekte | lost+found | Support

A NAT Router Firewall IPSec Gateway with FreeBSD 5.1-RELEASE

A typical setup for home users and small businesses is to have a single machine connected to the internet as a router that serves as gateway for the private network behind it. Obviously, this router has to "hide" the whole net behind its own external address, which can even be dynamically assigned via an ISP's DHCP service.

This article describes the steps necessary for setting this up on a machine with two network cards running FreeBSD 5.1-RELEASE. It is largely equivalent to the setup described for the same machine running Linux in another article here. Additionally, the FreeBSD Router serves as IPSec gateway, encrypting traffic to networks behind other routers on the internet.

Warning!

This article contains just the steps that it took me to set up the router. Neither does it cover the whole underlying concepts of networking, security, IPSec, and the state of the world as such, nor does it necessarily protect your machines from security risks (even though it is designed to do so). I might have overlooked something, might have opened a security hole in my configuration. I might be an absolute nincompoop. So do not simply read the text and follow the steps. Follow the links in it, try Google and freebsd.org to understand for yourself what it is you're doing. If you find any flaw in my configuration, please let me know (address at bottom).

You've been warned.

  1. Preparing the system

    1.1. Install FreeBSD 5.1-RELEASE. I used the ISO image here. Do read the Handbook!. Make sure to install the kernel sources and the ports collection.

    1.2. Recompile the kernel. How to do this is described in detail in the Handbook!. Because stock 5.1-RELEASE comes without firewall and IPSec support in the kernel, we have to compile that in. Those are the necessary options:
        # Firewall support added 
        options         IPFIREWALL
        # Divert support added (necessary for natd)
        options         IPDIVERT
        # IPSec support added
        options         IPSEC
        options         IPSEC_ESP
        options         IPSEC_DEBUG
    
    Compile, install and reboot.

  2. Set up the firewall
    (This setup uses ipfw, a dynamically assigned external address and a local net of 192.168.1.0/24.)

    2.1. Enable all necessary services and functions in /etc/rc.conf (adopt addresses and interface names for your setup):
        # use DHCP for external interface
        ifconfig_ep0="DHCP"
        # static address for internal interface
        ifconfig_ep1="inet 192.168.1.1 netmask 255.255.255.0 \
    	broadcast 192.168.1.255
    
        # enable IP forwarding
        gateway_enable="YES"
    
        # enable firewall
        firewall_enable="YES"
        # set path to custom firewall config
        firewall_type="/etc/fw/rc.firewall.rules"
        # be non-verbose? set to YES after testing
        firewall_quiet="NO"
    
        # enable natd, the NAT daemon
        natd_enable="YES"
        # which is the interface to the internet that we hide behind?
        natd_interface="ep0"
        # flags for natd
        natd_flags="-f /etc/fw/natd.conf"
    
    2.2 Edit /etc/fw/rc.firewall.rules (or whatever you set firewall_type to)
        # be quiet and flush all rules on start
        -q flush
        
        # allow local traffic, deny RFC 1918 addresses on the outside
        add 00100 allow ip from any to any via lo0
        add 00110 deny ip from any to 127.0.0.0/8
        add 00120 deny ip from any to any not verrevpath in
        add 00301 deny ip from 10.0.0.0/8 to any in via ep0
        add 00302 deny ip from 172.16.0.0/12 to any in via ep0
        add 00303 deny ip from 192.168.0.0/16 to any in via ep0
                                                                                        
        # check if incoming packets belong to a natted session, allow through if yes
        add 01000 divert natd ip from any to me in via ep0
        add 01001 check-state
        
        # allow some traffic from the local net to the router 
        # SSH
        add 04000 allow tcp from 192.168.1.0/24 to me dst-port 22 in via ep1 setup keep-state
        # ICMP
        add 04001 allow icmp from 192.168.1.0/24 to me in via ep1
        # NTP
        add 04002 allow tcp from 192.168.1.0/24 to me dst-port 123 in via ep1 setup keep-state
        add 04003 allow udp from 192.168.1.0/24 to me dst-port 123 in via ep1 keep-state
        # DNS
        add 04006 allow udp from 192.168.1.0/24 to me dst-port 53 in via ep1
        
        # drop everything else
        add 04009 deny ip from 192.168.1.0/24 to me
             
        # pass outgoing packets (to be natted) on to a special NAT rule
        add 04109 skipto 61000 ip from 192.168.1.0/24 to any in via ep1 keep-state
                                                                                        
        # allow all outgoing traffic from the router (maybe you should be more restrictive)
        add 05010 allow ip from me to any out keep-state
        
        # drop everything that has come so far. This means it doesn't belong to an 
        established connection, don't log the most noisy scans.
        add 59998 deny icmp from any to me
        add 59999 deny ip from any to me dst-port 135,137-139,445,4665
        add 60000 deny log tcp from any to any established
        add 60000 deny log ip from any to any
        
        # this is the NAT rule. Only outgoing packets from the local net will come here.
        # First, nat them, then pass them on (again, you may choose to be more restrictive)
        add 61000 divert natd ip from 192.168.1.0/24 to any out via ep0
        add 61001 allow ip from any to any
    
    2.3 Edit /etc/fw/natd.conf (or whatever you set natd_flags -f to)
        unregistered_only
        interface ep0
        use_sockets
        dynamic
        # dyamically open fw for ftp, irc
        punch_fw 2000:50
    
    2.4. Start the firewall

    Run /etc/rc.d/ipfw start. If you have the DHCP client running, you should have an external address and your firewall router will be functional. Else start it with /etc/rc.d/dhclient start or reboot. Rebooting would be a good idea at this point anyway.
    Time to get yourself a nice cold beer. You've deserved it!

    2.5. Tuning

    It may be useful to set some connection related parameters in the kernel. I did this in /etc/sysctl.conf (you can of course call sysctl directly).
     
        security.bsd.see_other_uids=0
        net.inet.ip.fw.dyn_ack_lifetime=3600
        net.inet.ip.fw.dyn_udp_lifetime=10
        net.inet.ip.fw.dyn_buckets=1024
    
    This sets the session lifetime for TCP sessions without any traffic to 1 hour, for UDP to 10 seconds.

  3. Set up IPSec

    3.1. Get and install racoon

    (Also take a look at http://www.x-itec.de/projects/tuts/ipsec-howto.txt)

    Go to /usr/ports/security/racoon.
    Enter make all install clean. This will install racoon and clean up afterwards. Racoon is used for managing the key exchange needed for IPSec. The encryption itself is done in the kernel.

    3.2. Enable racoon

    Add the following line to /etc/rc.conf
        racoon_enable="YES"
    
    This will start racoon at boot time by running /usr/local/etc/rc.d/racoon.sh. I simply edited this file and set my options there: it now calls /usr/local/sbin/racoon -f /etc/ipsec/racoon.conf -l /var/log/racoon. This means that config is in /etc/ipsec, logs go to /var/log.

    3.3. Configure racoon

    Now we have to edit racoon's config file. In my case this is /etc/ipsec/racoon.conf, per default it is /usr/local/etc/racoon/racoon.conf.

    Very good documentation has been written on how to configure racoon. Google knows it, and you might also take a look at http://www.kame.net/newsletter/20001119/. Below is just what I changed in /etc/ipsec/racoon.conf.
        [....]
        path certificate "/etc/ipsec/cert" ;
        [....]
        log info;
        [....]
    
        # this is the other gateway's address
        remote aaa.bbb.ccc.ddd 
    {
    	# it's freeswan, so it doesn't support aggressive mode
            exchange_mode main,aggressive; 
            doi ipsec_doi;
            situation identity_only;
                                                                                    
            my_identifier asn1dn ;
    	# Subject of other gateway's certificate
            peers_identifier asn1dn "C=XY/O=XY Org/CN=xy.org.org";
    	# my own X.509 certificate and key
            certificate_type x509 "mycert.crt" "mykey.key";
     
            nonce_size 16;
            lifetime time 1 min;    # sec,min,hour
            initial_contact on;
            support_mip6 on;
            proposal_check obey;    # obey, strict or claim
     
            proposal {
                    encryption_algorithm 3des;
                    hash_algorithm sha1;
                    authentication_method rsasig ;
                    dh_group 2 ;
            }
    }
    

    3.4. Install the certificates

    Also take a look at http://www.kame.net/newsletter/20001119b/

    Copy the following files to /etc/ipsec/cert/ (or whatever you set path certificate to above):
    • Your certificate (named "mycert.crt" above)
    • Your private key (mykey.key; make this -rw------- root)
    • The other gateway's CA's public certificate.

    Then make a link to the other gateway's CA's certificate (I assume in the following example that the certificate file is named ca.pem). This link must be named after the hash value of the file itself. You can create it with
        # ln -s ca.pem `openssl x509 -noout -hash -in ca.pem`.0
    

    3.5. Tell the kernel to use IPSec

    You should have read the above links before you proceed.

    Now you must tell the kernel to use IPSec when communicating with the other gateway. You do this by creating a file which is then used by /etc/rc.d/ipsec on startup. I put the file in /etc/ipsec/ipsec.conf and entered the following in /etc/rc.conf
        ipsec_enable="YES"
        ipsec_file="/etc/ipsec/ipsec.conf"
    
    /etc/ipsec/ipsec.conf contains the parameters for the setkey (8) command that adds, updates, dumps, or flushes Security Association Database (SAD) entries as well as Security Policy Database (SPD) entries in the kernel.
        # First, flush all SAD and SPD
                                                                                    
        flush;
        spdflush;
                                                                                    
        # Then, set up SPD for the local net and the net behind the other gateway
        # www.xxx.yyy.zzz is my own external address
        # aaa.bbb.ccc.ddd is the other gateway's address as given in
        # /etc/ipsec/racoon.conf
    
        spdadd 192.168.1.0/24 192.168.100.0/24 any -P out ipsec \
            esp/tunnel/www.xxx.yyy.zzz-aaa.bbb.ccc.ddd/require ;
        spdadd 192.168.100.0/24 192.168.1.0/24 any -P in ipsec \
            esp/tunnel/aaa.bbb.ccc.ddd-www.xxx.yyy.zzz/require ;
    
    This tells the kernel to use the ESP tunnel between www.xxx.yyy.zzz (the router) and aaa.bbb.ccc.ddd (the other gateway) when routing between the subnets 192.168.1.0/24 (local) and 192.168.100.0/24 (remote).

    This is the only place in the whole configuration where my own external IP address is used. Since this is a dynamically assigned address which can change, I had to provide some mechanism to restart IPSec when the address changes. I did this by configuring dhclient to run a script which rewrites /etc/ipsec/ipsec.conf and restarts /etc/rc.d/ipsec. I found it to be the easiest way to name the script /etc/dhclient-exit-hooks, as the ISC dhclient will look for this file everytime it has to update anything (see dhclient-script(8)). Somebody is likely to come up with a better solution.

    3.6. Amend the firewall rules for IPSec

    Add the following lines to your firewall config (/etc/fw/rc.firewall.rules in this example)
        add 05020 allow udp from any 500 to me dst-port 500 in via ep0 keep-state
        add 05021 allow esp from any to me in via ep0 keep-state
        add 05022 allow ah from any to me in via ep0 keep-state
    
    This will allow racoon to exchange keys on UDP port 500 and the kernel to talk via ESP and AH with the outside world (AH is actually not needed in my setup, but I keep it there for completeness and portability).

    3.7. Start all required services and enjoy

    You may want to add a DHCP, NTP, and DNS server to your router (I did, as the above firewall rules imply). I will not cover this here, though. The configuration files are about the same as in the Linux setup article.

    Now start all services manually with their respective scripts in /etc/rc.d/ or simply reboot. This should do.

    In case you're also in charge of the other IPSec gateway, here are its /etc/ipsec.conf and /etc/ipsec.secrets. In my case it was RedHat Linux, Kernel 2.4.18-3, and the super-freeswan-1.99.8 patch from http://www.freeswan.ca/.

    Markus Wernig (public at wernig dot net)


    Webmaster at lugbe.ch

    Die Artikel dieser Seite stehen unter Copyleft ihrer jeweiligen Autor*innen. Ihre Nutzung, Wiedergabe etc. untersteht den Bedingungen der GNU Free Documentation License (http://www.gnu.org/copyleft/fdl.html), wo nicht explizit anders vermerkt.