Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 23 Jun 2006 01:22:21 -0500
From:      David DeSimone <fox@verio.net>
To:        freebsd-net@freebsd.org
Subject:   Re: VPN with FAST_IPSEC and ipsec tools
Message-ID:  <20060623062221.GA23272@verio.net>
In-Reply-To: <449B5D50.8000700@thebeastie.org>
References:  <449228FA.50303@thebeastie.org> <20060616122855.GA29279@uk.tiscali.com> <20060616154306.GA18578@verio.net> <449B5D50.8000700@thebeastie.org>

next in thread | previous in thread | raw e-mail | index | archive | help
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Michael Vince <mv@thebeastie.org> wrote:
>
> > The main reason to use IPSEC tunnel mode and avoid GIF is that such
> > a config is interoperable with other IPSEC implementations, and thus
> > is much more useful in the real world.
>
> OK that said, how do you create a network to network tunnel based VPN
> without using the gif or gre devices?

Ok, here's a typical setup that I've used.

Suppose you have two gateways:

    Gateway 1	IP = 1.2.3.4

	Networks behind it:

	    192.168.1.0/24
	    192.168.2.0/24

    Gateway 2	IP = 5.6.7.8

	Networks behind it:

	    192.168.11.0/24
	    192.168.12.0/24

Most of the examples you'll find will teach you to use IPSEC in
Transport mode.  But Transport mode is only used for one endpoint to
talk to another endpoint.  What you want here (and what other gateways
like Cisco will implement) is Tunnel mode, where the traffic is
encapsulated rather than merely encrypted.

First you must define your SA's (security associations) in ipsec.conf.

SA's are defined from the perspective of the gateway.  What is "inbound"
on one gateway is "outbound" on the other.  You can't use the same
ipsec.conf on each endpoint; you have to tailor it to match what it
should expect to see.

Gateway 1's ipsec.conf:

    # This is ipsec.conf
    # Load it using:	setkey -f /etc/ipsec.conf

    spdflush;	# If you ever reload you will want to start fresh

    # Outbound traffic

    spdadd  192.168.1.0/24  192.168.11.0/24  any  \
	-P out  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;
    spdadd  192.168.2.0/24  192.168.11.0/24  any  \
	-P out  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;

    spdadd  192.168.1.0/24  192.168.12.0/24  any  \
	-P out  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;
    spdadd  192.168.2.0/24  192.168.12.0/24  any  \
	-P out  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;

    # Inbound traffic

    spdadd  192.168.11.0/24  192.168.1.0/24  any  \
	-P in  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;
    spdadd  192.168.12.0/24  192.168.1.0/24  any  \
	-P in  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;

    spdadd  192.168.11.0/24  192.168.2.0/24  any  \
	-P in  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;
    spdadd  192.168.12.0/24  192.168.2.0/24  any  \
	-P in  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;

Things to notice:

    There are 8 SA's defined, because each gateway has two subnets,
    and each unique subnet needs an SA defined IN EACH DIRECTION.
    So, 2 subnets here x 2 subnets there x 2 directions = 8 SA's total.

    Half of the SA's will be from the perspective of inbound traffic,
    (using "-P in") and the other half will be output ("-P out").

    When an SA is inbound ("-P in"), the tunnel descriptor will
    have the remote gateway listed first, followed by the local
    gateway.  Inbound traffic flows FROM the remote gateway (5.6.7.8)
    TO the local gateway (1.2.3.4), leading to a descriptor of
    "esp/tunnel/5.6.7.8-1.2.3.4/unique;"

    When an SA is outbound ("-P out"), the descriptor will be
    reversed, showing traffic FROM this gateway TO the remote,
    as in "esp/tunnel/1.2.3.4-5.6.7.8/unique;"

    When an SA is inbound ("-P in"), the remote gateway's network will
    appear first (because it is the source), followed by the local
    gateway's network (the destination).  That is why the first inbound
    specification reads "spdadd 192.168.11.0/24 192.168.1.0/24 any",
    specifying traffic coming FROM a remote network TO a local network.

    An output SA specifies a local network followed by a remote network.
    I hope you can see the pattern.

Gateway 2's ipsec.conf:

    # This is ipsec.conf
    # Load it using:	setkey -f /etc/ipsec.conf

    spdflush;	# If you ever reload you will want to start fresh

    # Inbound traffic

    spdadd  192.168.11.0/24  192.168.1.0/24  any  \
	-P out  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;
    spdadd  192.168.12.0/24  192.168.1.0/24  any  \
	-P out  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;

    spdadd  192.168.11.0/24  192.168.2.0/24  any  \
	-P out  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;
    spdadd  192.168.12.0/24  192.168.2.0/24  any  \
	-P out  ipsec esp/tunnel/5.6.7.8-1.2.3.4/unique;

    # Outbound traffic

    spdadd  192.168.1.0/24  192.168.11.0/24  any  \
	-P in  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;
    spdadd  192.168.2.0/24  192.168.11.0/24  any  \
	-P in  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;

    spdadd  192.168.1.0/24  192.168.12.0/24  any  \
	-P in  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;
    spdadd  192.168.2.0/24  192.168.12.0/24  any  \
	-P in  ipsec esp/tunnel/1.2.3.4-5.6.7.8/unique;

Things to notice:

    The pattern is exactly the same, but what was defined as "-P out"
    on the other gateway shows up as "-P in" on this gateway, and
    vice-versa.

This configuration is sufficient to define the necessary SA prototypes,
but in order to negotiate keys and parameters, you need IKE, and for
that you need the ipsec-tools port, in order to get racoon(8).

Just as each gateway has its own tailored ipsec.conf, each gateway also
has its own racoon.conf.  There is of course more than one way to set up
an IKE daemon.  These are the settings that I use.

Gateway 1's racoon.conf:

    # This is racoon.conf
    # Loaded by racoon when it starts, or when receiving a HUP.

    log  notify;            # notify(*), debug, debug2

    path  pre_shared_key  "/etc/ipsec.keys";
    path  pidfile         "/var/run/racoon.pid";

    listen
    {
	isakmp  1.2.3.4;
	strict_address;             # Needed?
    }

    remote  5.6.7.8
    {
	exchange_mode  aggressive,main,base;

	my_identifier     address  1.2.3.4;
	peers_identifier  address  5.6.7.8;

	verify_identifier  off;

	proposal_check  claim;      # obey, strict, claim(*), exact(*)

	proposal
	{
	    encryption_algorithm    aes;
	    hash_algorithm          sha1;
	    authentication_method   pre_shared_key;
	    dh_group                2;
	    lifetime        time    24 hours;
	}
    }

    sainfo  address  192.168.1.0/24 any   address  192.168.11.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.2.0/24 any   address  192.168.11.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.1.0/24 any   address  192.168.12.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.2.0/24 any   address  192.168.12.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.11.0/24 any   address  192.168.1.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.11.0/24 any   address  192.168.2.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.12.0/24 any   address  192.168.1.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.12.0/24 any   address  192.168.2.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

Things to notice:

    Some people feel aggressive mode is not as secure.  If you don't
    like it, remove it.

    There should be one "remote" section for each gateway that you
    converse with.

    In my config, I specify IP's as identifiers.  I am not sure if this
    is actually necessary, which is why I turn verification off.  I have
    not tested this portion strongly, but the config does work.

    The main use of proposal_check is to decide what to do when the two
    gateways do not agree on lifetime parameters.  It is best for the
    gateways to never disagree, of course.  :)

    The proposal section sets encryption and verification parameters
    for Phase 1 of IKE, where the gateways first establish trust.  My
    config uses pre-shared secrets.  I have not (yet) experimented with
    certificate-based authentication.

    The "sainfo" sections duplicate the information found in ipsec.conf.
    For every SA, there needs to be an "sainfo" section describing the
    exact same pair of networks.  Yes, it is cumbersome.  I have wished
    that racoon could just read the information out of the kernel, but I
    believe the problem is that the kernel only keeps track of networks
    that match SA's.  The other parameters (lifetime, encryption, etc)
    are not stored anywhere else.  They come from racoon negotiating
    these parameters with the remote system.

    If you are really lucky, and all your tunnels use exactly the same
    encryption parameters, you could instead use a single "anonymous"
    sainfo section like so:

	sainfo  anonymous
	{
	    lifetime            time    1 hour;

	    encryption_algorithm        aes;
	    authentication_algorithm    hmac_sha1;
	    compression_algorithm       deflate;
	}

    This will avoid a lot of repetitive data entry.  However, if you
    have different tunnels with different peers who want different
    parameters, you will have to specify them explicitly.

    Yes, you must specify the compression algorithm, even if you are not
    going to use compression.  Racoon demands it.

Gateway 2's racoon.conf:

    # This is racoon.conf
    # Loaded by racoon when it starts, or when receiving a HUP.

    log  notify;            # notify(*), debug, debug2

    path  pre_shared_key  "/etc/ipsec.keys";
    path  pidfile         "/var/run/racoon.pid";

    listen
    {
	isakmp  5.6.7.8;
	strict_address;             # Needed?
    }

    remote  1.2.3.4
    {
	exchange_mode  aggressive,main,base;

	my_identifier     address  5.6.7.8;
	peers_identifier  address  1.2.3.4;

	verify_identifier  off;

	proposal_check  claim;      # obey, strict, claim(*), exact(*)

	proposal
	{
	    encryption_algorithm    aes;
	    hash_algorithm          sha1;
	    authentication_method   pre_shared_key;
	    dh_group                2;
	    lifetime        time    24 hours;
	}
    }

    sainfo  address  192.168.1.0/24 any   address  192.168.11.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.2.0/24 any   address  192.168.11.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.1.0/24 any   address  192.168.12.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.2.0/24 any   address  192.168.12.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.11.0/24 any   address  192.168.1.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.11.0/24 any   address  192.168.2.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.12.0/24 any   address  192.168.1.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

    sainfo  address  192.168.12.0/24 any   address  192.168.2.0/24 any
    {
	lifetime            time    1 hour;

	encryption_algorithm        aes;
	authentication_algorithm    hmac_sha1;
	compression_algorithm       deflate;
    }

Things to notice:

    Yes, this file is almost exactly the same, with the IP's swapped. 
    What is local here is remote there, and vice-versa.  The "sainfo"
    sections must be left unchanged.  If they were different, the two
    racoons would not agree on parameter negotiations, and would fail to
    negotiate a tunnel.


Finally, you must specify the keys.  Each gateway needs to define the
keys used on the other gateways.

Gateway 1's ipsec.keys:

    # Keys for IKE

    5.6.7.8	SECRET_KEY

Gateway 2's ipsec.keys:

    # Keys for IKE

    1.2.3.4	SECRET_KEY

Things to notice:

    Each gateway has keys defined for OTHER gateways.  The OTHER gateway
    has to have the SAME key defined for THIS gateway.

    This ipsec.keys file MUST BE PROTECTED.  If you do not have it set
    to permissions 0600, racoon will silently IGNORE the file, and your
    IKE negotiation will fail, and there will be no indication in the
    logs as to why.  Check your permissions!


With these parameters a pair of gateways should be able to establish
IPSEC sessions with each other, even if the other gateway is some other
standards-compliant IPSEC implementation, such as found on Cisco or
Checkpoint devices.  I have tested specifically with those
implementations, and have had no problems.

I suppose I should talk about PF.  All my gateways use PF, so I have not
tested with any other firewall methods such as ipfw.

You must certainly allow your gateways to speak IKE and ESP to each
other if you want them to work.  I use a table called IPSEC_PEERS which
I load with the IP's of my peer gateways, and then I use these rules to
pass traffic:

    EXTIP="(fxp0)"

    pass in quick proto udp from <IPSEC_PEERS> to $EXTIP port 500  keep state
    pass in quick proto esp from <IPSEC_PEERS> to $EXTIP           keep state

    pass out quick from self  keep state

I am not convinced that "keep state" is needed or even useful here,
since neither UDP nor ESP are stateful protocols, and even if PF did not
keep state the traffic would still be allowed anyway.

An interesting side effect of IPSEC is that PF cannot track the full
state of it ("enc0" patches notwithstanding).  Traffic arrives on an
internal interface, and then if it becomes encrypted, the traffic
"mysteriously disappears" as far as PF can tell.  The traffic never goes
out another interface.  However, reply traffic from the other side of
the tunnel does magically "appear" from nowhere.

A typical gateway will have an "outbound" rule like this:

    pass in  quick on { $INT } all  keep state

where $INT defines the interfaces on the internal side.  Since "keep
state" is specified, reply traffic from the external net will have no
trouble coming back in.

However, VPN traffic does not come in through the external network... at
least, not in any way that PF can determine.  The traffic comes in as
seemingly-unrelated ESP, is decrypted in the kernel, then magically
appears decrypted on the internal interface for the first time.  Your
firewall will not understand this and will block the traffic unless you
add a rule like this:

    # VPN traffic appears here...?

    pass out quick on { $INT } to $INT:network  keep state

So, traffic appearing suddenly on an internal interface, destined for an
internal network, is permitted, on the assumption that it must have come
from an external VPN.

Naturally you can (and probably should) set up much more prohibitive
rules, but keep in mind that the first chance you will have to sense
inbound VPN traffic is when it is headed OUTBOUND on your INTERNAL
interfaces.

- -- 
David DeSimone == Network Admin == fox@verio.net
  "It took me fifteen years to discover that I had no
   talent for writing, but I couldn't give it up because
   by that time I was too famous.  -- Robert Benchley
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFEm4idFSrKRjX5eCoRAkFEAJ9QCbodDJUvwG3RG0fuQTXlKRX2dwCbBUhG
Q2QZv3RX582uXHSwBeyQuUk=
=LxYq
-----END PGP SIGNATURE-----



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20060623062221.GA23272>