July 06, 2006

IPSec and NAT with Cisco and Linux boxen

Introduction

This is a quick mini-howto on how to get IPSec working between an OpenWRT (or any linux gateway) box and a Cisco IOS router, where both are also doing NAT for their networks. I'll also describe how to get the Linux gateway talking IPSec to a standalone Linux host elsewhere on the net. Last but not least, you'll also see how to get an IPV6 over IPv4 tunnel working across this.

I was recently rebuilding my home network topology, and part of that rebuild involved some IPSec tunnels across the Internet. I though I'd share my documentation on this to assist anyone else attempting a similar setup. The setup is built around Linux and Cisco IOS NAT gateways, as well as one standalone host, and the challenge faced here is to have IPSec running between the Linux NAT box and the other two devices.

The basic network topology is shown below. It consists of two NATed networks (Amsterdam and Sydney) and one standalone host (San Jose), all with static public IP addresses. These networks have significant communication between them, and with a desire for privacy and bypassing of ISP filters (eg port 25), tunnels have been built between the devices to give transparency across the network. The use of NAT adds to some of the challenges in building this.

Topology
The setup

The devices here consist of one standalone Linux host, one OpenWRT-Linux based router and one Cisco 827 ADSL router. The routers are NATting for the networks behind them, and some port redirection is enabling outside access to services on those networks. All networks have global IPv6 connectivity via tunnels (not shown here), and there are IPv6 tunnels directly connecting them. The internal networks behind the NAT gateways will be reachable from each other via IPSec tunnels.

My IPSec daemon of choice is OpenSwan, and a package is available for OpenWRT. There's a reasonable amount of documentation on it, but strangely I found only a few references to getting it working with a Cisco IOS box, and even fewer in combination with NAT.

The Cisco is an 827 ADSL router, running a 12.3 IOS with IPSec features. Conveniently, it also has support for IPv6, however IPSec on IPv6 is not supported, hence the use of tunneling IPv6 over the IPv4 IPSec.

IPSec and tunnel topology

For the Amsterdam to Sydney connection, I decided to build various tunnels linking the possible communication paths over IPv4. One of the niggles with IPSec is that you've got to define all source and destination combinations as separate tunnels. The IPv6 addresses are tunnelled over an IPv4 IPSec tunnel, using standard 6-in-4. I had to use this for IPv6 anyway, given that the devices don't support IPv6 IPSec.

Between Amsterdam and San Jose, IPv4 is tunnelled with IPSec, but IPv6 would is routed over an IPv6 in IPv4 tunnel, for the same reasons - limited device support.

Configuration

The configuration of OpenSwan is handled within two files. The file /etc/ipsec.conf is the main config file and describes the settings for each IPSec connection. Configuration of crypto keys are in /etc/ipsec.secrets, and if simple preshared keying is used, this is all that needs to be touched. In the following examples, the key will be "SecretKey" but of course you should use something much longer and more obscure.

Amsterdam to Sydney

The following is the OpenSwan configuration for the Amsterdam to Sydney connection. It contains four tunnel definitions, defining all possible traffic flows:

  • 31.31.31.31 to 61.61.61.61 (Amsterdam public to Sydney public)
  • 31.31.31.31 to 10.0.0.0/24 (Amsterdam public to Sydney internal)
  • 192.168.0.0/24 to 61.61.61.61 (Amsterdam internal to Sydney public)
  • 192.168.0.0/24 to 10.0.0.0/24 (Amsterdam internal to Sydney internal)
In the /etc/ipsec.conf file is:
conn sydney1
        left=31.31.31.31
        leftsubnet=31.31.31.31/32
        leftnexthop=%defaultroute
        right=61.61.61.61
        rightsubnet=61.61.61.61/32
        rightnexthop=%defaultroute
        authby=secret
        esp=3des-md5-96
        keyexchange=ike
        pfs=yes
        auth=esp
        auto=start
conn sydney2
        left=31.31.31.31
        leftsubnet=31.31.31.31/32
        leftnexthop=%defaultroute
        right=61.61.61.61
        rightsubnet=192.168.0.0/24
        rightnexthop=%defaultroute
        authby=secret
        esp=3des-md5-96
        keyexchange=ike
        pfs=yes
        auth=esp
        auto=start
conn sydney3
        left=31.31.31.31
        leftsubnet=192.168.0.0/24
        leftnexthop=%defaultroute
        right=61.61.61.61
        rightsubnet=61.61.61.61/32
        rightnexthop=%defaultroute
        authby=secret
        esp=3des-md5-96
        keyexchange=ike
        pfs=yes
        auth=esp
        auto=start
conn sydney4
        left=31.31.31.31
        leftsubnet=192.168.0.0/24
        leftnexthop=%defaultroute
        right=61.61.61.61
        rightsubnet=10.0.0.0/24
        rightnexthop=%defaultroute
        authby=secret
        esp=3des-md5-96
        keyexchange=ike
        pfs=yes
        auth=esp
        auto=start
The sections above are all the same except for the leftsubnet/rightsubnet definitions.

In the /etc/ipsec.secrets file is:

31.31.31.31 61.61.61.61: PSK "SecretKey"

And here is the IOS configuration for Sydney:

crypto isakmp policy 10
 encr 3des
 hash md5
 authentication pre-share
 group 2
 lifetime 28800
crypto isakmp key SecretKey address 31.31.31.31
!
!
crypto ipsec transform-set SydAms esp-3des esp-md5-hmac 
!
crypto map Amsterdam 10 ipsec-isakmp 
 set peer 31.31.31.31
 set transform-set SydAms 
 set pfs group2
 match address 192
!
! This is the outgoing interface with the public IP address
!
interface Dialer1
 crypto map Amsterdam
!
access-list 192 permit ip host 218.214.47.18 host 80.61.87.81
access-list 192 permit ip 192.168.1.0 0.0.0.255 10.0.0.0 0.0.0.255
access-list 192 permit ip 192.168.1.0 0.0.0.255 host 80.61.87.81
access-list 192 permit ip host 218.214.47.18 10.0.0.0 0.0.0.255

We will now configure the IPv6 tunnel to complete the tunnel config for this connection.

First the Amsterdam configuration:


ip tunnel add Sydney6 mode sit ttl 64 remote 61.61.61.61  \
  && ip link set dev Sydney6 mtu 1420 up \
  && ip -6 addr add 2001:31::1/128 dev Sydney6 \
  && ip -6 route add 2001:61::/48 dev Sydney6

And the Cisco configuration:

!         
interface Tunnel0
 description IPV6 tunnel to Amsterdam
 no ip address
 ipv6 unnumbered Ethernet0
 ipv6 enable
 tunnel source Dialer1
 tunnel destination 31.31.31.31
 tunnel mode ipv6ip
!
ipv6 route 2001:31::/48 Tunnel0
!

Does the connection work yet?

No it doesn't. The problem is related to NAT and firewalling configuration on both ends.

On the Cisco side, the router will attempt to NAT the outgoing packets from the internal hosts before encrypting them with IPSec, and thus mangle the source address of the packet. Ingress traceroutes to an internal address will terminate on the public address, not the internal address. To solve this, we need to exclude IPSec-handled packets from the NAT processing. Somewhere in your IOS configuration will be a line similar to this:

ip nat inside source list 101 interface Dialer1 overload
The source-list referred to will need to be added to so that packets out via IPSec are excluded. So access-list 101 becomes:
access-list 101 deny   ip any 192.168.0.0 0.0.0.255
access-list 101 deny   ip any host 31.31.31.31
access-list 101 permit ip 10.0.0.0 0.0.0.255 any

On the OpenWrt box, NAT doesn't mangle with the IPSec packets, due to the rule processing order in iptables. NAT only affects packets leaving the WAN interface, but IPSec processing occurs before this, and by the time they're encrypted, the source address of the IPSec packets won't match the NAT rule any more.

However, the FORWARD table of the OpenWrt box will be default drop all packets that it can't establish as being related to an existing connection (ie anything not NAT or port forwarded) and so will drop any not destined for the local machine, ie IPSec traffic for any inside host.

Thus, to fix this, we put in a forwarding rule that allows all IPSec traffic through the box. In addition, we'll add a rule that ensures port 500 UDP traffic (IPSec key management aka ISAKMP) is handled locally:

In /etc/firewall.user:
# This is a catch all - it's essential to also put it more secure rules.
iptables -A forwarding_rule -i ipsec0 -j ACCEPT
iptables -A forwarding_rule -o ipsec0 -j ACCEPT
# These rules send ISAKMP traffic to the local OpenSwan daemon.
iptables -t nat -A prerouting_rule -i $WAN -p udp --dport 500 -j ACCEPT 
iptables -A input_rule      -i $WAN -p udp --dport 500 -j ACCEPT

At this point, there should be full connectivity between the internal networks of both ends on both IPv4 and IPv6. Traceroute from an internal host at one end to any IP address of the other end should reveal only one WAN hop.

That completes the configuration for the Amsterdam to Sydney link.

Amsterdam to San Jose

Between Amsterdam and San Jose, we'll also configure OpenSwan at both ends. There will be two IPSec tunnels configured here - one will carry traffic between the public addresses, and the other will carry traffic between the Amsterdam internal network and San Jose. So one tunnel will carry traffic between 1.1.1.1 and 31.31.31.31, whilst the other will carry it from 1.1.1.1 to 192.168.0.0/24. The rules for this configuration are quite simple:

Amsterdam config:

/etc/ipsec.conf
conn sj
        left=31.31.31.31
        leftsubnet=31.31.31.31/32
        leftnexthop=%defaultroute
        right=1.1.1.1
        rightsubnet=1.1.1.1/32
        rightnexthop=%defaultroute
        authby=secret
        keyexchange=ike
        pfs=yes
        esp=3des-md5-96
        auth=esp
        auto=start
        
conn sjpriv
        left=31.31.31.31
        leftsubnet=192.168.0.0/24
        leftnexthop=%defaultroute
        right=1.1.1.1
        rightsubnet=1.1.1.1/32
        rightnexthop=%defaultroute
        authby=secret
        keyexchange=ike
        pfs=yes
        esp=3des-md5-96
        auth=esp
        auto=start
/etc/ipsec.secrets
31.31.31.31 1.1.1.1: PSK "SecretKey"

San Jose config:

/etc/ipsec.conf
conn ams              
        left=1.1.1.1                           
        leftsubnet=1.1.1.1/32
        leftnexthop=%defaultroute                                          
        right=1.1.1.1
        rightsubnet=1.1.1.1/32
        rightnexthop=%defaultroute
        authby=secret                                                       
        keyexchange=ike              
        pfs=yes                      
        esp=3des-md5-96
        auth=esp
        auto=start                   

conn amspriv
        left=1.1.1.1                             
        leftsubnet=1.1.1.1/32
        leftnexthop=%defaultroute                                          
        right=31.31.31.31
        rightsubnet=192.168.0.0/24
        rightnexthop=%defaultroute
        authby=secret                                                       
        keyexchange=ike              
        pfs=yes                      
        esp=3des-md5-96
        auth=esp
        auto=start                   
/etc/ipsec.secrets
1.1.1.1 31.31.31.31: PSK "SecretKey"

When be bring these connections up, we see that all traffic is successful, and this tunnel works. The forwarding rule for the ipsec0 interface above is important and allows reachability to the inside hosts. Note that you don't need to put an equivalent forwarding rule on the Linux host, as nothing is forwarded through the box. But you may need to configure general input and output rules to allow IPSec packets.

Lastly, we configure an IPv6 tunnel across this IPSec link. It's quite straight forward and this is the configuration:

Amsterdam end:

ip tunnel add US mode sit ttl 64 remote 1.1.1.1  \
  && ip link set dev US mtu 1420 up \
  && ip -6 addr add 2001:31::1/128 dev US \
  && ip -6 route add 2001:1::1/128 dev US

San Jose end:

ip tunnel add NL mode sit ttl 64 remote 31.31.31.31  \
&& ip link set dev NL mtu 1420 up \
&& ip -6 addr add 2001:1::1/128 dev NL \
&& ip -6 route add 2001:31::1/64 dev NL

A quick ping will show the IPv6 tunnel up and there should be reachability to all hosts on the inside of the Amsterdam network. It's also a good idea to configure firewalling rules on the IPv6 side - anyone breaching the public host has full IPv4 and IPv6 access to the Amsterdam network, so be smart and build that firewall.

Last but not least...the IPSec connection between San Jose and Sydney....is left as an excercise to the reader.

Conclusion

IPSec is often seen as a mysterious protocol in the IP world, difficult to setup and hard to maintain. But a look into a basic configuration with it reveals it to be not that hard at all. The configurations above should be a good solution for many common network topologies, and with a clear understanding as to what it all does, adapting it to other networks should be relatively painless.

Posted by Ben at July 6, 2006 10:02 PM
Comments
Post a comment









Remember personal info?






Recent Entries
- Linux on an HP Compaq NC6400
- Sodium in water? Bah..try Caesium!
- I'm off to Lugradio
- Food for thought...
- Replacing ugly Helvetica fonts in Xorg
Support me...

Contact
Email me: bb@bb.cactii.net
Current...
NL time:02:57
Book: Assassini (Thomas Gifford)
Amazon wish list
Search the web
Google