TL/DR
I set up a static nat rule, so that I can use my public internet IP address to directly ssh into a dedicated machine on the inside network. As a result I cannot ssh into the dedicated machine anymore, when connected through AnyConnect VPN. Why? How do I fix this?
Long story:
I have an ASA 5512-X which I use to terminate AnyConnect VPN sessions. This has been working very well. People use AnyConnect to connect to the ASA, the get IP addresses from the specially assigned IP pool and they can access everything I put in the access-lists.
Now, I have this idea to directly forward port 22 to a dedicated machine in my network (I only have inside and outside interfaces), so that I can SSH into my public IP address and end up on the dedicated machine, without the requirement for a VPN connection.
The setup seemed simple enough:
object network MyEndpoint
host 10.0.0.1
nat (inside, outside) static interface service tcp ssh ssh
access-list from_outside_inside extended permit tcp any object MyEndpoint eq ssh
access-group from_outside_inside in interface outside
and it actually works. Now I can
ssh ssh.mydomain.com
and actually get a ssh session on my dedicated machine. However, this success comes at the cost that ssh 10.0.0.1
does not work anymore when I am connected to my ASA/network through AnyConnect as it used to, before I set up static NAT.
I can deterministically control the ASA's behaviour:
When I don't have the nat (inside,outside)
line in my configuration,
ssh 10.0.0.1
works butssh ssh.mydomain.com
does not work.
When I have the nat (inside,outside)
rule as part of the configuration, it is the other way round:
ssh ssh.mydomain.com
works butssh 10.0.0.1
does not work.
To investigate I set up a capture on the ASA
capture MyEndpoint type raw-data interface inside
capture MyEndpoint type raw-data interface outside
capture MyEndpoint match tcp host 10.0.0.1 any
When I first connect via AnyConnect and then try to ssh 10.0.0.1
, the capture doesn't show/capture anything, as long as the nat (inside,outside)
rule is active. Also, my SSH connect attempts never reach the server.
Clearly the ASA is eating the connection requests originating from AnyConnect-connected endpoints, when the nat
rule is active.
At first I thought I require some addition to the access-list, but I couldn't make it work that way. On which interface does VPN traffic enter the ASA anyway? outside or inside? I suppose in this case doesn't matter, because I am using a wildcard (any
) for the source, which, in my oppinion, includes VPN traffic as well.
Then I thought the ASA would always apply the nat, which means the packet carrying the SYN-ACK would be natted to my outside IP which breaks the connection, as I am connecting from a private VPN address, not the Internet. But the SYNs never get to the server, hence there are no SYN-ACKs.
I spent all day warpping my head around the "new" nat
command. When I do a show nat
on my ASA I get
Auto NAT Policies (Section 2)
1 (inside) to (outside) source static MyEndpoint interface service tcp ssh ssh
translate_hits = 0, untranslate_hits = 0
As far as I got today this only does a source address translation for leaving traffic. I have no idea, how/why this implies a destination address translation for traffic originating from the Internet arriving at the ASA. Can anybody explain to me why the ASA is behaving this way?
Edit 1: packet-tracer
Reading on through the documentations I came accross the ASA command packet-tracer
. When I do
packet-tracer input inside tcp 10.100.0.2 31337 10.0.0.1 22
The ASA tells me a flow would be created and the packet accepted. When I do
packet-tracer input outside tcp 10.100.0.2 31337 10.0.0.1 22
I get
Phase: 6
Type: WEBVPN-SVC
Subtype: in
Result: DROP
Config:
Additional Information:
Result:
input-interface: outside
input-status: up
input-line-status: up
output-interface: inside
output-status: up
output-line-status: up
Action: drop
Drop-reason: (acl-drop) Flow is denied by configured rule
So I guess this is some implicitly wanted behaviour, except if you are me.
Edit 2: Searching for hairpins
I found some articles which eventually pointed me to this promissing one which held the information I might need a nat rule along the lines of
nat (outside,outside) \
source static \
client_vpn_pool client_vpn_pool \
destination static \
remote_office_net remote_office_net
but it didn't work. I have a network object for my vpn client subnet and a network object for the subnet of 10.0.0.1. Adapting the template above did not yield the expected result.
When my nat (inside,outside)
rule is in place, SSH does not work, but I can ping 10.0.0.1 just fine. When I add a nat (outside,ouside)
rule as suggested by the article, the ping stops working. I also tried versions of the outside-ouside rule with network objects for the specific host and just the range of the ip local pool
used for vpn clients. They all just make the ping stop.
Edit 3: Never mind
It's working now. I literally didn't change anything. I just went to bed. I guess there was some internal state in the ASA that caused this behaviour and it timed out while I was asleep.
There is still the thing that when I use packet-tracer input [inside|outside] tcp <my vpn ip> 31337 10.0.0.1 22
packet-tracer always says the packet would be dropped.
When I put packet-tracer input inside
it fails at:
Phase: 6
Type: WEBVPN-SVC
Subtype: in
Result: DROP
Config:
Additional Information:
[...]
Drop-reason: (acl-drop) Flow is denied by configured rule
and when I put packet-tracer input outside
it fails at:
Phase: 6
Type: NAT
Subtype: rpf-check
Result: DROP
Config:
object network MyEndpoint
nat (inside,outside) static interface service tcp ssh ssh
Additional Information:
[...]
Drop-reason: (acl-drop) Flow is denied by configured rule
Yet, as of today, ping and SSH to/from my VPN-connected Laptop to MyEndpoint (a.k.a. 10.0.0.1) works like a charm. How does this ASA thing work???