DMVPN with PKI authentication (GNS3 Lab)

DMVPN with PKI authentication (GNS3 Lab)

Dynamic Multipoint VPNs (DMVPN) offer a low admin overhead and scalable VPN solution. It is also efficient at routing traffic as it can dynamically reconfigure itself from a hub and spoke to a partial or full mesh topology! This means that not all traffic has to pass through the hub device.

Two protocols that enable DMVPN deployments are Multipoint Generic Routing Encapsulation (mGRE) rfc2735 / rfc1701and Next Hop Resolution Protocol (NHRP) rfc2735. There is also a very good post on Packetlife  about DMVPN.

The following if a GNS3 lab is relatively long compared to the other labs I have done and has quite a few steps so I have tried to arrange them in a logical order thats hopefully easy to follow. As always any feedback is welcome.

We will use the basic topology above and the initial GNS3 net file can be found [here]. It includes all the basic config and addressing done. There is full connectivity between the physical interfaces. I made this lab using 3600 running Version 12.4(16a) of IOS. Certificate setup and routing is covered towards the end of the post :)

Hub config<

Step Description
1 ISAKMP Policy
2 Transform Set / IPSEC Profile
3 mGRE interface
4 NHRP Server
5 Addressing / MTU

We will start with the Hub configuration. The first two steps a pretty much the same for any IPSEC VPN setup. Steps 3 & 4 are where we make it a DMVPN :)

ISAKMP Policy & Profile

Since this step is common to both the Hub and spokes please follow the Certificate setup section of this lab found on the 2nd page of this post here: Certificate setup

Transform Set / IPSEC profile

Now we configure our own custom transform set and get it to use tunnel mode. There are default transform sets so this step can be optional. Then we apply the transform set to the IPSEC protection profile we created earlier.

HUB#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
HUB(config)#crypto ipsec transform-set AES_128-SHA esp-aes 128 ah-sha-hmac
HUB(cfg-crypto-trans)#mode tunnel
HUB(cfg-crypto-trans)#exit
HUB(config)#crypto ipsec profile IPsecProfile
HUB(ipsec-profile)#set transform-set AES_128-SHA

mGRE interface

Now we configure our tunnel interface to be a mGRE interface. It is possible to have multiple DMVPN networks running in parallel i.e a second DMVPN for failover. The "key" is used to distinguish between these. The tunnel source interface is the physical interface which the mGRE tunnel is bound to.

HUB(config)#int tunnel 0
HUB(config-if)#tunnel mode gre multipoint
HUB(config-if)#tunnel key 123
HUB(config-if)#tunnel source s 0/0

NHRP Server

NHRP is configured on the tunnel interface. Again it is possible to have multiple NHRP networks so a network-id is used to differentiate them and we add an authentication password (max of 8 characters).

HUB(config)#int tunnel 0
HUB(config-if)#ip nhrp network-id 10
HUB(config-if)#ip nhrp authentication pass@m00nie!
% Authentication string exceeds 8 character maximum
HUB(config-if)#ip nhrp authentication @m00nie!
HUB(config-if)#ip nhrp map multicast ?
  A.B.C.D  IP NBMA address
  dynamic  Dynamically learn destinations from client registrations on hub
HUB(config-if)#ip nhrp map multicast dynamic

Addressing / MTU

Now we simply configure the IP of the mGRE interface. Its important to note that all tunnel interfaces across the DMVPN must be in the same subnet! This is so all next hops appear as directly connected to the tunnel interfaces.

HUB(config)#int tunnel 0
HUB(config-if)#ip add 192.168.10.1 255.255.255.0
HUB(config-if)#ip mtu 1450
HUB(config-if)#ip tcp adjust-mss 1410

Thats the config done for the HUB :)

Spoke Config

The configuration of the spokes is very similar so I'll just copy the similar config below. You can use either mGRE or GRE tunnels on the spokes. mGRE gives the option to dynamically build meshed topologies bypassing the HUB for spoke to spoke on the fly which is what we will configure.

ISAKMP / Transform set / IPSEC profile

Exactly the same as the Hub

crypto pki certificate map CertificateMap 10
 subject-name co o = m00nieco
!
crypto isakmp policy 100
 encr aes
 group 5
crypto isakmp profile ISAKMPProfile
   ca trust-point CA-Server
   match certificate CertificateMap
!
!
crypto ipsec transform-set AES_128-SHA ah-sha-hmac esp-aes
!
crypto ipsec profile IPsecProfile
 set transform-set AES_128-SHA
 set isakmp-profile ISAKMPProfile

mGRE interface

Again pretty much the same as the configuration of the Hub router. Enable multipoint GRE make the key the same and add the physical interface as the source. Also adding the previously configured IPSEC protection profile

Bambi#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Bambi(config)#int tun
Bambi(config)#int tunnel 0
Bambi(config-if)#
*Mar  1 01:44:38.559: %LINEPROTO-5-UPDOWN: Line protocol on Interface Tunnel0, changed state to down
Bambi(config-if)#tunnel mode gre multipoint
Bambi(config-if)#tunnel source s 0/0
Bambi(config-if)#tunnel key 123

NHRP Client

Now the NHRP config is a little different on the spokes than the Hub. Make the network-id match as well as the authentication. For NHRP clients we must configure the location of the NHRP server in this case its the IP of the Hub routers tunnel interface.

Bambi#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Bambi(config)#int tunnel 0
Bambi(config-if)#tunnel mode gre multipoint
Bambi(config-if)#tunnel source s 0/0
Bambi(config-if)#tunnel key 123
Bambi(config-if)#ip nhrp network-id 10
Bambi(config-if)#ip nhrp authentication @m00nie!
Bambi(config-if)#ip nhrp nhs 192.168.10.1
Bambi(config-if)#ip nhrp map multicast 10.0.14.4
Bambi(config-if)#ip nhrp map 192.168.10.1 10.0.14.4

Addressing / MTU

Bambi(config-if)# ip mtu 1450
Bambi(config-if)# ip tcp adjust-mss 1410
Bambi(config-if)#ip add 192.168.10.2 255.255.255.0<

Now you should have an ISAKMP and IPSEC SA's built and stable between Bambi + HUB. The config for Borgy is exactly the same as Bambi but the IP on the tunnel interface is 192.168.10.3. Now each spoke should have SA's with the HUB.

Routing / Dynamic Mesh

Now we have a stable hub and spoke topology its time to add some routing :) Just some short notes on using either EIGRP or OSPF :) Although DMVPN can pass multicast traffic there is an initial problem of spokes not being able to exchange routing info directly with other spokes even though logically they are on the same subnet.

EIGRP

EIGRP uses the multicast address of 224.0.0.10 to send its updates but if all updates are initially sent to the HUB router the next hop would always be the HUB. To avoid this we can use the no ip next-hop-self eigrp interface command on the HUB. This we mean that forwarded EIGRP updates contain the original spoke IP and will allow for spoke to spoke communication! The following will configure EIGRP on the topology and allow for dynamic mesh formations.

HUB#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
HUB(config)#router eigrp 1
HUB(config-router)# network 4.4.4.0 0.0.0.255
HUB(config-router)# network 192.168.10.0
HUB(config-router)# no auto-summary
HUB(config-router)#exit
HUB(config)#interface Tunnel0
HUB(config-if)# no ip next-hop-self eigrp 1
HUB(config-if)# no ip split-horizon eigrp 1

The configuration on the spoke routers is just a basic EIGRP config

Bambi#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Bambi(config)#router eigrp 1
Bambi(config-router)# network 2.2.2.0 0.0.0.255
Bambi(config-router)# network 192.168.10.0
Bambi(config-router)# no auto-summary

Config for Borgy is basically just the same as the other spoke (Bambi).

Now we can see from the routing table on Bambi it has the correct next hop (logical tunnel interface) for the 3.3.3.0 subnet.

Bambi#show ip route | b Gateway
Gateway of last resort is 10.0.12.1 to network 0.0.0.0

     2.0.0.0/24 is subnetted, 1 subnets
C       2.2.2.0 is directly connected, Loopback0
     3.0.0.0/24 is subnetted, 1 subnets
D       3.3.3.0 [90/310172416] via 192.168.10.3, 00:07:47, Tunnel0
     4.0.0.0/24 is subnetted, 1 subnets
D       4.4.4.0 [90/297372416] via 192.168.10.1, 00:09:00, Tunnel0
C    192.168.10.0/24 is directly connected, Tunnel0
     10.0.0.0/24 is subnetted, 1 subnets
C       10.0.12.0 is directly connected, Serial0/0
S*   0.0.0.0/0 [1/0] via 10.0.12.1

But if we check the SA's there is still only a single connection to the HUB rather than a connection to another spoke.

Bambi#show crypto isakmp sa
dst             src             state          conn-id slot status
10.0.14.4       10.0.12.2       QM_IDLE              4    0 ACTIVE

Try an extended ping from Bambi's loopback to Borgy's loopback then see what happens :) Below we can see the spoke to spoke tunnel dynamically created! Its important to note that previous to this traffic is still passed via the HUB while the spoke to spoke tunnel is brought up.

Bambi#ping
Protocol [ip]:
Target IP address: 3.3.3.3
Repeat count [5]: 100
Datagram size [100]:
Timeout in seconds [2]:
Extended commands [n]: y
Source address or interface: 2.2.2.2
Type of service [0]:
Set DF bit in IP header? [no]:
Validate reply data? [no]:
Data pattern [0xABCD]:
Loose, Strict, Record, Timestamp, Verbose[none]:
Sweep range of sizes [n]:
Type escape sequence to abort.
Sending 100, 100-byte ICMP Echos to 3.3.3.3, timeout is 2 seconds:
Packet sent with a source address of 2.2.2.2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Success rate is 100 percent (100/100), round-trip min/avg/max = 8/47/672 ms
Bambi#
Bambi#show crypto isakmp sa
dst             src             state          conn-id slot status
10.0.13.3       10.0.12.2       QM_IDLE              6    0 ACTIVE
10.0.12.2       10.0.13.3       QM_IDLE              5    0 ACTIVE
10.0.14.4       10.0.12.2       QM_IDLE              4    0 ACTIVE

We now have a full mesh that was created dynamically :)

OSPF

OSPF uses the destination address of 224.0.0.5 and is a little bit different in its implementation that EIGRP. OSPF has the concept of Designated Routers (DR) on multiaccess segments. In the config below the HUB is set with a higher DR priority and the spokes are set with 0 meaning they can never become the DR.

HUB#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
HUB(config)#interface Tunnel0
HUB(config-if)# ip ospf network broadcast
HUB(config-if)# ip ospf priority 200
HUB(config-if)#exit
HUB(config)#router ospf 1
HUB(config-router)# log-adjacency-changes
HUB(config-router)# network 4.4.4.0 0.0.0.255 area 4
HUB(config-router)# network 192.168.10.0 0.0.0.255 area 0

The spoke configs are quite similar with the 0 priority set.

Borgy#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Borgy(config)#interface Tunnel0
Borgy(config-if)# ip ospf network broadcast
Borgy(config-if)# ip ospf priority 0
Borgy(config-if)#exit
Borgy(config)#router ospf 1
Borgy(config-router)# log-adjacency-changes
Borgy(config-router)# network 2.2.2.0 0.0.0.255 area 2
Borgy(config-router)# network 192.168.10.0 0.0.0.255 area 0

Now like EIGRP check the routing table and notice that the next hops are correct and that there is only one SA (form spoke to hub)

Bambi#show ip route | b Ga
Gateway of last resort is 10.0.12.1 to network 0.0.0.0

     2.0.0.0/24 is subnetted, 1 subnets
C       2.2.2.0 is directly connected, Loopback0
     3.0.0.0/32 is subnetted, 1 subnets
O IA    3.3.3.3 [110/11112] via 192.168.10.3, 00:04:02, Tunnel0
     4.0.0.0/32 is subnetted, 1 subnets
O IA    4.4.4.4 [110/11112] via 192.168.10.1, 00:04:02, Tunnel0
C    192.168.10.0/24 is directly connected, Tunnel0
     10.0.0.0/24 is subnetted, 1 subnets
C       10.0.12.0 is directly connected, Serial0/0
S*   0.0.0.0/0 [1/0] via 10.0.12.1
Bambi#
Bambi#show crypto isakmp sa
dst             src             state          conn-id slot status
10.0.14.4       10.0.12.2       QM_IDLE              3    0 ACTIVE

And again like EIGRP lets send some traffic from spoke to spoke and see if we can dynamically create a direct tunnel :)

Bambi#ping
Protocol [ip]:
Target IP address: 3.3.3.3
Repeat count [5]: 100
Datagram size [100]:
Timeout in seconds [2]:
Extended commands [n]: y
Source address or interface: 2.2.2.2
Type of service [0]:
Set DF bit in IP header? [no]:
Validate reply data? [no]:
Data pattern [0xABCD]:
Loose, Strict, Record, Timestamp, Verbose[none]:
Sweep range of sizes [n]:
Type escape sequence to abort.
Sending 100, 100-byte ICMP Echos to 3.3.3.3, timeout is 2 seconds:
Packet sent with a source address of 2.2.2.2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Success rate is 99 percent (99/100), round-trip min/avg/max = 8/58/600 ms

Bambi#show crypto isakmp sa
dst             src             state          conn-id slot status
10.0.13.3       10.0.12.2       QM_IDLE              4    0 ACTIVE
10.0.12.2       10.0.13.3       QM_IDLE              5    0 ACTIVE
10.0.14.4       10.0.12.2       QM_IDLE              3    0 ACTIVE

Again we have dynamically created a full mesh! Other routing protocols can be used but I've not covered them here.

Certificate Setup

First step is to get a CA server and enroll each device. In this lab the CA  router is the certificate server for the deployment. In a real world situation the CA would probably be a stand alone box.

Certificate Enrollment

To set-up the certificate server and for each peer to get its certificate/trustpoint please follow the lab [here]. This can be done using SCEP like in the preceding lab link or manually which will be more secure for deployments over the internet.

Certificate Authentication

Now we have signed certificates on Bambi, Borgy and HUB with CA acting as the certificate server we need to configure ISAKMP to use them as authentication and how it should go about verifying them.

Step Description
1 ISAKMP policy
2 Certificate Map
3 ISAKMP profile
4 Protection Profile

ISAKMP policy

First off we configure the usual ISAKMP policy but use rsa-sig as the authentication method.

HUB#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
HUB(config)#crypto isakmp policy 100
HUB(config-isakmp)#authentication rsa-sig
HUB(config-isakmp)#group 5
HUB(config-isakmp)#encryption aes 128

Certificate Map

We dont want every certificate issued by the CA server to be able to authenticate a VPN session so we create a certificate map to only allow certificates with  an O value of m00nieCo.

HUB(config)#crypto pki certificate map CertificateMap 10
HUB(ca-certificate-map)#subject-name co O=m00nieCo

ISAKMP profile

Now we configure an ISAKMP profile to use the trustpoint we setup (CA-Server) and filter certificates we allow with the certificate map from step 2.

HUB(config)#crypto isakmp profile ISAKMPProfile
% A profile is deemed incomplete until it has match identity statements
HUB(conf-isa-prof)#ca trust-point CA-Server
HUB(conf-isa-prof)#match certificate CertificateMap

Protection Profile

Finally we create an IPSEC protection profile using the previous ISAKMP profile and apply it to the tunnel interface.

HUB(config)#crypto ipsec profile IPsecProfile
HUB(ipsec-profile)#set isakmp-profile ISAKMPProfile
HUB(ipsec-profile)#exit
HUB(config)#int tun 0
HUB(config-if)#tunnel protection ipsec profile IPsecProfile

Now we have a DMVPN cloud using certificates from our own CA to identify each other :D
Any feedback/questions on the lab are very welcome

m00nie