Full tunnel AnyConnect with Internet hairpin

As a matter of personal preference, I was never a HUGE fan of the ASA as a firewall appliance.  For VPN termination, it’s pretty slick but still has some issues.  Either way, I have a 5505 at home that I use for firewall and VPN.  Being bored some time ago (wish I had free time now) I decided to upgrade the device from 8.2 to 9.1 code.  Along with this change came the dreaded ASA 8.3 NAT configuration change.  I’d argue that NAT on the ASA never made true sense, but once you knew how it worked, you could make it do what you wanted it to do.  Not knowing how to configure the new mode of NAT in the CLI, I decided to try it through ASDM (this of course breaking my ‘ASDM is awful never use it’ rule (and yes, I know you have to use ASDM for some of the AnyConnect XML stuff)).  The ASDM configuration lead to the automagic creation of NAT groups I didn’t need, object groups I didn’t need, and ACLs I didn’t need.  Somehow I managed to click enough buttons that it worked, but I wasn’t happy with the end state of the config. 

Fast forward to now.  Now I want to be able to connect to VPN at my house, access local resources, as well as access the internet through my local Comcast connection (internet hairpin).  Thinking this would be straight forward, I pulled down a copy of my ASA config into notepad and realized that it was full of random stuff I didn’t need.  After some clean up, I came to some realizations about NAT on the newer ASA code.  Namely, the fact that you don’t HAVE to use the NAT configuration under the objects themselves.  This, at least for me, was a HUGE help.  Let’s take a quick look at my config so you can see what I’ve setup…


So the real goal here is to be able to access a hosting container I use out on the internets from my laptop.  The hosting container only allows certain IP addresses (my home IP) to access it.  So if I could VPN to my house and use my home internet connection to access the hosting space from my laptop, I’d be all set!

In order to accomplish this, you need to do some ‘weird’ NAT configuration. I’m not going to run through my whole ASA config, but here are the important pieces…

hostname ASA
ip local pool vpn mask
interface Ethernet0/0
switchport access vlan 2
interface Ethernet0/1
interface Ethernet0/2
interface Ethernet0/3
interface Ethernet0/4
interface Ethernet0/5
switchport access vlan 3
interface Ethernet0/6
interface Ethernet0/7
switchport access vlan 3
interface Vlan1
nameif inside
security-level 100
ip address
interface Vlan2
nameif outside
security-level 0
ip address <removed>
interface Vlan3
no forward interface Vlan1
nameif guest
security-level 50
ip address
boot system disk0:/asa911-k8.bin
same-security-traffic permit intra-interface
object network guest
object network locallan
object-group network VPNPOOL
nat (outside,inside) source static VPNPOOL VPNPOOL
nat (outside,outside) source dynamic VPNPOOL interface
nat (inside,outside) source dynamic locallan interface
nat (guest,outside) source dynamic guest interface
route outside <removed> 1
route inside 1
telnet inside
telnet timeout 1440
ssh timeout 5
console timeout 0
management-access inside
dhcpd address inside
dhcpd dns interface inside
dhcpd enable inside
dhcpd address guest
dhcpd dns interface guest
dhcpd enable guest
enable outside
anyconnect image disk0:/anyconnect-macosx-i386-2.5.2017-k9.pkg 1
anyconnect image disk0:/anyconnect-win-2.5.3055-k9.pkg 2
anyconnect profiles vpn disk0:/vpn.xml
anyconnect enable
group-policy DfltGrpPolicy attributes
vpn-idle-timeout none
vpn-tunnel-protocol ikev1 l2tp-ipsec ssl-client ssl-clientless
group-policy gp_anyconnect internal
group-policy gp_anyconnect attributes
dns-server value
vpn-tunnel-protocol ikev2 ssl-client
split-tunnel-policy tunnelall
split-tunnel-network-list value splitvpn
  anyconnect profiles value vpn type user
  anyconnect ask none default anyconnect
username <removed> password <removed>
tunnel-group tg_vpn type remote-access
tunnel-group tg_vpn general-attributes
address-pool vpn
default-group-policy gp_anyconnect
tunnel-group tg_vpn webvpn-attributes
group-url <removed> enable

Lot’s of config there, but I want to focus on are the bolded lines.  The first bolded line is what tells the ASA to allow the ‘hairpin’ to occur.  Specifically, you are telling the ASA with this command that it’s ok for traffic to come in a interface with a certain security level (0) and leave through an interface with an identical security level (0).  This allows VPN traffic to come in the outside interface encrypted, and leave back out the outside interface to get to the internet. 

The next 4 bolded lines are the NAT configuration.  This is what I’m really interested in…

nat (outside,inside) source static VPNPOOL VPNPOOL
nat (outside,outside) source dynamic VPNPOOL interface
nat (inside,outside) source dynamic locallan interface
nat (guest,outside) source dynamic guest interface

Let’s line these statements up on our diagram to give you a visual of what’s actually going on…


The first NAT statement tells the ASA to allow the client space in from the outside interface to the inside interface and to not modify the addresses.  This allows my VPN pool (tail end of my to talk to the Local LAN space. 


The second NAT statement tells the ASA to take the VPN client space in the outside interface, back out the outside interface, but to dynamically overload it to the outside interface IP.  This is the actual NAT hairpin configuration that allows a VPN client to come in the outside and then leave back out towards the internet with the NAT overload.  



The last two NATs are simple dynamic overloads for the Local LAN and the Guest LAN network.  This allows both RFC 1918 spaces to be hidden behind the outside interface of the ASA.

Not really a ton too it actually, but I did struggle initially with the NAT until I figured out I could do it without defining the NAT under the object group itself.

11 thoughts on “Full tunnel AnyConnect with Internet hairpin

  1. Kerry

    Thanks for this article, well written and love the drawings.
    It’s so much easier to configure the object NAT rules when someone’s got a good description of a working configuration.


  2. Mors

    I had this problem this morning and you’ve saved my bacon. Was working fine via the SSL portal but not the Anyconnect client. I was pushing down routes for specific website IP’s too via a splittunnel ACL.

  3. Mike

    I am hoping someone can help me.

    I have an ASA 5505. I am attempting to setup a full tunnel VPN with anyconnect. Currently anyconnect users are not able to access the internet when connected.

    I am not able to get my IOS to take the NAT commands mentioned in this article.

    The IOS will not take – (inside,outside) together. Only one or the other.

  4. -

    Is there a way to simply get the VPN clients to use a internal gateway present in the VPN LAN instead of messing around with NATs?

  5. Aakanksha

    Ive been struggling with application of NAT. I understand the theory..but the solution never comes easily to me..
    The diagrams are extremely helpful!
    I hope the next time Im in NAT ‘muddypool’ i can find my way out:)

  6. mlan

    Thank you for posting this. It helped confirm that my overall config concept was correct, but I found I was being stung by a legacy “tunneled vpn route” config line. Since the tunneled config was no longer required, I removed it and the hairpin NAT started working correct for the vpn clients’ Internet access:

    # no route INSIDE x.x.x.x tunneled

  7. Seth

    Hey Jon,
    I am trying to do similar with a setup I have (on ASA 8.2(3))
    But for some reason the DNS lookup is messing up…..it is not being passed by to the VPN Client.

    My company has a ASA 5510 in place. I have created a VPN Full tunnel for my iPhone. Phone connects, traffic is passed. So hairpinning is working.
    As long as the app or website is being accessed using the IP address, things work.
    But if you use Chrome or Safari it will not work. The DNS lookup is just not happening.

    Now the weird thing is that I have a PIX 515E here next to me I use for testing and I can get it to work as it should. I try to migrate those commands from the PIX (7.2) to the ASA and it does not work at all.

    I have tried creating a DNS Inspect map, but that hasn’t helped…..
    Running out of ideas to try. It should work.
    It’s hard to really see what is happening in the log, since all the traffic is being passed on the outside interface, not to much shows up.

    Thank you for any thoughts or ideas you may have.


Leave a Reply

Your email address will not be published. Required fields are marked *