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.
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.
- 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
# Divert support added (necessary for natd)
# IPSec support added
Compile, install and reboot.
- 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
# static address for internal interface
ifconfig_ep1="inet 192.168.1.1 netmask 255.255.255.0 \
# enable IP forwarding
# enable firewall
# set path to custom firewall config
# be non-verbose? set to YES after testing
# enable natd, the NAT daemon
# which is the interface to the internet that we hide behind?
# flags for natd
/etc/fw/rc.firewall.rules (or whatever you set firewall_type to)
# be quiet and flush all rules on start
# 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
add 04000 allow tcp from 192.168.1.0/24 to me dst-port 22 in via ep1 setup keep-state
add 04001 allow icmp from 192.168.1.0/24 to me in via ep1
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
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
/etc/fw/natd.conf (or whatever you set natd_flags -f to)
# dyamically open fw for ftp, irc
2.4. Start the firewall
/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!
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).
This sets the session lifetime for TCP sessions without any traffic to 1 hour,
for UDP to 10 seconds.
- Set up IPSec
3.1. Get and install
(Also take a look at http://www.x-itec.de/projects/tuts/ipsec-howto.txt)
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.
Add the following line to
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.
Now we have to edit racoon's config file. In my case this is
/etc/ipsec/racoon.conf, per default it is
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
path certificate "/etc/ipsec/cert" ;
# this is the other gateway's address
# it's freeswan, so it doesn't support aggressive mode
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";
lifetime time 1 min; # sec,min,hour
proposal_check obey; # obey, strict or claim
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
- Your private key (
mykey.key; make this
- 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/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
# 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
spdadd 192.168.1.0/24 192.168.100.0/24 any -P out ipsec \
spdadd 192.168.100.0/24 192.168.1.0/24 any -P in ipsec \
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 (
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.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.